Something went wrong. Try again.
MIRROR: javascript for 🐜's, a tiny runtime with big ambitions ant.themackabu.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550#include "errors.h"#include "cfunc.h"#include "error_render.h"#include "internal.h"#include "descriptors.h"#include "output.h"#include "modules/io.h"#include "gc/roots.h"#include "silver/stack_trace.h"#include "modules/bigint.h"#include "realm.h"
#include <math.h>#include <stdlib.h>#include <string.h>#include <stdarg.h>
void print_error_value(ant_t *js, ant_value_t value, ant_value_t fallback_stack, const char *prefix) { if (is_err(value)) { fallback_stack = Ant_Exception_Stack(js, value); value = Ant_Exception_Value(js, value); }
ant_value_t obj = value; ant_output_stream_t *out = ant_output_stream(stderr); ant_value_t stack = js_mkundef(); bool is_real_error = false, no_stack = false; ant_output_stream_begin(out); if (vtype(obj) == kTypeObject) { ant_value_t err_type = js_get_slot(obj, SLOT_ERR_TYPE); is_real_error = js_get_slot(obj, SLOT_ERROR_BRAND) == js_true || vtype(err_type) == kTypeNumber; no_stack = vtype(err_type) == kTypeNumber && ((int)js_getnum(err_type) & JS_ERR_NO_STACK); if (!no_stack) stack = js_getprop_fallback_len(js, obj, "stack", 5); } if (!no_stack && vtype(stack) != kTypeString) stack = fallback_stack; if (prefix) ant_output_stream_append_cstr(out, prefix); bool plain = !io_should_color_fd(fileno(stderr));
if (is_real_error && vtype(stack) == kTypeString) { io_print_error_stack(js, out, obj, stack, plain); } else if (is_real_error) { io_print_error_header(js, out, obj, plain); io_print_error_props(js, out, obj, plain); } else ant_output_stream_append_cstr(out, js_str(js, value));
ant_output_stream_putc(out, '\n'); ant_output_stream_flush(out);}
bool js_mark_errorlike_no_stack(ant_t *js, ant_value_t value) { if (vtype(value) != kTypeObject) return false; if (js_get_slot(value, SLOT_ERROR_BRAND) == js_true) return false;
const char *name = get_str_prop(js, value, "name", 4, NULL); const char *message = get_str_prop(js, value, "message", 7, NULL); if ((!name || !*name) && (!message || !*message)) return false;
ant_value_t err_type = js_get_slot(value, SLOT_ERR_TYPE); int base_type = vtype(err_type) == kTypeNumber ? (int)js_getnum(err_type) : JS_ERR_GENERIC;
js_set(js, value, "stack", js_mkundef()); js_set_slot(value, SLOT_ERR_TYPE, js_mknum((double)(base_type | JS_ERR_NO_STACK))); return true;}
static_assert( JS_ERR_TYPE_COUNT == sizeof(((ant_t *)0)->builtins.error_protos) / sizeof(ant_value_t), "resize error_protos in isolate_values.h");
const char *js_err_type_name(js_err_type_t err_type) { static const char *names[] = { [JS_ERR_GENERIC] = "Error", [JS_ERR_TYPE] = "TypeError", [JS_ERR_SYNTAX] = "SyntaxError", [JS_ERR_REFERENCE] = "ReferenceError", [JS_ERR_RANGE] = "RangeError", [JS_ERR_EVAL] = "EvalError", [JS_ERR_URI] = "URIError", [JS_ERR_INTERNAL] = "InternalError", [JS_ERR_AGGREGATE] = "AggregateError", };
return names[err_type] ?: "Error";}
static ant_value_t error_header_part(ant_t *js, ant_value_t obj, const char *key, size_t key_len, const char *fallback) { ant_value_t v = js_getprop_fallback_len(js, obj, key, key_len); if (is_err(v)) return v; if (vtype(v) == kTypeUndefined) return js_mkstr(js, fallback, strlen(fallback)); return vtype(v) == kTypeString ? v : js_tostring_val(js, v);}
static bool error_has_node_code(ant_value_t obj) { ant_value_t type = vtype(obj) == kTypeObject ? js_get_slot(obj, SLOT_ERR_TYPE) : js_mkundef(); return vtype(type) == kTypeNumber && ((int)js_getnum(type) & JS_ERR_NODE_CODE);}
static bool error_props_have_node_code(ant_t *js, ant_value_t props) { if (!is_object_type(props)) return false; ant_value_t code = js_get(js, props, "code"); size_t len = 0; const char *text = vtype(code) == kTypeString ? js_getstr(js, code, &len) : NULL; return text && len > 4 && memcmp(text, "ERR_", 4) == 0;}
ant_value_t js_error_display_name(ant_t *js, ant_value_t obj, ant_value_t name) { if (!error_has_node_code(obj) || vtype(name) != kTypeString) return name; ant_value_t code = js_getprop_fallback_len(js, obj, "code", 4); if (is_err(code) || vtype(code) != kTypeString) return is_err(code) ? code : name;
size_t name_len = 0, code_len = 0; const char *name_text = js_getstr(js, name, &name_len); const char *code_text = js_getstr(js, code, &code_len); errbuf_t eb; if (!errbuf_init(&eb, name_len + code_len + 3)) return name; size_t n = errbuf_appendf(&eb, 0, "%.*s [%.*s]", (int)name_len, name_text, (int)code_len, code_text); return errbuf_take_string(js, &eb, n);}
ant_value_t js_error_header_parts(ant_t *js, ant_value_t obj, ant_value_t *name, ant_value_t *message) { GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, obj); *name = error_header_part(js, obj, "name", 4, "Error"); GC_ROOT_PIN(js, *name); *message = is_err(*name) ? *name : error_header_part(js, obj, "message", 7, ""); GC_ROOT_RESTORE(js, root_mark); return is_err(*message) ? *message : js_mkundef();}
ant_value_t js_error_header_text(ant_t *js, ant_value_t obj) { GC_ROOT_SAVE(root_mark, js); ant_value_t name, msg, failed = js_error_header_parts(js, obj, &name, &msg); if (!is_err(failed)) name = js_error_display_name(js, obj, name); if (is_err(name)) failed = name; GC_ROOT_PIN(js, name); GC_ROOT_PIN(js, msg); if (is_err(failed)) { GC_ROOT_RESTORE(js, root_mark); return failed; }
size_t name_len = 0, msg_len = 0; const char *name_text = js_getstr(js, name, &name_len); const char *msg_text = js_getstr(js, msg, &msg_len);
ant_value_t text = name_len == 0 ? msg : name; errbuf_t eb; if (name_len > 0 && msg_len > 0 && errbuf_init(&eb, name_len + msg_len + 3)) { size_t n = errbuf_appendf(&eb, 0, "%.*s: %.*s", (int)name_len, name_text, (int)msg_len, msg_text); text = errbuf_take_string(js, &eb, n); }
GC_ROOT_RESTORE(js, root_mark); return text;}
ant_value_t js_error_stack_value(ant_t *js, ant_value_t obj) { ant_value_t text = js_mkundef(), rec = js_mkundef(); for (int depth = 0; is_object_type(obj) && depth < 64; depth++) { text = js_get_slot(obj, SLOT_ERROR_STACK_TEXT); rec = js_get_slot(obj, SLOT_ERROR_STACK); if (js_is_error_record(rec) || vtype(text) != kTypeUndefined) break; obj = js_get_proto(js, obj); }
if (!js_is_error_record(rec) || vtype(text) != kTypeUndefined) return text;
GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, obj); GC_ROOT_PIN(js, rec); js_set_slot(obj, SLOT_ERROR_STACK_TEXT, js_mkstr(js, "", 0)); ant_value_t stack = js_error_header_text(js, obj); if (!is_err(stack)) stack = js_error_record_stack_text(js, stack, rec); js_set_slot_wb(js, obj, SLOT_ERROR_STACK_TEXT, is_err(stack) ? js_mkundef() : stack); GC_ROOT_RESTORE(js, root_mark); return stack;}
static ant_value_t builtin_error_stack_get(ant_params_t) { return js_error_stack_value(js, js_getthis(js));}
static ant_value_t builtin_error_stack_set(ant_params_t) { ant_value_t obj = js_getthis(js); if (!is_object_type(obj)) return js_mkundef(); js_set_slot(obj, SLOT_ERROR_STACK, js_mkundef()); js_set_slot_wb(js, obj, SLOT_ERROR_STACK_TEXT, nargs > 0 ? args[0] : js_mkundef()); return js_mkundef();}
void js_error_define_stack(ant_t *js, ant_value_t obj, ant_value_t record, ant_value_t text) { js_reserve_slots(obj, 3); js_set_slot_wb(js, obj, SLOT_ERROR_STACK, record); if ( vtype(text) != kTypeUndefined || vtype(js_get_slot(obj, SLOT_ERROR_STACK_TEXT)) != kTypeUndefined ) js_set_slot_wb(js, obj, SLOT_ERROR_STACK_TEXT, text); js_define_accessor_desc( js, js_as_obj(obj), "stack", 5, Ant_Function_CreateWithMeta(builtin_error_stack_get, "get stack", 0, 0), Ant_Function_CreateWithMeta(builtin_error_stack_set, "set stack", 1, 0), JS_DESC_C );}
static bool error_has_captured_stack(ant_value_t obj) { return js_is_error_record(js_get_slot(obj, SLOT_ERROR_STACK)) || vtype(js_get_slot(obj, SLOT_ERROR_STACK_TEXT)) != kTypeUndefined;}
static void error_capture_stack_at(ant_t *js, ant_value_t err_obj, const js_error_site_t *site) { GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, err_obj); js_error_define_stack(js, err_obj, js_error_record_capture(js, site), js_mkundef()); GC_ROOT_RESTORE(js, root_mark);}
void js_capture_stack(ant_t *js, ant_value_t err_obj) { error_capture_stack_at(js, err_obj, NULL);}
static ant_value_t make_error_value( ant_t *js, const js_error_site_t *site, js_err_type_t err_type, ant_value_t props, const char *error_msg) { bool no_stack = (err_type & JS_ERR_NO_STACK) != 0; js_err_type_t base_type = (js_err_type_t)(err_type & ~JS_ERR_NO_STACK);
const char *err_name = js_err_type_name(base_type); size_t err_name_len = strlen(err_name); size_t msg_len = strlen(error_msg);
GC_ROOT_SAVE(mark, js); GC_ROOT_PIN(js, props);
ant_value_t err_obj = Ant_Object_CreateBare(js); GC_ROOT_PIN(js, err_obj);
if (is_err(err_obj)) { GC_ROOT_RESTORE(js, mark); return err_obj; }
ant_value_t message = js_mkstr(js, error_msg, msg_len); GC_ROOT_PIN(js, message); mkprop_interned(js, err_obj, js->intern.message, message, ANT_PROP_ATTR_WRITABLE | ANT_PROP_ATTR_CONFIGURABLE);
int type_bits = err_type; int props_type = vtype(props); if ((T_FLAG_FIND(props_type) & T_SPECIAL_OBJECT_MASK) != 0) { js_merge_obj(js, err_obj, props); if (error_props_have_node_code(js, props)) type_bits |= JS_ERR_NODE_CODE; } js_set_slot(err_obj, SLOT_ERR_TYPE, js_mknum((double)type_bits)); js_set_slot(err_obj, SLOT_ERROR_BRAND, js_true); ant_value_t proto = js->error_realm ? js->error_realm->builtins.error_protos[base_type] : js->builtins.error_protos[base_type]; int proto_type = vtype(proto); if ((T_FLAG_FIND(proto_type) & T_SPECIAL_OBJECT_MASK) != 0) js_set_proto_init(err_obj, proto); else { js_set(js, err_obj, "name", js_mkstr(js, err_name, err_name_len)); js_set_descriptor(js, err_obj, "name", 4, JS_DESC_W | JS_DESC_C); }
if (!no_stack) error_capture_stack_at(js, err_obj, site); GC_ROOT_RESTORE(js, mark);
return err_obj;}
__attribute__((format(printf, 5, 0)))static ant_value_t CreateFormattedErrorValue( ant_t *js, const js_error_site_t *site, js_err_type_t err_type, ant_value_t props, const char *fmt, va_list args) { char local[256]; char *message = local;
va_list copy;
va_copy(copy, args); int length = vsnprintf(local, sizeof(local), fmt, copy); va_end(copy);
if (length < 0) goto format_failed;
if ((size_t)length >= sizeof(local)) { size_t capacity = (size_t)length + 1; message = malloc(capacity);
if (!message) { ant_value_t failure = is_err(js->exception_oom) ? js->exception_oom : mkval(kTypeError, 0); Ant_Exception_Set(js, failure); return failure; }
va_copy(copy, args); int written = vsnprintf(message, capacity, fmt, copy); va_end(copy);
if (written < 0 || (size_t)written >= capacity) { free(message); goto format_failed; } }
ant_value_t value = make_error_value(js, site, err_type, props, message); if (message != local) free(message); return value;
format_failed: return js_throw(js, Ant_Error_Create(js, JS_ERR_INTERNAL | JS_ERR_NO_STACK, "failed to format error message"));}
static ant_value_t raise_created_error(ant_t *js, ant_value_t value) { if (is_err(value)) return value; return Ant_Exception_Raise(js, value, js_mkundef());}
__attribute__((format(printf, 4, 5)))ant_value_t js_create_error(ant_t *js, js_err_type_t err_type, ant_value_t props, const char *fmt, ...) { va_list ap; va_start(ap, fmt); ant_value_t value = CreateFormattedErrorValue(js, NULL, err_type, props, fmt, ap); va_end(ap); return raise_created_error(js, value);}
__attribute__((format(printf, 5, 6)))ant_value_t js_create_error_at( ant_t *js, const js_error_site_t *site, js_err_type_t err_type, ant_value_t props, const char *fmt, ...) { va_list ap; va_start(ap, fmt); ant_value_t value = CreateFormattedErrorValue(js, site, err_type, props, fmt, ap); va_end(ap); return raise_created_error(js, value);}
ant_value_t Ant_Error_Create(ant_t *js, js_err_type_t err_type, const char *message) { return make_error_value(js, NULL, err_type, js_mkundef(), message);}
ant_value_t Ant_Error_CreateWithProps(ant_t *js, js_err_type_t err_type, ant_value_t props, const char *message) { return make_error_value(js, NULL, err_type, props, message);}
__attribute__((format(printf, 3, 4)))ant_value_t Ant_Error_CreateFormatted(ant_t *js, js_err_type_t err_type, const char *fmt, ...) { va_list ap; va_start(ap, fmt); ant_value_t value = CreateFormattedErrorValue(js, NULL, err_type, js_mkundef(), fmt, ap); va_end(ap); return value;}
ant_value_t js_throw(ant_t *js, ant_value_t value) { if (is_err(value)) { Ant_Exception_Set(js, value); return value; }
GC_ROOT_SAVE(mark, js); GC_ROOT_PIN(js, value);
ant_value_t stack = js_mkundef(); GC_ROOT_PIN(js, stack);
bool no_stack = false; if (vtype(value) == kTypeObject) { ant_value_t kind = js_get_slot(value, SLOT_ERR_TYPE); if (error_has_captured_stack(value)) { no_stack = true; stack = js_get_slot(value, SLOT_ERROR_STACK_TEXT); } else if (vtype(kind) == kTypeNumber && ((int)js_getnum(kind) & JS_ERR_NO_STACK)) no_stack = true; else no_stack = js_try_get_own_data_prop(js, value, "stack", 5, &stack) && vtype(stack) == kTypeString; if (vtype(stack) != kTypeString) stack = js_mkundef(); }
if (!no_stack) stack = js_error_record_capture(js, NULL); ant_value_t result = Ant_Exception_Raise(js, value, stack);
GC_ROOT_RESTORE(js, mark); return result;}
__attribute__((noinline, cold)) ant_value_t js_mkerr_code(ant_t *js, js_err_type_t type, const char *code, const char *message) { ant_value_t props = Ant_Object_CreateBare(js); js_set(js, props, "code", js_mkstr(js, code, strlen(code))); return js_mkerr_props(js, type, props, "%s", message);}
void js_describe_received(ant_t *js, ant_value_t value, char *out, size_t out_len) { ant_value_type_t type = vtype(value);
if (type == kTypeUndefined || type == kTypeNull) { snprintf(out, out_len, "Received %s", type == kTypeNull ? "null" : "undefined"); return; }
if (is_object_type(value)) { bool callable = is_callable(value); ant_value_t named = callable ? value : js_getprop_fallback(js, value, "constructor"); ant_value_t name = is_err(named) || !is_object_type(named) ? js_mkundef() : js_getprop_fallback(js, named, "name"); if (is_err(named) || is_err(name)) Ant_Exception_Clear(js);
size_t name_len = 0; const char *name_str = vtype(name) == kTypeString ? js_getstr(js, name, &name_len) : NULL; if (callable) snprintf(out, out_len, "Received function %.*s", (int)name_len, name_str ? name_str : ""); else if (name_len) snprintf(out, out_len, "Received an instance of %.*s", (int)name_len, name_str); else snprintf(out, out_len, "Received type object"); return; }
char buf[64]; js_cstr_t received = js_to_cstr(js, value, buf, sizeof(buf)); const char *quote = type == kTypeString ? "'" : ""; snprintf( out, out_len, "Received type %s (%s%.25s%s%s)", typestr(type), quote, received.ptr, received.len > 28 ? "..." : "", quote ); if (received.needs_free) free((void *)received.ptr);}
static const char *js_arg_kind(const char *name) { return strchr(name, '.') ? "property" : "argument";}
__attribute__((noinline, cold)) ant_value_t Ant_Error_CreateInvalidArgType( ant_t *js, const char *name, const char *expected, ant_value_t received) { char got[160], message[320]; js_describe_received(js, received, got, sizeof(got)); snprintf(message, sizeof(message), "The \"%s\" %s must be %s. %s", name, js_arg_kind(name), expected, got); return js_mkerr_code(js, JS_ERR_TYPE, "ERR_INVALID_ARG_TYPE", message);}
__attribute__((noinline, cold)) ant_value_t Ant_Error_CreateInvalidArgValue( ant_t *js, const char *name, const char *expected, ant_value_t received) { char inspected[160], message[384]; js_cstr_t text = js_inspect_cstr(js, received, inspected, sizeof(inspected));
if (expected) snprintf( message, sizeof(message), "The %s '%s' must be one of: %s. Received %s", js_arg_kind(name), name, expected, text.ptr );
else snprintf(message, sizeof(message), "The %s '%s' is invalid. Received %s", js_arg_kind(name), name, text.ptr); if (text.needs_free) free((void *)text.ptr);
return js_mkerr_code(js, JS_ERR_TYPE, "ERR_INVALID_ARG_VALUE", message);}
ant_value_t Ant_Error_CheckNoNullBytes(ant_t *js, const char *name, ant_value_t value) { size_t len = 0; const char *str = vtype(value) == kTypeString ? js_getstr(js, value, &len) : NULL; if (!str || !memchr(str, '\0', len)) return js_mkundef();
char inspected[160], message[384]; js_cstr_t text = js_inspect_cstr(js, value, inspected, sizeof(inspected)); snprintf( message, sizeof(message), "The %s '%s' must be a string without null bytes. Received %s", js_arg_kind(name), name, text.ptr ); if (text.needs_free) free((void *)text.ptr); return js_mkerr_code(js, JS_ERR_TYPE, "ERR_INVALID_ARG_VALUE", message);}
static void js_group_digits(const char *digits, char *out, size_t len) { size_t n = strlen(digits), start = digits[0] == '-' ? 1 : 0, head = n; while (head >= start + 4) head -= 3; size_t w = 0; for (size_t i = 0; i < n && w + 1 < len; i++) { if (i >= head && i > start && (i - head) % 3 == 0) out[w++] = '_'; if (w + 1 < len) out[w++] = digits[i]; } out[w] = '\0';}
static void js_describe_range_value(ant_t *js, ant_value_t value, char *out, size_t len) { char raw[96]; if (vtype(value) == kTypeBigInt) { size_t n = strbigint(js, value, raw, sizeof(raw) - 1); raw[n < sizeof(raw) - 1 ? n : sizeof(raw) - 2] = '\0'; const char *digits = raw[0] == '-' ? raw + 1 : raw; bool large = strlen(digits) > 10 || (strlen(digits) == 10 && strcmp(digits, "4294967296") > 0); if (large) js_group_digits(raw, out, len - 1); else snprintf(out, len - 1, "%s", raw); strncat(out, "n", len - strlen(out) - 1); return; } double d = js_getnum(value); if (d == 0 && signbit(d)) { snprintf(out, len, "-0"); return; } size_t n = js_number_to_chars(value, raw, sizeof(raw) - 1); raw[n < sizeof(raw) - 1 ? n : sizeof(raw) - 2] = '\0'; if (isfinite(d) && d == trunc(d) && fabs(d) > 4294967296.0) js_group_digits(raw, out, len); else snprintf(out, len, "%s", raw);}
__attribute__((noinline, cold)) ant_value_t Ant_Error_CreateStackOverflow(ant_t *js) { return js_mkerr_typed(js, JS_ERR_RANGE | JS_ERR_NO_STACK, "Maximum call stack size exceeded");}
__attribute__((noinline, cold)) ant_value_t Ant_Error_CreateOutOfRange( ant_t *js, const char *name, const char *range, ant_value_t received) { char got[128], message[384]; js_describe_range_value(js, received, got, sizeof(got)); snprintf(message, sizeof(message), "The value of \"%s\" is out of range. It must be %s. Received %s", name, range, got); return js_mkerr_code(js, JS_ERR_RANGE, "ERR_OUT_OF_RANGE", message);}