MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <compat.h> // IWYU pragma: keep
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <libgen.h>
7
8#include "ant.h"
9#include "errors.h"
10#include "internal.h"
11#include "esm/loader.h"
12#include "esm/library.h"
13#include "modules/symbol.h"
14
15typedef struct { ant_t *js; ant_value_t arr; } builtin_iter_ctx_t;
16
17static void push_builtin_name(const char *name, void *ud) {
18 builtin_iter_ctx_t *ctx = (builtin_iter_ctx_t *)ud;
19 js_arr_push(ctx->js, ctx->arr, js_mkstr(ctx->js, name, strlen(name)));
20}
21
22static ant_value_t builtin_createRequire_call(ant_t *js, ant_value_t *args, int nargs) {
23 if (nargs < 1 || vtype(args[0]) != T_STR)
24 return js_mkerr(js, "require() expects a string specifier");
25
26 ant_value_t fn = js_getcurrentfunc(js);
27 ant_value_t data = js_get_slot(fn, SLOT_DATA);
28 const char *base_path = js_module_eval_active_filename(js);
29
30 if (vtype(data) == T_STR) {
31 ant_offset_t plen = 0;
32 ant_offset_t poff = vstr(js, data, &plen);
33 base_path = (const char *)(uintptr_t)(poff);
34 }
35
36 ant_value_t ns = js_esm_import_sync_from(js, args[0], base_path);
37 if (is_err(ns)) return ns;
38
39 if (vtype(ns) == T_OBJ) {
40 ant_value_t default_export = js_get_slot(ns, SLOT_DEFAULT);
41 if (vtype(default_export) != T_UNDEF) return default_export;
42 }
43
44 return ns;
45}
46
47static ant_value_t resolve_strip_file_url(ant_t *js, ant_value_t resolved) {
48 if (is_err(resolved) || vtype(resolved) != T_STR) return resolved;
49
50 ant_offset_t len = 0;
51 ant_offset_t off = vstr(js, resolved, &len);
52
53 const char *s = (const char *)(uintptr_t)(off);
54 static const char *prefix = "file://";
55
56 if ((size_t)len >= strlen(prefix) && strncmp(s, prefix, strlen(prefix)) == 0) {
57 const char *path_part = s + strlen(prefix);
58 size_t plen = (size_t)len - strlen(prefix);
59 return js_mkstr(js, path_part, plen);
60 }
61
62 return resolved;
63}
64
65// require.resolve(specifier, options?)
66static ant_value_t builtin_createRequire_resolve(ant_t *js, ant_value_t *args, int nargs) {
67 if (nargs < 1 || vtype(args[0]) != T_STR)
68 return js_mkerr(js, "require.resolve() expects a string specifier");
69
70 ant_value_t fn = js_getcurrentfunc(js);
71 ant_value_t data = js_get_slot(fn, SLOT_DATA);
72 const char *base_path = js_module_eval_active_filename(js);
73
74 if (vtype(data) == T_STR) {
75 ant_offset_t dlen = 0;
76 ant_offset_t doff = vstr(js, data, &dlen);
77 base_path = (const char *)(uintptr_t)(doff);
78 }
79
80 ant_value_t paths_val = (nargs >= 2 && is_object_type(args[1]))
81 ? js_get(js, args[1], "paths") : js_mkundef();
82
83 if (vtype(paths_val) != T_ARR) {
84 ant_value_t resolved = js_esm_resolve_specifier(js, args[0], base_path);
85 return resolve_strip_file_url(js, resolved);
86 }
87
88 ant_offset_t path_count = js_arr_len(js, paths_val);
89 for (ant_offset_t i = 0; i < path_count; i++) {
90 ant_value_t p = js_arr_get(js, paths_val, i);
91 if (vtype(p) != T_STR) continue;
92
93 char *dir = js_getstr(js, p, NULL);
94 if (!dir) continue;
95
96 ant_value_t resolved = js_esm_resolve_specifier(js, args[0], dir);
97 if (!is_err(resolved) && vtype(resolved) == T_STR)
98 return resolve_strip_file_url(js, resolved);
99 }
100
101 return js_mkerr(js, "Cannot resolve module");
102}
103
104// createRequire(filename)
105static ant_value_t builtin_createRequire(ant_t *js, ant_value_t *args, int nargs) {
106 if (nargs < 1) return js_mkerr(js, "createRequire() requires a filename argument");
107
108 ant_value_t filename_val = args[0];
109 if (vtype(filename_val) != T_STR)
110 return js_mkerr(js, "createRequire() filename must be a string");
111
112 size_t fname_len;
113 char *fname = js_getstr(js, filename_val, &fname_len);
114 if (!fname) return js_mkerr(js, "createRequire() invalid filename");
115
116 const char *path = fname;
117 size_t path_len = fname_len;
118
119 static const char *file_prefix = "file://";
120 size_t prefix_len = strlen(file_prefix);
121
122 if (path_len >= prefix_len && strncmp(path, file_prefix, prefix_len) == 0) {
123 path += prefix_len;
124 path_len -= prefix_len;
125 }
126
127 ant_value_t path_val = js_mkstr(js, path, path_len);
128 ant_value_t require_fn = js_heavy_mkfun(js, builtin_createRequire_call, path_val);
129 ant_value_t resolve_fn = js_heavy_mkfun(js, builtin_createRequire_resolve, path_val);
130 js_set(js, require_fn, "resolve", resolve_fn);
131
132 return require_fn;
133}
134
135// Module._resolveFilename(request, parent)
136static ant_value_t builtin_resolveFilename(ant_t *js, ant_value_t *args, int nargs) {
137 if (nargs < 1 || vtype(args[0]) != T_STR)
138 return js_mkerr(js, "Module._resolveFilename() requires a string request");
139
140 const char *base_path = js_module_eval_active_filename(js);
141 if (nargs >= 2 && vtype(args[1]) == T_OBJ) {
142 ant_value_t parent_filename = js_get(js, args[1], "filename");
143 if (vtype(parent_filename) == T_STR) {
144 ant_offset_t plen = 0;
145 ant_offset_t poff = vstr(js, parent_filename, &plen);
146 base_path = (const char *)(uintptr_t)(poff);
147 }
148 }
149
150 ant_value_t resolved = js_esm_resolve_specifier(js, args[0], base_path);
151 if (is_err(resolved)) return resolved;
152 if (vtype(resolved) != T_STR) return resolved;
153
154 ant_offset_t len = 0;
155 ant_offset_t off = vstr(js, resolved, &len);
156
157 const char *s = (const char *)(uintptr_t)(off);
158 static const char *prefix = "file://";
159
160 if ((size_t)len >= strlen(prefix) && strncmp(s, prefix, strlen(prefix)) == 0) {
161 const char *path_part = s + strlen(prefix);
162 size_t plen = (size_t)len - strlen(prefix);
163 return js_mkstr(js, path_part, plen);
164 }
165
166 return resolved;
167}
168
169ant_value_t module_library(ant_t *js) {
170 ant_value_t lib = js_mkobj(js);
171 js_set(js, lib, "createRequire", js_mkfun(builtin_createRequire));
172
173 ant_value_t modules_arr = js_mkarr(js);
174 builtin_iter_ctx_t ctx = { js, modules_arr };
175 ant_library_foreach(push_builtin_name, &ctx);
176
177 js_set(js, lib, "builtinModules", modules_arr);
178 js_set(js, lib, "_resolveFilename", js_mkfun(builtin_resolveFilename));
179 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "Module", 6));
180
181 return lib;
182}