MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1// TODO: split module into smaller files
2
3#include <compat.h> // IWYU pragma: keep
4
5#include <ctype.h>
6#include <math.h>
7#include <stdbool.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include "ant.h"
13#include "internal.h"
14#include "esm/library.h"
15#include "silver/engine.h"
16
17#include "modules/buffer.h"
18#include "modules/date.h"
19#include "modules/json.h"
20#include "modules/symbol.h"
21#include "modules/util.h"
22#include "modules/abort.h"
23#include "modules/collections.h"
24
25typedef struct {
26 char *buf;
27 size_t len;
28 size_t cap;
29} util_sb_t;
30
31typedef struct {
32 const char *name;
33 const char *open;
34 const char *close;
35} util_style_entry_t;
36
37typedef enum {
38 UTIL_PARSE_ARG_TYPE_BOOLEAN = 0,
39 UTIL_PARSE_ARG_TYPE_STRING,
40} util_parse_arg_type_t;
41
42typedef struct {
43 char *name;
44 char short_name;
45 util_parse_arg_type_t type;
46 bool multiple;
47 ant_value_t default_value;
48} util_parse_arg_option_t;
49
50// TODO: migrate to crprintf
51static const util_style_entry_t util_styles[] = {
52 {"bold", "\x1b[1m", "\x1b[22m"},
53 {"dim", "\x1b[2m", "\x1b[22m"},
54 {"italic", "\x1b[3m", "\x1b[23m"},
55 {"underline", "\x1b[4m", "\x1b[24m"},
56 {"inverse", "\x1b[7m", "\x1b[27m"},
57 {"hidden", "\x1b[8m", "\x1b[28m"},
58 {"strikethrough", "\x1b[9m", "\x1b[29m"},
59 {"black", "\x1b[30m", "\x1b[39m"},
60 {"red", "\x1b[31m", "\x1b[39m"},
61 {"green", "\x1b[32m", "\x1b[39m"},
62 {"yellow", "\x1b[33m", "\x1b[39m"},
63 {"blue", "\x1b[34m", "\x1b[39m"},
64 {"magenta", "\x1b[35m", "\x1b[39m"},
65 {"cyan", "\x1b[36m", "\x1b[39m"},
66 {"white", "\x1b[37m", "\x1b[39m"},
67 {"gray", "\x1b[90m", "\x1b[39m"},
68 {"grey", "\x1b[90m", "\x1b[39m"},
69 {"bgBlack", "\x1b[40m", "\x1b[49m"},
70 {"bgRed", "\x1b[41m", "\x1b[49m"},
71 {"bgGreen", "\x1b[42m", "\x1b[49m"},
72 {"bgYellow", "\x1b[43m", "\x1b[49m"},
73 {"bgBlue", "\x1b[44m", "\x1b[49m"},
74 {"bgMagenta", "\x1b[45m", "\x1b[49m"},
75 {"bgCyan", "\x1b[46m", "\x1b[49m"},
76 {"bgWhite", "\x1b[47m", "\x1b[49m"},
77};
78
79static bool util_sb_reserve(util_sb_t *sb, size_t extra) {
80 size_t need = sb->len + extra + 1;
81 if (need <= sb->cap) return true;
82
83 size_t next = sb->cap ? sb->cap : 128;
84 while (next < need) next *= 2;
85
86 char *buf = (char *)realloc(sb->buf, next);
87 if (!buf) return false;
88
89 sb->buf = buf;
90 sb->cap = next;
91 return true;
92}
93
94static bool util_sb_append_n(util_sb_t *sb, const char *s, size_t n) {
95 if (n == 0) return true;
96 if (!util_sb_reserve(sb, n)) return false;
97 memcpy(sb->buf + sb->len, s, n);
98 sb->len += n;
99 sb->buf[sb->len] = '\0';
100 return true;
101}
102
103static bool util_sb_append_c(util_sb_t *sb, char c) {
104 if (!util_sb_reserve(sb, 1)) return false;
105 sb->buf[sb->len++] = c;
106 sb->buf[sb->len] = '\0';
107 return true;
108}
109
110static bool util_sb_append_jsval(ant_t *js, util_sb_t *sb, ant_value_t v) {
111 char cbuf[512];
112 js_cstr_t cstr = js_to_cstr(js, v, cbuf, sizeof(cbuf));
113 size_t len = strlen(cstr.ptr);
114 bool ok = util_sb_append_n(sb, cstr.ptr, len);
115 if (cstr.needs_free) free((void *)cstr.ptr);
116 return ok;
117}
118
119static bool util_sb_append_json(ant_t *js, util_sb_t *sb, ant_value_t v) {
120 ant_value_t json = json_stringify_value(js, v);
121 if (vtype(json) != T_STR) {
122 return util_sb_append_n(sb, "[Circular]", 10);
123 }
124 size_t len = 0;
125 const char *s = js_getstr(js, json, &len);
126 return s ? util_sb_append_n(sb, s, len) : util_sb_append_n(sb, "[Circular]", 10);
127}
128
129static bool util_set_named_value(
130 ant_t *js,
131 ant_value_t values,
132 const char *name,
133 ant_value_t value,
134 bool multiple
135) {
136 if (!multiple) {
137 js_set(js, values, name, value);
138 return !js->thrown_exists;
139 }
140
141 ant_value_t existing = js_get(js, values, name);
142 if (is_err(existing)) return false;
143
144 if (vtype(existing) == T_UNDEF) {
145 ant_value_t arr = js_mkarr(js);
146 js_arr_push(js, arr, value);
147 js_set(js, values, name, arr);
148 return !js->thrown_exists;
149 }
150
151 if (vtype(existing) == T_ARR) {
152 js_arr_push(js, existing, value);
153 return !js->thrown_exists;
154 }
155
156 ant_value_t arr = js_mkarr(js);
157 js_arr_push(js, arr, existing);
158 js_arr_push(js, arr, value);
159 js_set(js, values, name, arr);
160 return !js->thrown_exists;
161}
162
163static util_parse_arg_option_t *util_find_option_by_name(
164 util_parse_arg_option_t *options,
165 size_t option_count,
166 const char *name
167) {
168 for (size_t i = 0; i < option_count; i++) {
169 if (strcmp(options[i].name, name) == 0) return &options[i];
170 }
171 return NULL;
172}
173
174static util_parse_arg_option_t *util_find_option_by_short(
175 util_parse_arg_option_t *options,
176 size_t option_count,
177 char short_name
178) {
179 for (size_t i = 0; i < option_count; i++) {
180 if (options[i].short_name == short_name) return &options[i];
181 }
182 return NULL;
183}
184
185static void util_free_parse_options(util_parse_arg_option_t *options, size_t option_count) {
186 if (!options) return;
187 for (size_t i = 0; i < option_count; i++) free(options[i].name);
188 free(options);
189}
190
191static ant_value_t util_parse_args(ant_t *js, ant_value_t *args, int nargs) {
192 ant_value_t config = nargs > 0 ? args[0] : js_mkundef();
193 if (!is_object_type(config)) {
194 return js_mkerr_typed(js, JS_ERR_TYPE, "parseArgs(config) requires an options object");
195 }
196
197 ant_value_t args_list = js_get(js, config, "args");
198 if (is_err(args_list)) return args_list;
199 if (vtype(args_list) == T_UNDEF) {
200 ant_value_t process_obj = js_get(js, js_glob(js), "process");
201 if (is_err(process_obj)) return process_obj;
202 args_list = js_get(js, process_obj, "argv");
203 if (is_err(args_list)) return args_list;
204 }
205
206 ant_value_t options_obj = js_get(js, config, "options");
207 if (is_err(options_obj)) return options_obj;
208 if (!is_object_type(options_obj)) options_obj = js_mkobj(js);
209
210 bool strict = js_truthy(js, js_get(js, config, "strict"));
211 if (vtype(js_get(js, config, "strict")) == T_UNDEF) strict = true;
212 bool allow_positionals = js_truthy(js, js_get(js, config, "allowPositionals"));
213
214 size_t option_count = 0;
215 {
216 ant_iter_t iter = js_prop_iter_begin(js, options_obj);
217 const char *key = NULL;
218 size_t key_len = 0;
219 while (js_prop_iter_next(&iter, &key, &key_len, NULL)) option_count++;
220 js_prop_iter_end(&iter);
221 }
222
223 util_parse_arg_option_t *options = NULL;
224 if (option_count > 0) {
225 options = calloc(option_count, sizeof(*options));
226 if (!options) return js_mkerr(js, "Out of memory");
227
228 ant_iter_t iter = js_prop_iter_begin(js, options_obj);
229 const char *key = NULL;
230 size_t key_len = 0;
231 size_t idx = 0;
232
233 while (idx < option_count && js_prop_iter_next(&iter, &key, &key_len, NULL)) {
234 ant_value_t spec = js_get(js, options_obj, key);
235 ant_value_t type_val = is_object_type(spec) ? js_get(js, spec, "type") : js_mkundef();
236 ant_value_t short_val = is_object_type(spec) ? js_get(js, spec, "short") : js_mkundef();
237 ant_value_t multiple_val = is_object_type(spec) ? js_get(js, spec, "multiple") : js_mkundef();
238 ant_value_t default_val = is_object_type(spec) ? js_get(js, spec, "default") : js_mkundef();
239
240 options[idx].name = strndup(key, key_len);
241 if (!options[idx].name) {
242 js_prop_iter_end(&iter);
243 util_free_parse_options(options, option_count);
244 return js_mkerr(js, "Out of memory");
245 }
246
247 options[idx].type = UTIL_PARSE_ARG_TYPE_BOOLEAN;
248 if (vtype(type_val) == T_STR) {
249 size_t type_len = 0;
250 const char *type_str = js_getstr(js, type_val, &type_len);
251 if (type_str && type_len == 6 && memcmp(type_str, "string", 6) == 0) {
252 options[idx].type = UTIL_PARSE_ARG_TYPE_STRING;
253 }
254 }
255
256 if (vtype(short_val) == T_STR) {
257 size_t short_len = 0;
258 const char *short_str = js_getstr(js, short_val, &short_len);
259 if (short_str && short_len > 0) options[idx].short_name = short_str[0];
260 }
261
262 options[idx].multiple = js_truthy(js, multiple_val);
263 options[idx].default_value = default_val;
264 idx++;
265 }
266
267 js_prop_iter_end(&iter);
268 }
269
270 ant_value_t values = js_mkobj(js);
271 ant_value_t positionals = js_mkarr(js);
272 ant_value_t out = js_mkobj(js);
273
274 for (size_t i = 0; i < option_count; i++) {
275 if (vtype(options[i].default_value) != T_UNDEF) {
276 if (!util_set_named_value(js, values, options[i].name, options[i].default_value, options[i].multiple)) {
277 util_free_parse_options(options, option_count);
278 return js->thrown_exists ? js->thrown_value : js_mkerr(js, "parseArgs failed to set default");
279 }}}
280
281 ant_offset_t arg_len = js_arr_len(js, args_list);
282 bool stop_parsing = false;
283
284 for (ant_offset_t i = 0; i < arg_len; i++) {
285 ant_value_t arg_val = js_arr_get(js, args_list, i);
286 if (vtype(arg_val) != T_STR) continue;
287
288 size_t arg_slen = 0;
289 const char *arg = js_getstr(js, arg_val, &arg_slen);
290 if (!arg) continue;
291
292 if (stop_parsing) {
293 js_arr_push(js, positionals, arg_val);
294 continue;
295 }
296
297 if (arg_slen == 2 && memcmp(arg, "--", 2) == 0) {
298 stop_parsing = true;
299 continue;
300 }
301
302 if (arg_slen > 2 && arg[0] == '-' && arg[1] == '-') {
303 const char *name = arg + 2;
304 size_t name_len = arg_slen - 2;
305 const char *inline_value = NULL;
306 size_t inline_len = 0;
307 const char *eq = memchr(name, '=', name_len);
308
309 if (eq) {
310 name_len = (size_t)(eq - name);
311 inline_value = eq + 1;
312 inline_len = (size_t)(arg + arg_slen - inline_value);
313 }
314
315 char *name_buf = strndup(name, name_len);
316 if (!name_buf) {
317 util_free_parse_options(options, option_count);
318 return js_mkerr(js, "Out of memory");
319 }
320
321 util_parse_arg_option_t *opt = util_find_option_by_name(options, option_count, name_buf);
322 if (!opt) {
323 if (strict) {
324 free(name_buf);
325 util_free_parse_options(options, option_count);
326 return js_mkerr_typed(js, JS_ERR_TYPE, "Unknown option '--%.*s'", (int)name_len, name);
327 }
328
329 ant_value_t unknown = inline_value
330 ? js_mkstr(js, inline_value, inline_len)
331 : js_true;
332
333 js_set(js, values, name_buf, unknown);
334 free(name_buf);
335
336 continue;
337 }
338
339 ant_value_t parsed_value = js_true;
340 if (opt->type == UTIL_PARSE_ARG_TYPE_STRING) {
341 if (inline_value) parsed_value = js_mkstr(js, inline_value, inline_len);
342 else if (i + 1 < arg_len) parsed_value = js_arr_get(js, args_list, ++i);
343 else {
344 free(name_buf);
345 util_free_parse_options(options, option_count);
346 return js_mkerr_typed(js, JS_ERR_TYPE, "Option '--%s' requires a value", opt->name);
347 }
348 } else if (inline_value) parsed_value = js_mkstr(js, inline_value, inline_len);
349
350 if (!util_set_named_value(js, values, opt->name, parsed_value, opt->multiple)) {
351 free(name_buf);
352 util_free_parse_options(options, option_count);
353 return js->thrown_exists ? js->thrown_value : js_mkerr(js, "parseArgs failed");
354 }
355
356 free(name_buf);
357 continue;
358 }
359
360 if (arg_slen > 1 && arg[0] == '-') {
361 for (size_t j = 1; j < arg_slen; j++) {
362 util_parse_arg_option_t *opt = util_find_option_by_short(options, option_count, arg[j]);
363 if (!opt) {
364 if (strict) {
365 util_free_parse_options(options, option_count);
366 return js_mkerr_typed(js, JS_ERR_TYPE, "Unknown option '-%c'", arg[j]);
367 }
368 char key[2] = {arg[j], '\0'};
369 js_set(js, values, key, js_true);
370 continue;
371 }
372
373 ant_value_t parsed_value = js_true;
374 if (opt->type == UTIL_PARSE_ARG_TYPE_STRING) {
375 if (j + 1 < arg_slen) {
376 parsed_value = js_mkstr(js, arg + j + 1, arg_slen - (j + 1));
377 j = arg_slen;
378 } else if (i + 1 < arg_len) {
379 parsed_value = js_arr_get(js, args_list, ++i);
380 j = arg_slen;
381 } else {
382 util_free_parse_options(options, option_count);
383 return js_mkerr_typed(js, JS_ERR_TYPE, "Option '-%c' requires a value", arg[j]);
384 }
385 }
386
387 if (!util_set_named_value(js, values, opt->name, parsed_value, opt->multiple)) {
388 util_free_parse_options(options, option_count);
389 return js->thrown_exists ? js->thrown_value : js_mkerr(js, "parseArgs failed");
390 }
391
392 if (opt->type == UTIL_PARSE_ARG_TYPE_STRING) break;
393 }
394 continue;
395 }
396
397 if (!allow_positionals) {
398 util_free_parse_options(options, option_count);
399 return js_mkerr_typed(js, JS_ERR_TYPE, "Unexpected positional argument '%s'", arg);
400 }
401 js_arr_push(js, positionals, arg_val);
402 }
403
404 util_free_parse_options(options, option_count);
405 js_set(js, out, "values", values);
406 js_set(js, out, "positionals", positionals);
407
408 return out;
409}
410
411static ant_value_t util_format_impl(ant_t *js, ant_value_t *args, int nargs, int fmt_index) {
412 util_sb_t sb = {0};
413 if (fmt_index >= nargs) {
414 ant_value_t out = js_mkstr(js, "", 0);
415 free(sb.buf);
416 return out;
417 }
418
419 if (vtype(args[fmt_index]) != T_STR) {
420 for (int i = fmt_index; i < nargs; i++) {
421 if (i > fmt_index) util_sb_append_c(&sb, ' ');
422 util_sb_append_jsval(js, &sb, args[i]);
423 }
424 ant_value_t out = js_mkstr(js, sb.buf ? sb.buf : "", sb.len);
425 free(sb.buf);
426 return out;
427 }
428
429 size_t fmt_len = 0;
430 const char *fmt = js_getstr(js, args[fmt_index], &fmt_len);
431 int argi = fmt_index + 1;
432
433 for (size_t i = 0; i < fmt_len; i++) {
434 if (fmt[i] != '%' || i + 1 >= fmt_len) {
435 util_sb_append_c(&sb, fmt[i]);
436 continue;
437 }
438
439 char spec = fmt[i + 1];
440 if (spec == '%') {
441 util_sb_append_c(&sb, '%');
442 i++;
443 continue;
444 }
445
446 bool known = (
447 spec == 's' || spec == 'd' || spec == 'i' || spec == 'f' ||
448 spec == 'j' || spec == 'o' || spec == 'O' || spec == 'c'
449 );
450
451 if (!known) {
452 util_sb_append_c(&sb, '%');
453 util_sb_append_c(&sb, spec);
454 i++;
455 continue;
456 }
457
458 ant_value_t v = (argi < nargs) ? args[argi++] : js_mkundef();
459
460 if (spec == 's') {
461 util_sb_append_jsval(js, &sb, v);
462 } else if (spec == 'd' || spec == 'i') {
463 double d = js_to_number(js, v);
464 char nb[64];
465 if (isnan(d)) snprintf(nb, sizeof(nb), "NaN");
466 else if (!isfinite(d)) snprintf(nb, sizeof(nb), d < 0 ? "-Infinity" : "Infinity");
467 else snprintf(nb, sizeof(nb), "%lld", (long long)d);
468 util_sb_append_n(&sb, nb, strlen(nb));
469 } else if (spec == 'f') {
470 double d = js_to_number(js, v);
471 char nb[64];
472 if (isnan(d)) snprintf(nb, sizeof(nb), "NaN");
473 else if (!isfinite(d)) snprintf(nb, sizeof(nb), d < 0 ? "-Infinity" : "Infinity");
474 else snprintf(nb, sizeof(nb), "%g", d);
475 util_sb_append_n(&sb, nb, strlen(nb));
476 } else if (spec == 'j') {
477 util_sb_append_json(js, &sb, v);
478 } else if (spec == 'o' || spec == 'O') {
479 util_sb_append_jsval(js, &sb, v);
480 } else if (spec == 'c') {
481 // style placeholder: consume arg, emit nothing.
482 }
483
484 i++;
485 }
486
487 for (; argi < nargs; argi++) {
488 util_sb_append_c(&sb, ' ');
489 util_sb_append_jsval(js, &sb, args[argi]);
490 }
491
492 ant_value_t out = js_mkstr(js, sb.buf ? sb.buf : "", sb.len);
493 free(sb.buf);
494 return out;
495}
496
497static ant_value_t util_format(ant_t *js, ant_value_t *args, int nargs) {
498 return util_format_impl(js, args, nargs, 0);
499}
500
501static ant_value_t util_format_with_options(ant_t *js, ant_value_t *args, int nargs) {
502 if (nargs <= 1) return js_mkstr(js, "", 0);
503 return util_format_impl(js, args, nargs, 1);
504}
505
506static ant_value_t util_inspect(ant_t *js, ant_value_t *args, int nargs) {
507 if (nargs < 1) return js_mkstr(js, "undefined", 9);
508 char cbuf[512];
509 js_cstr_t cstr = js_to_cstr(js, args[0], cbuf, sizeof(cbuf));
510 ant_value_t out = js_mkstr(js, cstr.ptr, strlen(cstr.ptr));
511 if (cstr.needs_free) free((void *)cstr.ptr);
512 return out;
513}
514
515static bool util_has_proto_in_chain(ant_t *js, ant_value_t value, ant_value_t proto) {
516 if (!is_special_object(proto)) return false;
517
518 ant_value_t current = value;
519 while (is_special_object(current)) {
520 current = js_get_proto(js, current);
521 if (current == proto) return true;
522 }
523
524 return false;
525}
526
527static bool util_is_boxed_primitive(ant_value_t value, uint8_t *type_out) {
528 ant_value_t primitive;
529
530 if (!is_object_type(value)) return false;
531 primitive = js_get_slot(value, SLOT_PRIMITIVE);
532 if (vtype(primitive) == T_UNDEF) return false;
533
534 if (type_out) *type_out = vtype(primitive);
535 return true;
536}
537
538static bool util_has_to_string_tag(ant_t *js, ant_value_t value, const char *tag, size_t tag_len) {
539 ant_value_t to_string_tag;
540 size_t actual_len = 0;
541 const char *actual;
542
543 if (!is_object_type(value)) return false;
544
545 to_string_tag = js_get_sym(js, value, get_toStringTag_sym());
546 if (vtype(to_string_tag) != T_STR) return false;
547
548 actual = js_getstr(js, to_string_tag, &actual_len);
549 return actual != NULL && actual_len == tag_len && memcmp(actual, tag, tag_len) == 0;
550}
551
552static bool util_is_arguments_object_value(ant_t *js, ant_value_t value) {
553 ant_value_t callee = js_mkundef();
554
555 if (vtype(value) != T_ARR) return false;
556 if (js_get_slot(value, SLOT_STRICT_ARGS) == js_true) return true;
557 if (!util_has_to_string_tag(js, value, "Arguments", 9)) return false;
558
559 return js_try_get_own_data_prop(js, value, "callee", 6, &callee);
560}
561
562static ant_value_t util_types_is_any_array_buffer(ant_t *js, ant_value_t *args, int nargs) {
563 ArrayBufferData *buffer = (nargs > 0) ? buffer_get_arraybuffer_data(args[0]) : NULL;
564 return js_bool(buffer != NULL);
565}
566
567static ant_value_t util_types_is_array_buffer(ant_t *js, ant_value_t *args, int nargs) {
568 ArrayBufferData *buffer = (nargs > 0) ? buffer_get_arraybuffer_data(args[0]) : NULL;
569 return js_bool(buffer != NULL && !buffer->is_shared);
570}
571
572static ant_value_t util_types_is_shared_array_buffer(ant_t *js, ant_value_t *args, int nargs) {
573 ArrayBufferData *buffer = (nargs > 0) ? buffer_get_arraybuffer_data(args[0]) : NULL;
574 return js_bool(buffer != NULL && buffer->is_shared);
575}
576
577static ant_value_t util_types_is_array_buffer_view(ant_t *js, ant_value_t *args, int nargs) {
578 if (nargs < 1) return js_false;
579 return js_bool(buffer_is_dataview(args[0]) || buffer_get_typedarray_data(args[0]) != NULL);
580}
581
582static ant_value_t util_types_is_data_view(ant_t *js, ant_value_t *args, int nargs) {
583 if (nargs < 1) return js_false;
584 return js_bool(buffer_is_dataview(args[0]));
585}
586
587static ant_value_t util_types_is_typed_array(ant_t *js, ant_value_t *args, int nargs) {
588 if (nargs < 1) return js_false;
589 return js_bool(buffer_get_typedarray_data(args[0]) != NULL);
590}
591
592static ant_value_t util_types_is_float16_array(ant_t *js, ant_value_t *args, int nargs) {
593 TypedArrayData *typed_array = (nargs > 0) ? buffer_get_typedarray_data(args[0]) : NULL;
594 if (!typed_array) return js_false;
595 return js_bool(typed_array != NULL && typed_array->type == TYPED_ARRAY_FLOAT16);
596}
597
598#define DEFINE_TYPED_ARRAY_CHECK(fn_name, typed_array_kind) \
599 static ant_value_t fn_name(ant_t *js, ant_value_t *args, int nargs) { \
600 TypedArrayData *typed_array = (nargs > 0) ? buffer_get_typedarray_data(args[0]) : NULL; \
601 if (!typed_array) return js_false; \
602 return js_bool(typed_array != NULL && typed_array->type == typed_array_kind); \
603 }
604
605DEFINE_TYPED_ARRAY_CHECK(util_types_is_int8_array, TYPED_ARRAY_INT8)
606DEFINE_TYPED_ARRAY_CHECK(util_types_is_uint8_array, TYPED_ARRAY_UINT8)
607DEFINE_TYPED_ARRAY_CHECK(util_types_is_int16_array, TYPED_ARRAY_INT16)
608DEFINE_TYPED_ARRAY_CHECK(util_types_is_uint16_array, TYPED_ARRAY_UINT16)
609DEFINE_TYPED_ARRAY_CHECK(util_types_is_int32_array, TYPED_ARRAY_INT32)
610DEFINE_TYPED_ARRAY_CHECK(util_types_is_uint32_array, TYPED_ARRAY_UINT32)
611DEFINE_TYPED_ARRAY_CHECK(util_types_is_float32_array, TYPED_ARRAY_FLOAT32)
612DEFINE_TYPED_ARRAY_CHECK(util_types_is_float64_array, TYPED_ARRAY_FLOAT64)
613DEFINE_TYPED_ARRAY_CHECK(util_types_is_bigint64_array, TYPED_ARRAY_BIGINT64)
614DEFINE_TYPED_ARRAY_CHECK(util_types_is_biguint64_array, TYPED_ARRAY_BIGUINT64)
615DEFINE_TYPED_ARRAY_CHECK(util_types_is_uint8_clamped_array, TYPED_ARRAY_UINT8_CLAMPED)
616
617static ant_value_t util_types_is_promise(ant_t *js, ant_value_t *args, int nargs) {
618 if (nargs < 1) return js_false;
619 return js_bool(vtype(args[0]) == T_PROMISE);
620}
621
622static ant_value_t util_types_is_proxy(ant_t *js, ant_value_t *args, int nargs) {
623 if (nargs < 1 || !is_object_type(args[0])) return js_false;
624 return js_bool(is_proxy(args[0]));
625}
626
627static ant_value_t util_types_is_regexp(ant_t *js, ant_value_t *args, int nargs) {
628 ant_value_t regexp_proto;
629 if (nargs < 1 || !is_object_type(args[0])) return js_false;
630 regexp_proto = js_get_ctor_proto(js, "RegExp", 6);
631 return js_bool(util_has_proto_in_chain(js, args[0], regexp_proto));
632}
633
634static ant_value_t util_types_is_date(ant_t *js, ant_value_t *args, int nargs) {
635 if (nargs < 1) return js_false;
636 return js_bool(is_date_instance(args[0]));
637}
638
639static ant_value_t util_types_is_map(ant_t *js, ant_value_t *args, int nargs) {
640 if (nargs < 1 || vtype(args[0]) != T_OBJ) return js_false;
641 return js_bool(js_obj_ptr(args[0])->type_tag == T_MAP);
642}
643
644static ant_value_t util_types_is_set(ant_t *js, ant_value_t *args, int nargs) {
645 if (nargs < 1 || vtype(args[0]) != T_OBJ) return js_false;
646 return js_bool(js_obj_ptr(args[0])->type_tag == T_SET);
647}
648
649static ant_value_t util_types_is_weak_map(ant_t *js, ant_value_t *args, int nargs) {
650 if (nargs < 1 || vtype(args[0]) != T_OBJ) return js_false;
651 return js_bool(js_obj_ptr(args[0])->type_tag == T_WEAKMAP);
652}
653
654static ant_value_t util_types_is_weak_set(ant_t *js, ant_value_t *args, int nargs) {
655 if (nargs < 1 || vtype(args[0]) != T_OBJ) return js_false;
656 return js_bool(js_obj_ptr(args[0])->type_tag == T_WEAKSET);
657}
658
659static ant_value_t util_types_is_async_function(ant_t *js, ant_value_t *args, int nargs) {
660 ant_value_t func_obj;
661 if (nargs < 1 || vtype(args[0]) != T_FUNC) return js_false;
662 func_obj = js_func_obj(args[0]);
663 return js_bool(js_get_slot(func_obj, SLOT_ASYNC) == js_true);
664}
665
666static ant_value_t util_types_is_generator_function(ant_t *js, ant_value_t *args, int nargs) {
667 sv_closure_t *closure;
668
669 if (nargs < 1 || vtype(args[0]) != T_FUNC) return js_false;
670
671 closure = js_func_closure(args[0]);
672 return js_bool(closure != NULL && closure->func != NULL && closure->func->is_generator);
673}
674
675static ant_value_t util_types_is_generator_object(ant_t *js, ant_value_t *args, int nargs) {
676 if (nargs < 1) return js_false;
677 return js_bool(vtype(args[0]) == T_GENERATOR);
678}
679
680static ant_value_t util_types_is_arguments_object(ant_t *js, ant_value_t *args, int nargs) {
681 if (nargs < 1) return js_false;
682 return js_bool(util_is_arguments_object_value(js, args[0]));
683}
684
685static ant_value_t util_types_is_native_error(ant_t *js, ant_value_t *args, int nargs) {
686 if (nargs < 1 || !is_object_type(args[0])) return js_false;
687 return js_bool(js_get_slot(args[0], SLOT_ERROR_BRAND) == js_true);
688}
689
690static ant_value_t util_types_is_boxed_primitive(ant_t *js, ant_value_t *args, int nargs) {
691 return js_bool(nargs > 0 && util_is_boxed_primitive(args[0], NULL));
692}
693
694static ant_value_t util_types_is_boolean_object(ant_t *js, ant_value_t *args, int nargs) {
695 uint8_t type = T_UNDEF;
696 return js_bool(nargs > 0 && util_is_boxed_primitive(args[0], &type) && type == T_BOOL);
697}
698
699static ant_value_t util_types_is_number_object(ant_t *js, ant_value_t *args, int nargs) {
700 uint8_t type = T_UNDEF;
701 return js_bool(nargs > 0 && util_is_boxed_primitive(args[0], &type) && type == T_NUM);
702}
703
704static ant_value_t util_types_is_string_object(ant_t *js, ant_value_t *args, int nargs) {
705 uint8_t type = T_UNDEF;
706 return js_bool(nargs > 0 && util_is_boxed_primitive(args[0], &type) && type == T_STR);
707}
708
709static ant_value_t util_types_is_symbol_object(ant_t *js, ant_value_t *args, int nargs) {
710 uint8_t type = T_UNDEF;
711 return js_bool(nargs > 0 && util_is_boxed_primitive(args[0], &type) && type == T_SYMBOL);
712}
713
714static ant_value_t util_types_is_bigint_object(ant_t *js, ant_value_t *args, int nargs) {
715 uint8_t type = T_UNDEF;
716 return js_bool(nargs > 0 && util_is_boxed_primitive(args[0], &type) && type == T_BIGINT);
717}
718
719static ant_value_t util_types_is_map_iterator(ant_t *js, ant_value_t *args, int nargs) {
720 if (nargs < 1 || !is_object_type(args[0])) return js_false;
721 return js_bool(util_has_proto_in_chain(js, args[0], g_map_iter_proto));
722}
723
724static ant_value_t util_types_is_set_iterator(ant_t *js, ant_value_t *args, int nargs) {
725 if (nargs < 1 || !is_object_type(args[0])) return js_false;
726 return js_bool(util_has_proto_in_chain(js, args[0], g_set_iter_proto));
727}
728
729static ant_value_t util_types_is_module_namespace_object(ant_t *js, ant_value_t *args, int nargs) {
730 if (nargs < 1) return js_false;
731 return js_bool(js_check_brand(args[0], BRAND_MODULE_NAMESPACE));
732}
733
734ant_value_t util_types_library(ant_t *js) {
735 ant_value_t types = js_mkobj(js);
736
737 js_set(js, types, "isAnyArrayBuffer", js_mkfun(util_types_is_any_array_buffer));
738 js_set(js, types, "isArrayBuffer", js_mkfun(util_types_is_array_buffer));
739 js_set(js, types, "isArgumentsObject", js_mkfun(util_types_is_arguments_object));
740 js_set(js, types, "isArrayBufferView", js_mkfun(util_types_is_array_buffer_view));
741 js_set(js, types, "isAsyncFunction", js_mkfun(util_types_is_async_function));
742 js_set(js, types, "isBigInt64Array", js_mkfun(util_types_is_bigint64_array));
743 js_set(js, types, "isBigIntObject", js_mkfun(util_types_is_bigint_object));
744 js_set(js, types, "isBigUint64Array", js_mkfun(util_types_is_biguint64_array));
745 js_set(js, types, "isBooleanObject", js_mkfun(util_types_is_boolean_object));
746 js_set(js, types, "isBoxedPrimitive", js_mkfun(util_types_is_boxed_primitive));
747 js_set(js, types, "isDataView", js_mkfun(util_types_is_data_view));
748 js_set(js, types, "isDate", js_mkfun(util_types_is_date));
749 js_set(js, types, "isFloat16Array", js_mkfun(util_types_is_float16_array));
750 js_set(js, types, "isFloat32Array", js_mkfun(util_types_is_float32_array));
751 js_set(js, types, "isFloat64Array", js_mkfun(util_types_is_float64_array));
752 js_set(js, types, "isGeneratorFunction", js_mkfun(util_types_is_generator_function));
753 js_set(js, types, "isGeneratorObject", js_mkfun(util_types_is_generator_object));
754 js_set(js, types, "isInt8Array", js_mkfun(util_types_is_int8_array));
755 js_set(js, types, "isInt16Array", js_mkfun(util_types_is_int16_array));
756 js_set(js, types, "isInt32Array", js_mkfun(util_types_is_int32_array));
757 js_set(js, types, "isMap", js_mkfun(util_types_is_map));
758 js_set(js, types, "isMapIterator", js_mkfun(util_types_is_map_iterator));
759 js_set(js, types, "isModuleNamespaceObject", js_mkfun(util_types_is_module_namespace_object));
760 js_set(js, types, "isNativeError", js_mkfun(util_types_is_native_error));
761 js_set(js, types, "isNumberObject", js_mkfun(util_types_is_number_object));
762 js_set(js, types, "isPromise", js_mkfun(util_types_is_promise));
763 js_set(js, types, "isProxy", js_mkfun(util_types_is_proxy));
764 js_set(js, types, "isRegExp", js_mkfun(util_types_is_regexp));
765 js_set(js, types, "isSet", js_mkfun(util_types_is_set));
766 js_set(js, types, "isSetIterator", js_mkfun(util_types_is_set_iterator));
767 js_set(js, types, "isSharedArrayBuffer", js_mkfun(util_types_is_shared_array_buffer));
768 js_set(js, types, "isStringObject", js_mkfun(util_types_is_string_object));
769 js_set(js, types, "isSymbolObject", js_mkfun(util_types_is_symbol_object));
770 js_set(js, types, "isTypedArray", js_mkfun(util_types_is_typed_array));
771 js_set(js, types, "isUint8Array", js_mkfun(util_types_is_uint8_array));
772 js_set(js, types, "isUint8ClampedArray", js_mkfun(util_types_is_uint8_clamped_array));
773 js_set(js, types, "isUint16Array", js_mkfun(util_types_is_uint16_array));
774 js_set(js, types, "isUint32Array", js_mkfun(util_types_is_uint32_array));
775 js_set(js, types, "isWeakMap", js_mkfun(util_types_is_weak_map));
776 js_set(js, types, "isWeakSet", js_mkfun(util_types_is_weak_set));
777
778 return types;
779}
780
781static ant_value_t util_get_types_object(ant_t *js) {
782 bool loaded = false;
783 ant_value_t types = js_esm_load_registered_library(js, "util/types", 10, &loaded);
784 return loaded ? types : util_types_library(js);
785}
786
787static ant_value_t util_debuglog_call(ant_params_t) {
788 return js_mkundef();
789}
790
791static ant_value_t util_debuglog(ant_params_t) {
792 ant_value_t logger = js_mkfun(util_debuglog_call);
793 js_set(js, logger, "enabled", js_false);
794
795 if (nargs >= 2 && is_callable(args[1])) {
796 ant_value_t cb_args[1] = { logger };
797 ant_value_t result = sv_vm_call(js->vm, js, args[1], js_mkundef(), cb_args, 1, NULL, false);
798 if (is_err(result) || js->thrown_exists) return result;
799 }
800
801 return logger;
802}
803
804static ant_value_t util_strip_vt_control_characters(ant_t *js, ant_value_t *args, int nargs) {
805 if (nargs < 1) return js_mkstr(js, "", 0);
806
807 char cbuf[512];
808 js_cstr_t cstr = js_to_cstr(js, args[0], cbuf, sizeof(cbuf));
809 const char *src = cstr.ptr;
810 size_t len = strlen(src);
811
812 util_sb_t sb = {0};
813 for (size_t i = 0; i < len; i++) {
814 unsigned char ch = (unsigned char)src[i];
815 if (ch != 0x1b) {
816 util_sb_append_c(&sb, (char)ch);
817 continue;
818 }
819
820 if (i + 1 < len && src[i + 1] == '[') {
821 i += 2;
822 while (i < len) {
823 unsigned char c = (unsigned char)src[i];
824 if (c >= '@' && c <= '~') break;
825 i++;
826 }
827 continue;
828 }
829
830 if (i + 1 < len && src[i + 1] == ']') {
831 i += 2;
832 while (i < len) {
833 if ((unsigned char)src[i] == 0x07) break;
834 if ((unsigned char)src[i] == 0x1b && i + 1 < len && src[i + 1] == '\\') {
835 i++;
836 break;
837 }
838 i++;
839 }
840 continue;
841 }
842 }
843
844 ant_value_t out = js_mkstr(js, sb.buf ? sb.buf : "", sb.len);
845 if (cstr.needs_free) free((void *)cstr.ptr);
846 free(sb.buf);
847 return out;
848}
849
850static inline bool util_env_is_inline_ws(char ch) {
851 return ch == ' ' || ch == '\t' || ch == '\r';
852}
853
854static inline bool util_env_is_ident_start(char ch) {
855 return
856 (ch >= 'A' && ch <= 'Z') ||
857 (ch >= 'a' && ch <= 'z') ||
858 ch == '_';
859}
860
861static inline bool util_env_is_ident_continue(char ch) {
862 return util_env_is_ident_start(ch) || (ch >= '0' && ch <= '9');
863}
864
865static void util_env_skip_inline_ws(const char *src, size_t len, size_t *cursor) {
866 while (*cursor < len && util_env_is_inline_ws(src[*cursor])) (*cursor)++;
867}
868
869static void util_env_skip_line(const char *src, size_t len, size_t *cursor) {
870 while (*cursor < len && src[*cursor] != '\n') (*cursor)++;
871 if (*cursor < len && src[*cursor] == '\n') (*cursor)++;
872}
873
874static bool util_env_consume_export(const char *src, size_t len, size_t *cursor) {
875 if (*cursor + 6 > len) return false;
876 if (memcmp(src + *cursor, "export", 6) != 0) return false;
877
878 if (
879 *cursor + 6 < len &&
880 !util_env_is_inline_ws(src[*cursor + 6]) &&
881 src[*cursor + 6] != '\n'
882 ) return false;
883
884 *cursor += 6;
885 util_env_skip_inline_ws(src, len, cursor);
886 return true;
887}
888
889static bool util_env_parse_key(
890 const char *src, size_t len, size_t *cursor,
891 size_t *key_start, size_t *key_end
892) {
893 if (*cursor >= len || !util_env_is_ident_start(src[*cursor])) return false;
894 *key_start = *cursor; (*cursor)++;
895
896 while (*cursor < len && util_env_is_ident_continue(src[*cursor])) (*cursor)++;
897 *key_end = *cursor;
898
899 return true;
900}
901
902static void util_env_set_entry(
903 ant_t *js, ant_value_t obj, const char *key,
904 size_t key_len, ant_value_t value
905) {
906 ant_value_t key_str = js_mkstr(js, key, key_len);
907 js_setprop(js, obj, key_str, value);
908}
909
910static ant_value_t util_env_parse_quoted_value(
911 ant_t *js, const char *src,
912 size_t len, size_t *cursor
913) {
914 util_sb_t sb = {0};
915 ant_value_t value = js_mkstr(js, "", 0);
916 char quote;
917
918 if (*cursor >= len) return value;
919 quote = src[(*cursor)++];
920
921 while (*cursor < len) {
922 char ch = src[(*cursor)++];
923 if (ch == quote) goto done;
924 if (ch != '\\' || *cursor >= len) {
925 util_sb_append_c(&sb, ch);
926 continue;
927 }
928
929 char esc = src[(*cursor)++];
930 if (quote != '"') {
931 util_sb_append_c(&sb, esc);
932 continue;
933 }
934
935 switch (esc) {
936 case 'n': util_sb_append_c(&sb, '\n'); break;
937 case 'r': util_sb_append_c(&sb, '\r'); break;
938 case 't': util_sb_append_c(&sb, '\t'); break;
939 case '\\': util_sb_append_c(&sb, '\\'); break;
940 case '"': util_sb_append_c(&sb, '"'); break;
941 default: util_sb_append_c(&sb, esc); break;
942 }
943 }
944
945done:
946 value = js_mkstr(js, sb.buf ? sb.buf : "", sb.len);
947 free(sb.buf);
948
949 while (*cursor < len && src[*cursor] != '\n') {
950 if (src[*cursor] == '#') break;
951 (*cursor)++;
952 }
953
954 return value;
955}
956
957static ant_value_t util_env_parse_unquoted_value(
958 ant_t *js, const char *src,
959 size_t len, size_t *cursor
960) {
961 size_t value_start = *cursor;
962 size_t value_end = *cursor;
963 bool saw_space = false;
964
965 while (*cursor < len && src[*cursor] != '\n' && src[*cursor] != '\r') {
966 if (src[*cursor] == '#') {
967 if (*cursor == value_start || saw_space) goto done;
968 }
969
970 saw_space = util_env_is_inline_ws(src[*cursor]);
971 (*cursor)++;
972 value_end = *cursor;
973 }
974
975done:
976 while (value_start < value_end && util_env_is_inline_ws(src[value_start])) {
977 value_start++;
978 }
979 while (value_end > value_start && util_env_is_inline_ws(src[value_end - 1])) {
980 value_end--;
981 }
982
983 return js_mkstr(js, src + value_start, value_end - value_start);
984}
985
986static ant_value_t util_parse_env(ant_t *js, ant_value_t *args, int nargs) {
987 ant_value_t out = js_mkobj(js);
988 if (nargs < 1) return out;
989
990 char cbuf[512];
991 js_cstr_t cstr = js_to_cstr(js, args[0], cbuf, sizeof(cbuf));
992 const char *src = cstr.ptr;
993 size_t len = strlen(src);
994 size_t i = 0;
995
996 if (len >= 3 &&
997 (unsigned char)src[0] == 0xEF &&
998 (unsigned char)src[1] == 0xBB &&
999 (unsigned char)src[2] == 0xBF) {
1000 i = 3;
1001 }
1002
1003 while (i < len) {
1004 size_t key_start = 0;
1005 size_t key_end = 0;
1006 ant_value_t value = js_mkstr(js, "", 0);
1007
1008 util_env_skip_inline_ws(src, len, &i);
1009 if (i >= len) break;
1010 if (src[i] == '\n') {
1011 i++;
1012 continue;
1013 }
1014 if (src[i] == '#') goto skip_line;
1015
1016 util_env_consume_export(src, len, &i);
1017 if (!util_env_parse_key(src, len, &i, &key_start, &key_end)) goto skip_line;
1018
1019 util_env_skip_inline_ws(src, len, &i);
1020 if (i >= len || src[i] != '=') goto skip_line;
1021 i++;
1022 util_env_skip_inline_ws(src, len, &i);
1023
1024 if (i < len && (src[i] == '"' || src[i] == '\'' || src[i] == '`')) {
1025 value = util_env_parse_quoted_value(js, src, len, &i);
1026 } else {
1027 value = util_env_parse_unquoted_value(js, src, len, &i);
1028 }
1029
1030 util_env_set_entry(js, out, src + key_start, key_end - key_start, value);
1031
1032skip_line:
1033 util_env_skip_line(src, len, &i);
1034 }
1035
1036 if (cstr.needs_free) free((void *)cstr.ptr);
1037 return out;
1038}
1039
1040static const util_style_entry_t *util_find_style(const char *name) {
1041 for (size_t i = 0; i < sizeof(util_styles) / sizeof(util_styles[0]); i++) {
1042 if (strcmp(name, util_styles[i].name) == 0) return &util_styles[i];
1043 }
1044 return NULL;
1045}
1046
1047static ant_value_t util_style_text(ant_t *js, ant_value_t *args, int nargs) {
1048 if (nargs < 2) return js_mkstr(js, "", 0);
1049
1050 char text_buf[512];
1051 js_cstr_t text_cstr = js_to_cstr(js, args[1], text_buf, sizeof(text_buf));
1052
1053 const util_style_entry_t *picked[16];
1054 int picked_n = 0;
1055
1056 if (vtype(args[0]) == T_STR) {
1057 const char *name = js_getstr(js, args[0], NULL);
1058 const util_style_entry_t *e = name ? util_find_style(name) : NULL;
1059 if (e) picked[picked_n++] = e;
1060 } else if (vtype(args[0]) == T_ARR) {
1061 ant_offset_t len = js_arr_len(js, args[0]);
1062 for (ant_offset_t i = 0; i < len && picked_n < (int)(sizeof(picked) / sizeof(picked[0])); i++) {
1063 ant_value_t item = js_arr_get(js, args[0], i);
1064 if (vtype(item) != T_STR) continue;
1065 const char *name = js_getstr(js, item, NULL);
1066 const util_style_entry_t *e = name ? util_find_style(name) : NULL;
1067 if (e) picked[picked_n++] = e;
1068 }
1069 }
1070
1071 if (picked_n == 0) {
1072 ant_value_t out = js_mkstr(js, text_cstr.ptr, strlen(text_cstr.ptr));
1073 if (text_cstr.needs_free) free((void *)text_cstr.ptr);
1074 return out;
1075 }
1076
1077 util_sb_t sb = {0};
1078 for (int i = 0; i < picked_n; i++) {
1079 util_sb_append_n(&sb, picked[i]->open, strlen(picked[i]->open));
1080 }
1081 util_sb_append_n(&sb, text_cstr.ptr, strlen(text_cstr.ptr));
1082 for (int i = picked_n - 1; i >= 0; i--) {
1083 util_sb_append_n(&sb, picked[i]->close, strlen(picked[i]->close));
1084 }
1085
1086 ant_value_t out = js_mkstr(js, sb.buf ? sb.buf : "", sb.len);
1087 if (text_cstr.needs_free) free((void *)text_cstr.ptr);
1088 free(sb.buf);
1089 return out;
1090}
1091
1092static ant_value_t util_promisify_callback(ant_t *js, ant_value_t *args, int nargs) {
1093 ant_value_t fn = js_getcurrentfunc(js);
1094 ant_value_t ctx = js_get_slot(fn, SLOT_DATA);
1095 if (!is_object_type(ctx)) return js_mkundef();
1096
1097 ant_value_t settled = js_get_slot(ctx, SLOT_SETTLED);
1098 if (vtype(settled) == T_BOOL && settled == js_true) return js_mkundef();
1099 js_set_slot(ctx, SLOT_SETTLED, js_true);
1100
1101 ant_value_t promise = js_get_slot(ctx, SLOT_DATA);
1102 if (!is_object_type(promise)) return js_mkundef();
1103
1104 if (nargs > 0 && !is_null(args[0]) && !is_undefined(args[0])) {
1105 js_reject_promise(js, promise, args[0]);
1106 return js_mkundef();
1107 }
1108
1109 if (nargs <= 1) {
1110 js_resolve_promise(js, promise, js_mkundef());
1111 return js_mkundef();
1112 }
1113
1114 if (nargs == 2) {
1115 js_resolve_promise(js, promise, args[1]);
1116 return js_mkundef();
1117 }
1118
1119 ant_value_t arr = js_mkarr(js);
1120 for (int i = 1; i < nargs; i++) js_arr_push(js, arr, args[i]);
1121 js_resolve_promise(js, promise, arr);
1122 return js_mkundef();
1123}
1124
1125static ant_value_t util_promisified_call(ant_t *js, ant_value_t *args, int nargs) {
1126 ant_value_t fn = js_getcurrentfunc(js);
1127 ant_value_t original = js_get_slot(fn, SLOT_DATA);
1128 if (!is_callable(original)) return js_mkerr(js, "promisified target is not callable");
1129
1130 ant_value_t promise = js_mkpromise(js);
1131 ant_value_t ctx = js_mkobj(js);
1132 js_set_slot(ctx, SLOT_DATA, promise);
1133 js_set_slot(ctx, SLOT_SETTLED, js_false);
1134 ant_value_t cb = js_heavy_mkfun(js, util_promisify_callback, ctx);
1135
1136 ant_value_t *call_args = (ant_value_t *)malloc((size_t)(nargs + 1) * sizeof(ant_value_t));
1137 if (!call_args) {
1138 js_reject_promise(js, promise, js_mkerr(js, "Out of memory"));
1139 return promise;
1140 }
1141 for (int i = 0; i < nargs; i++) call_args[i] = args[i];
1142 call_args[nargs] = cb;
1143
1144 ant_value_t this_arg = js_getthis(js);
1145 ant_value_t call_result = sv_vm_call(
1146 js->vm, js, original, this_arg, call_args, nargs + 1, NULL, false
1147 );
1148 free(call_args);
1149
1150 ant_value_t settled = js_get_slot(ctx, SLOT_SETTLED);
1151 bool is_settled = (vtype(settled) == T_BOOL && settled == js_true);
1152 if (!is_settled && (is_err(call_result) || js->thrown_exists)) {
1153 ant_value_t ex = js->thrown_exists ? js->thrown_value : call_result;
1154 js->thrown_exists = false;
1155 js->thrown_value = js_mkundef();
1156 js->thrown_stack = js_mkundef();
1157 js_set_slot(ctx, SLOT_SETTLED, js_true);
1158 js_reject_promise(js, promise, ex);
1159 }
1160
1161 return promise;
1162}
1163
1164static ant_value_t util_callbackify_success(ant_t *js, ant_value_t *args, int nargs) {
1165 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
1166 if (!is_object_type(state)) return js_mkundef();
1167
1168 ant_value_t callback = js_get_slot(state, SLOT_DATA);
1169 if (!is_callable(callback)) return js_mkundef();
1170
1171 ant_value_t cb_args[2] = {
1172 js_mknull(),
1173 nargs > 0 ? args[0] : js_mkundef()
1174 };
1175
1176 return sv_vm_call(js->vm, js, callback, js_mkundef(), cb_args, 2, NULL, false);
1177}
1178
1179static ant_value_t util_callbackify_error(ant_t *js, ant_value_t *args, int nargs) {
1180 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
1181 if (!is_object_type(state)) return js_mkundef();
1182
1183 ant_value_t callback = js_get_slot(state, SLOT_DATA);
1184 if (!is_callable(callback)) return js_mkundef();
1185
1186 ant_value_t err = nargs > 0 ? args[0] : js_mkerr(js, "Promise was rejected");
1187 ant_value_t cb_args[1] = { err };
1188 return sv_vm_call(js->vm, js, callback, js_mkundef(), cb_args, 1, NULL, false);
1189}
1190
1191static ant_value_t util_callbackified_call(ant_t *js, ant_value_t *args, int nargs) {
1192 ant_value_t fn = js_getcurrentfunc(js);
1193 ant_value_t original = js_get_slot(fn, SLOT_DATA);
1194
1195 if (!is_callable(original)) return js_mkerr(js, "callbackified target is not callable");
1196 if (nargs < 1 || !is_callable(args[nargs - 1]))
1197 return js_mkerr_typed(js, JS_ERR_TYPE, "callbackified function requires a callback");
1198
1199 ant_value_t callback = args[nargs - 1];
1200 int call_nargs = nargs - 1;
1201 ant_value_t *call_args = NULL;
1202
1203 if (call_nargs > 0) {
1204 call_args = (ant_value_t *)malloc((size_t)call_nargs * sizeof(ant_value_t));
1205 if (!call_args) return js_mkerr(js, "Out of memory");
1206 for (int i = 0; i < call_nargs; i++) call_args[i] = args[i];
1207 }
1208
1209 ant_value_t result = sv_vm_call(js->vm, js, original, js_getthis(js), call_args, call_nargs, NULL, false);
1210 free(call_args);
1211
1212 if (is_err(result) || js->thrown_exists) {
1213 ant_value_t ex = js->thrown_exists ? js->thrown_value : result;
1214 js->thrown_exists = false;
1215 js->thrown_value = js_mkundef();
1216 js->thrown_stack = js_mkundef();
1217 ant_value_t cb_args[1] = { ex };
1218 sv_vm_call(js->vm, js, callback, js_mkundef(), cb_args, 1, NULL, false);
1219 return js_mkundef();
1220 }
1221
1222 if (vtype(result) != T_PROMISE) {
1223 ant_value_t cb_args[2] = { js_mknull(), result };
1224 sv_vm_call(js->vm, js, callback, js_mkundef(), cb_args, 2, NULL, false);
1225 return js_mkundef();
1226 }
1227
1228 ant_value_t state = js_mkobj(js);
1229 js_set_slot(state, SLOT_DATA, callback);
1230 ant_value_t success = js_heavy_mkfun(js, util_callbackify_success, state);
1231 ant_value_t error = js_heavy_mkfun(js, util_callbackify_error, state);
1232 js_promise_then(js, result, success, error);
1233
1234 return js_mkundef();
1235}
1236
1237static ant_value_t util_deprecated_call(ant_t *js, ant_value_t *args, int nargs) {
1238 ant_value_t fn = js_getcurrentfunc(js);
1239 ant_value_t ctx = js_get_slot(fn, SLOT_DATA);
1240 if (!is_object_type(ctx)) return js_mkundef();
1241
1242 ant_value_t warned = js_get_slot(ctx, SLOT_SETTLED);
1243 if (!(vtype(warned) == T_BOOL && warned == js_true)) {
1244 js_set_slot(ctx, SLOT_SETTLED, js_true);
1245 ant_value_t msg_val = js_get(js, ctx, "msg");
1246 const char *msg = js_getstr(js, msg_val, NULL);
1247 if (msg) fprintf(stderr, "DeprecationWarning: %s\n", msg);
1248 }
1249
1250 ant_value_t original = js_get_slot(ctx, SLOT_DATA);
1251 ant_value_t this_arg = js_getthis(js);
1252 return sv_vm_call(js->vm, js, original, this_arg, args, nargs, NULL, false);
1253}
1254
1255static ant_value_t util_deprecate(ant_t *js, ant_value_t *args, int nargs) {
1256 if (nargs < 1 || !is_callable(args[0])) {
1257 return js_mkerr(js, "deprecate(fn, msg) requires a function");
1258 }
1259
1260 ant_value_t ctx = js_mkobj(js);
1261 js_set_slot(ctx, SLOT_DATA, args[0]);
1262 js_set_slot(ctx, SLOT_SETTLED, js_false);
1263 if (nargs >= 2) js_set(js, ctx, "msg", args[1]);
1264
1265 return js_heavy_mkfun(js, util_deprecated_call, ctx);
1266}
1267
1268static ant_value_t util_promisify(ant_t *js, ant_value_t *args, int nargs) {
1269 if (nargs < 1 || !is_callable(args[0])) {
1270 return js_mkerr(js, "promisify(fn) requires a function");
1271 }
1272
1273 ant_value_t custom = js_get_symbol(js, args[0], "nodejs.util.promisify.custom");
1274 if (is_callable(custom)) return custom;
1275
1276 return js_heavy_mkfun(js, util_promisified_call, args[0]);
1277}
1278
1279static ant_value_t util_callbackify(ant_t *js, ant_value_t *args, int nargs) {
1280 if (nargs < 1 || !is_callable(args[0])) {
1281 return js_mkerr(js, "callbackify(fn) requires a function");
1282 }
1283 return js_heavy_mkfun(js, util_callbackified_call, args[0]);
1284}
1285
1286static ant_value_t util_aborted_listener(ant_t *js, ant_value_t *args, int nargs) {
1287 ant_value_t promise = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
1288 if (vtype(promise) == T_PROMISE) js_resolve_promise(js, promise, js_mkundef());
1289 return js_mkundef();
1290}
1291
1292static ant_value_t util_aborted(ant_t *js, ant_value_t *args, int nargs) {
1293 if (nargs < 1 || !abort_signal_is_signal(args[0]))
1294 return js_mkerr_typed(js, JS_ERR_TYPE, "aborted(signal, resource) requires an AbortSignal");
1295
1296 ant_value_t promise = js_mkpromise(js);
1297 if (abort_signal_is_aborted(args[0])) {
1298 js_resolve_promise(js, promise, js_mkundef());
1299 return promise;
1300 }
1301
1302 abort_signal_add_listener(js, args[0], js_heavy_mkfun(js, util_aborted_listener, promise));
1303 return promise;
1304}
1305
1306static ant_value_t util_inherits(ant_t *js, ant_value_t *args, int nargs) {
1307 if (nargs < 2 || !is_callable(args[0]) || !is_callable(args[1])) {
1308 return js_mkerr(js, "inherits(ctor, superCtor) requires constructor functions");
1309 }
1310
1311 ant_value_t ctor = args[0];
1312 ant_value_t super_ctor = args[1];
1313 ant_value_t ctor_proto = js_get(js, ctor, "prototype");
1314 ant_value_t super_proto = js_get(js, super_ctor, "prototype");
1315
1316 if (!is_object_type(ctor_proto) || !is_object_type(super_proto)) {
1317 return js_mkerr(js, "inherits(ctor, superCtor) requires prototype objects");
1318 }
1319
1320 js_set(js, ctor, "super_", super_ctor);
1321 js_set_proto_init(ctor_proto, super_proto);
1322
1323 return js_mkundef();
1324}
1325
1326static ant_value_t util_is_deep_strict_equal(ant_t *js, ant_value_t *args, int nargs) {
1327 if (nargs < 2) return js_bool(true);
1328 return js_bool(js_deep_equal(js, args[0], args[1], true));
1329}
1330
1331ant_value_t util_library(ant_t *js) {
1332 ant_value_t lib = js_mkobj(js);
1333 ant_value_t types = util_get_types_object(js);
1334
1335 ant_value_t promisify = js_heavy_mkfun(js, util_promisify, js_mkundef());
1336 js_set(js, promisify, "custom", js_mksym_for(js, "nodejs.util.promisify.custom"));
1337
1338 js_set(js, lib, "format", js_mkfun(util_format));
1339 js_set(js, lib, "formatWithOptions", js_mkfun(util_format_with_options));
1340 js_set(js, lib, "debuglog", js_mkfun(util_debuglog));
1341 js_set(js, lib, "inspect", js_mkfun(util_inspect));
1342 js_set(js, lib, "deprecate", js_mkfun(util_deprecate));
1343 js_set(js, lib, "inherits", js_mkfun(util_inherits));
1344 js_set(js, lib, "isDeepStrictEqual", js_mkfun(util_is_deep_strict_equal));
1345 js_set(js, lib, "parseArgs", js_mkfun(util_parse_args));
1346 js_set(js, lib, "parseEnv", js_mkfun(util_parse_env));
1347 js_set(js, lib, "promisify", promisify);
1348 js_set(js, lib, "callbackify", js_mkfun(util_callbackify));
1349 js_set(js, lib, "aborted", js_mkfun(util_aborted));
1350 js_set(js, lib, "stripVTControlCharacters", js_mkfun(util_strip_vt_control_characters));
1351 js_set(js, lib, "styleText", js_mkfun(util_style_text));
1352 js_set(js, lib, "types", types);
1353
1354 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "util", 4));
1355 return lib;
1356}