MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <stdlib.h>
2#include <string.h>
3#include <stdio.h>
4
5#include "ant.h"
6#include "utf8.h"
7#include "errors.h"
8#include "runtime.h"
9#include "internal.h"
10#include "silver/engine.h"
11#include "modules/symbol.h"
12#include "descriptors.h"
13#include "gc/roots.h"
14#include "gc/modules.h"
15
16#define DECL_SYM(name, _desc) static ant_value_t g_##name = {0};
17WELLKNOWN_SYMBOLS(DECL_SYM)
18#undef DECL_SYM
19
20#define DEF_GET_SYM(name, _desc) ant_value_t get_##name##_sym(void) { return g_##name; }
21WELLKNOWN_SYMBOLS(DEF_GET_SYM)
22#undef DEF_GET_SYM
23
24static ant_value_t builtin_Symbol(ant_t *js, ant_value_t *args, int nargs) {
25 if (vtype(js->new_target) != T_UNDEF)
26 return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol is not a constructor");
27
28 const char *desc = NULL;
29 if (nargs > 0 && vtype(args[0]) == T_STR) {
30 desc = js_getstr(js, args[0], NULL);
31 }
32 return js_mksym(js, desc);
33}
34
35static ant_value_t builtin_Symbol_for(ant_t *js, ant_value_t *args, int nargs) {
36 if (nargs < 1 || vtype(args[0]) != T_STR) {
37 return js_mkerr(js, "Symbol.for requires a string argument");
38 }
39
40 char *key = js_getstr(js, args[0], NULL);
41 if (!key) return js_mkerr(js, "Invalid key");
42
43 return js_mksym_for(js, key);
44}
45
46static ant_value_t builtin_Symbol_keyFor(ant_t *js, ant_value_t *args, int nargs) {
47 if (nargs < 1 || vtype(args[0]) != T_SYMBOL) {
48 return js_mkundef();
49 }
50
51 const char *key = js_sym_key(args[0]);
52 if (!key) return js_mkundef();
53
54 return js_mkstr(js, key, strlen(key));
55}
56
57static ant_value_t builtin_Symbol_toString(ant_t *js, ant_value_t *args, int nargs) {
58 ant_value_t this_val = js_getthis(js);
59
60 if (vtype(this_val) != T_SYMBOL) {
61 return js_mkerr(js, "Symbol.prototype.toString requires a symbol");
62 }
63
64 return js_symbol_to_string(js, this_val);
65}
66
67static ant_value_t builtin_Symbol_description(ant_t *js, ant_value_t *args, int nargs) {
68 ant_value_t this_val = js_getthis(js);
69 ant_value_t sym = this_val;
70
71 if (vtype(sym) != T_SYMBOL && is_object_type(sym)) {
72 ant_value_t prim = js_get_slot(sym, SLOT_PRIMITIVE);
73 if (vtype(prim) == T_SYMBOL) sym = prim;
74 }
75
76 if (vtype(sym) != T_SYMBOL)
77 return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol.prototype.description requires a symbol");
78
79 const char *desc = js_sym_desc(sym);
80 if (!desc) return js_mkundef();
81
82 return js_mkstr(js, desc, strlen(desc));
83}
84
85static ant_value_t get_iterator_prototype(ant_t *js) {
86 if (vtype(js->sym.iterator_proto) == T_OBJ) return js->sym.iterator_proto;
87
88 js->sym.iterator_proto = js_mkobj(js);
89 js_set_proto_init(js->sym.iterator_proto, js->sym.object_proto);
90 js_set_sym(js, js->sym.iterator_proto, g_iterator, js_mkfun(sym_this_cb));
91
92 return js->sym.iterator_proto;
93}
94
95static inline ant_value_t iter_get_element(ant_t *js, ant_value_t obj, uint32_t idx) {
96 if (vtype(obj) == T_ARR) return js_arr_get(js, obj, (ant_offset_t)idx);
97 char buf[16]; snprintf(buf, sizeof(buf), "%u", idx);
98 return js_get(js, obj, buf);
99}
100
101static inline ant_offset_t iter_get_length(ant_t *js, ant_value_t obj) {
102 if (vtype(obj) == T_ARR) return js_arr_len(js, obj);
103 ant_value_t v = js_get(js, obj, "length");
104 return (vtype(v) == T_NUM) ? (ant_offset_t)js_getnum(v) : 0;
105}
106
107static bool advance_array(ant_t *js, js_iter_t *it, ant_value_t *out) {
108 ant_value_t iter = it->iterator;
109 ant_value_t array = js_get_slot(iter, SLOT_DATA);
110 ant_value_t state_v = js_get_slot(iter, SLOT_ITER_STATE);
111
112 uint32_t state = (vtype(state_v) == T_NUM) ? (uint32_t)js_getnum(state_v) : 0;
113 uint32_t kind = ITER_STATE_KIND(state);
114 uint32_t idx = ITER_STATE_INDEX(state);
115 ant_offset_t len = iter_get_length(js, array);
116 if (idx >= (uint32_t)len) return false;
117
118 switch (kind) {
119 case ARR_ITER_KEYS:
120 *out = js_mknum((double)idx);
121 break;
122 case ARR_ITER_ENTRIES: {
123 ant_value_t pair = js_mkarr(js);
124 js_arr_push(js, pair, js_mknum((double)idx));
125 js_arr_push(js, pair, iter_get_element(js, array, idx));
126 *out = pair;
127 break;
128 }
129 default:
130 *out = iter_get_element(js, array, idx);
131 break;
132 }
133
134 js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, idx + 1)));
135 return true;
136}
137
138static bool advance_string(ant_t *js, js_iter_t *it, ant_value_t *out) {
139 ant_value_t iter = it->iterator;
140 ant_value_t str = js_get_slot(iter, SLOT_DATA);
141 ant_value_t idx_v = js_get_slot(iter, SLOT_ITER_STATE);
142 int idx = (vtype(idx_v) == T_NUM) ? (int)js_getnum(idx_v) : 0;
143
144 size_t slen;
145 char *s = js_getstr(js, str, &slen);
146 if (idx >= (int)slen) return false;
147
148 unsigned char c = (unsigned char)s[idx];
149 int char_bytes = utf8_sequence_length(c);
150 if (char_bytes < 1) char_bytes = 1;
151 if (idx + char_bytes > (int)slen) char_bytes = (int)slen - idx;
152
153 *out = js_mkstr(js, s + idx, (ant_offset_t)char_bytes);
154 js_set_slot(iter, SLOT_ITER_STATE, js_mknum(idx + char_bytes));
155 return true;
156}
157
158static ant_value_t arr_iter_next(ant_t *js, ant_value_t *args, int nargs) {
159 js_iter_t it = { .iterator = js->this_val };
160 ant_value_t value;
161 return js_iter_result(js, advance_array(js, &it, &value), value);
162}
163
164static ant_value_t get_array_iterator_prototype(ant_t *js) {
165 if (vtype(js->sym.array_iterator_proto) == T_OBJ) return js->sym.array_iterator_proto;
166
167 ant_value_t iterator_proto = get_iterator_prototype(js);
168 js->sym.array_iterator_proto = js_mkobj(js);
169 js_set(js, js->sym.array_iterator_proto, "next", js_mkfun(arr_iter_next));
170 js_set_proto_init(js->sym.array_iterator_proto, iterator_proto);
171
172 return js->sym.array_iterator_proto;
173}
174
175ant_value_t make_array_iterator(ant_t *js, ant_value_t array, int kind) {
176 ant_value_t iter = js_mkobj(js);
177 js_set_slot_wb(js, iter, SLOT_DATA, array);
178 js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, 0)));
179 js_set_proto_init(iter, get_array_iterator_prototype(js));
180 return iter;
181}
182
183static ant_value_t str_iter_next(ant_t *js, ant_value_t *args, int nargs) {
184 js_iter_t it = { .iterator = js->this_val };
185 ant_value_t value;
186 return js_iter_result(js, advance_string(js, &it, &value), value);
187}
188
189static ant_value_t get_string_iterator_prototype(ant_t *js) {
190 if (vtype(js->sym.string_iterator_proto) == T_OBJ) return js->sym.string_iterator_proto;
191
192 ant_value_t iterator_proto = get_iterator_prototype(js);
193 js->sym.string_iterator_proto = js_mkobj(js);
194 js_set(js, js->sym.string_iterator_proto, "next", js_mkfun(str_iter_next));
195 js_set_proto_init(js->sym.string_iterator_proto, iterator_proto);
196
197 return js->sym.string_iterator_proto;
198}
199
200static ant_value_t string_iterator(ant_t *js, ant_value_t *args, int nargs) {
201 ant_value_t iter = js_mkobj(js);
202
203 js_set_slot_wb(js, iter, SLOT_DATA, js->this_val);
204 js_set_slot(iter, SLOT_ITER_STATE, js_mknum(0));
205 js_set_proto_init(iter, get_string_iterator_prototype(js));
206
207 return iter;
208}
209
210static struct {
211 ant_value_t proto;
212 js_iter_advance_fn fn;
213} g_advance_table[8];
214
215static int g_advance_count = 0;
216
217void js_iter_register_advance(ant_value_t proto, js_iter_advance_fn fn) {
218 if (g_advance_count < 8)
219 g_advance_table[g_advance_count++] = (typeof(g_advance_table[0])){ proto, fn };
220}
221
222bool js_iter_open(ant_t *js, ant_value_t iterable, js_iter_t *it) {
223 memset(it, 0, sizeof(*it));
224
225 ant_value_t iter_fn = js_get_sym(js, iterable, get_iterator_sym());
226 if (!is_callable(iter_fn)) return false;
227
228 ant_value_t iterator = sv_vm_call(js->vm, js, iter_fn, iterable, NULL, 0, NULL, false);
229 if (is_err(iterator)) return false;
230
231 it->iterator = iterator;
232 it->next_fn = js_getprop_fallback(js, iterator, "next");
233 it->advance = NULL;
234
235 ant_value_t proto = (vtype(iterator) == T_OBJ) ? js_get_proto(js, iterator) : js_mkundef();
236 for (int i = 0; i < g_advance_count; i++)
237 if (proto == g_advance_table[i].proto) { it->advance = g_advance_table[i].fn; break; }
238
239 return true;
240}
241
242bool js_iter_next(ant_t *js, js_iter_t *it, ant_value_t *out) {
243 if (it->advance) return it->advance(js, it, out);
244
245 ant_value_t next_fn = it->next_fn;
246 ant_value_t result;
247
248 if (vtype(next_fn) == T_CFUNC) {
249 ant_value_t old_this = js->this_val;
250 js->this_val = it->iterator;
251 result = js_as_cfunc(next_fn)(js, NULL, 0);
252 js->this_val = old_this;
253 }
254
255 else if (is_callable(next_fn)) result = sv_vm_call(js->vm, js, next_fn, it->iterator, NULL, 0, NULL, false);
256 else return false;
257
258 if (is_err(result)) return false;
259 ant_value_t done = js_getprop_fallback(js, result, "done");
260
261 if (js_truthy(js, done)) return false;
262 *out = js_getprop_fallback(js, result, "value");
263
264 return true;
265}
266
267void js_iter_close(ant_t *js, js_iter_t *it) {
268 if (it->advance) return;
269 ant_value_t return_fn = js_getprop_fallback(js, it->iterator, "return");
270 if (is_callable(return_fn)) sv_vm_call(js->vm, js, return_fn, it->iterator, NULL, 0, NULL, false);
271}
272
273ant_value_t maybe_call_symbol_method(
274 ant_t *js, ant_value_t target,
275 ant_value_t sym,
276 ant_value_t this_arg, ant_value_t *args,
277 int nargs, bool *called
278) {
279 *called = false;
280 if (vtype(sym) != T_SYMBOL || !is_object_type(target)) return js_mkundef();
281
282 ant_value_t method = js_get_sym(js, target, sym);
283 if (is_err(method)) return method;
284
285 uint8_t mt = vtype(method);
286 if (mt == T_UNDEF || mt == T_NULL) return js_mkundef();
287 if (!is_callable(method)) {
288 return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol method is not callable");
289 }
290
291 *called = true;
292 return sv_vm_call(js->vm, js, method, this_arg, args, nargs, NULL, false);
293}
294
295void js_define_species_getter(ant_t *js, ant_value_t ctor) {
296 if (!is_object_type(ctor) || vtype(g_species) != T_SYMBOL) return;
297 ctor = js_as_obj(ctor);
298 js_set_sym_getter_desc(js, ctor, g_species, js_mkfun(sym_this_cb), JS_DESC_C);
299}
300
301void init_symbol_module(void) {
302 ant_t *js = rt->js;
303
304 js->sym.iterator_proto = js_mkundef();
305 js->sym.array_iterator_proto = js_mkundef();
306 js->sym.string_iterator_proto = js_mkundef();
307 js->sym.generator_proto = js_mkundef();
308 js->sym.async_generator_proto = js_mkundef();
309 js->sym.async_iterator_proto = js_mkundef();
310
311 gc_register_root(&js->sym.iterator_proto);
312 gc_register_root(&js->sym.array_iterator_proto);
313 gc_register_root(&js->sym.string_iterator_proto);
314 gc_register_root(&js->sym.generator_proto);
315 gc_register_root(&js->sym.async_generator_proto);
316 gc_register_root(&js->sym.async_iterator_proto);
317
318 #define INIT_SYM(name, desc) g_##name = js_mksym_well_known(js, desc);
319 WELLKNOWN_SYMBOLS(INIT_SYM)
320 #undef INIT_SYM
321
322 ant_value_t symbol_proto = js_mkobj(js);
323 ant_value_t object_proto = js->sym.object_proto;
324
325 if (is_object_type(object_proto)) js_set_proto_init(symbol_proto, object_proto);
326 js_set(js, symbol_proto, "toString", js_mkfun(builtin_Symbol_toString));
327 js_set_getter_desc(js, symbol_proto, "description", 11, js_mkfun(builtin_Symbol_description), JS_DESC_C);
328
329 ant_value_t symbol_ctor = js_mkobj(js);
330 js_set_slot(symbol_ctor, SLOT_CFUNC, js_mkfun(builtin_Symbol));
331 js_setprop(js, symbol_ctor, js_mkstr(js, "for", 3), js_mkfun(builtin_Symbol_for));
332 js_set(js, symbol_ctor, "keyFor", js_mkfun(builtin_Symbol_keyFor));
333 js_set(js, symbol_ctor, "prototype", symbol_proto);
334
335 #define SET_CTOR_SYM(name, _desc) js_set(js, symbol_ctor, #name, g_##name);
336 WELLKNOWN_SYMBOLS(SET_CTOR_SYM)
337 #undef SET_CTOR_SYM
338
339 ant_value_t func_symbol = js_obj_to_func(symbol_ctor);
340 js_set(js, js_glob(js), "Symbol", func_symbol);
341
342 // set internal types before ant module snapshot
343 ant_value_t array_ctor = js_get(js, js_glob(js), "Array");
344 ant_value_t array_proto = js_get(js, array_ctor, "prototype");
345
346 (void)get_array_iterator_prototype(js);
347 (void)get_string_iterator_prototype(js);
348
349 js_iter_register_advance(js->sym.array_iterator_proto, advance_array);
350 js_iter_register_advance(js->sym.string_iterator_proto, advance_string);
351
352 js_set_sym(js, rt->ant_obj, g_toStringTag, js_mkstr(js, "Ant", 3));
353 js_set_sym(js, array_proto, g_iterator, js_get(js, array_proto, "values"));
354
355 ant_value_t array_unscopables = js_mkobj(js);
356 js_set(js, array_unscopables, "find", js_true);
357 js_set(js, array_unscopables, "findIndex", js_true);
358 js_set(js, array_unscopables, "fill", js_true);
359 js_set(js, array_unscopables, "copyWithin", js_true);
360 js_set(js, array_unscopables, "entries", js_true);
361 js_set(js, array_unscopables, "keys", js_true);
362 js_set(js, array_unscopables, "values", js_true);
363 js_set(js, array_unscopables, "flat", js_true);
364 js_set(js, array_unscopables, "flatMap", js_true);
365 js_set_sym(js, array_proto, g_unscopables, array_unscopables);
366
367 ant_value_t string_ctor = js_get(js, js_glob(js), "String");
368 ant_value_t string_proto = js_get(js, string_ctor, "prototype");
369 js_set_sym(js, string_proto, g_iterator, js_mkfun(string_iterator));
370
371 ant_value_t promise_ctor = js_get(js, js_glob(js), "Promise");
372 ant_value_t promise_proto = js_get(js, promise_ctor, "prototype");
373 js_set_sym(js, promise_proto, g_toStringTag, js_mkstr(js, "Promise", 7));
374
375 ant_value_t async_func_proto = js_get_slot(js_glob(js), SLOT_ASYNC_PROTO);
376 js_set_sym(js, async_func_proto, g_toStringTag, js_mkstr(js, "AsyncFunction", 13));
377
378 ant_value_t async_generator_func_proto = js_get_slot(js_glob(js), SLOT_ASYNC_GENERATOR_PROTO);
379 js_set_sym(js, async_generator_func_proto, g_toStringTag, js_mkstr(js, "AsyncGeneratorFunction", 22));
380
381 js_define_species_getter(js, promise_ctor);
382 js_define_species_getter(js, array_ctor);
383}
384
385void gc_mark_symbols(ant_t *js, gc_mark_fn mark) {
386 mark(js, js->sym.iterator_proto);
387 mark(js, js->sym.array_iterator_proto);
388 mark(js, js->sym.string_iterator_proto);
389 mark(js, js->sym.generator_proto);
390 mark(js, js->sym.async_generator_proto);
391 mark(js, js->sym.async_iterator_proto);
392
393 #define GC_SYM(name, _desc) mark(js, g_##name);
394 WELLKNOWN_SYMBOLS(GC_SYM)
395 #undef GC_SYM
396}