Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925#include "builder.h"
#include "internal.h"#include "silver/engine.h"#include "errors.h"#include "gc/roots.h"#include "modules/collections.h"
#include <math.h>#include <stdarg.h>#include <stdio.h>#include <stdlib.h>#include <string.h>
static bool is_valid_identifier(const char *str, ant_offset_t slen) { if (slen == 0) return false; char c = str[0]; if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$')) return false; for (ant_offset_t i = 1; i < slen; i++) { c = str[i]; if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '$')) return false; } return true;}
static size_t strkey_interned(ant_t *js, const char *key, size_t klen, char *buf, size_t len) { if (is_valid_identifier(key, (ant_offset_t)klen)) { return cpy(buf, len, key, klen); } ant_value_t key_str = js_mkstr(js, key, klen); return strstring(js, key_str, buf, len);}
static inline bool js_inspect_has_flag(ant_t *js, ant_inspect_flags_t flag) { return js->stringify && (js->stringify->flags & flag);}
static bool js_inspect_showing_hidden(ant_t *js) { return js_inspect_has_flag(js, ANT_INSPECT_SHOW_HIDDEN);}
typedef bool (*js_inspect_hidden_visit_t)( void *ctx, ant_value_t key, ant_value_t desc);
static bool js_inspect_each_hidden(ant_t *js, ant_value_t obj, js_inspect_hidden_visit_t visit, void *ctx) { if (!js_inspect_showing_hidden(js)) return true; if (vtype(obj) == kTypeBuiltin) obj = js_cfunc_promote(js, obj); if (!is_object_type(obj) || is_proxy(js_as_obj(obj))) return true;
GC_ROOT_SAVE(mark, js); GC_ROOT_PIN(js, obj); ant_value_t keys = js_own_property_keys(js, obj, true, false); GC_ROOT_PIN(js, keys); if (is_err(keys)) { Ant_Exception_Clear(js); GC_ROOT_RESTORE(js, mark); return true; }
bool ok = true; ant_offset_t count = js_arr_len(js, keys); for (ant_offset_t i = 0; ok && i < count; i++) { ant_value_t args[2] = { obj, js_arr_get(js, keys, i) }; ant_value_t desc = builtin_object_getOwnPropertyDescriptor(js, args, 2, js_mkundef()); if (is_err(desc)) { Ant_Exception_Clear(js); continue; } if (!is_object_type(desc) || js_truthy(js, js_get(js, desc, "enumerable"))) continue; GC_ROOT_PIN(js, desc); ok = visit(ctx, args[1], desc); }
GC_ROOT_RESTORE(js, mark); return ok;}
typedef struct { ant_t *js; int count; bool has_nested;} js_inspect_hidden_tally_t;
static bool js_inspect_tally_hidden(void *ctx, ant_value_t key, ant_value_t desc) { js_inspect_hidden_tally_t *tally = ctx; (void)key; tally->count++; ant_value_t value = js_get(tally->js, desc, "value"); uint8_t t = vtype(value); if (t == kTypeObject || t == kTypeArray || t == kTypeFunction) tally->has_nested = true; return true;}
int js_inspect_hidden_count(ant_t *js, ant_value_t obj, bool *has_nested) { js_inspect_hidden_tally_t tally = { js, 0, false }; js_inspect_each_hidden(js, obj, js_inspect_tally_hidden, &tally); if (has_nested) *has_nested = tally.has_nested; return tally.count;}
static bool is_small_object(ant_t *js, ant_value_t obj, int *prop_count) { int count = 0; bool has_nested = false;
ant_value_t as_obj = js_as_obj(obj); ant_object_t *ptr = js_obj_ptr(as_obj); uintptr_t obj_off = (uintptr_t)vdata(as_obj); if (ptr && ptr->shape) { uint32_t shape_count = ant_shape_count(ptr->shape); for (uint32_t i = 0; i < shape_count; i++) { const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i); if (!prop) continue; if ((ant_shape_get_attrs(ptr->shape, i) & ANT_PROP_ATTR_ENUMERABLE) == 0) continue;
if (prop->type == ANT_SHAPE_KEY_SYMBOL) { count++; continue; }
ant_value_t val = (i < ptr->prop_count) ? ant_object_prop_get_unchecked(ptr, i) : js_mkundef(); uint8_t t = vtype(val); if (t == kTypeObject || t == kTypeArray || t == kTypeFunction) has_nested = true; count++; } } if (ptr && ptr->flags.is_exotic) { descriptor_entry_t *desc, *tmp; HASH_ITER(hh, js->desc_registry, desc, tmp) { if (desc->obj_off != obj_off) continue; if (!desc->enumerable) continue; if (!desc->has_getter && !desc->has_setter) continue; count++; } } bool hidden_nested = false; count += js_inspect_hidden_count(js, obj, &hidden_nested); has_nested |= hidden_nested;
if (prop_count) *prop_count = count; return count <= 4 && !has_nested && !js_inspect_has_flag(js, ANT_INSPECT_EXPAND);}
void js_inspect_builder_init_fixed(js_inspect_builder_t *builder, ant_t *js, char *buf, size_t len, size_t initial_n) { builder->js = js; builder->buf = buf; builder->len = len; builder->n = initial_n; builder->growable = false; builder->inline_mode = false; builder->bare_mode = false; builder->first = true; builder->closed = false; builder->did_indent = false; builder->error_stack = NULL; builder->error_stack_len = 0;}
bool js_inspect_builder_init_dynamic(js_inspect_builder_t *builder, ant_t *js, size_t initial_cap) { size_t cap = initial_cap ? initial_cap : 128; char *buf = malloc(cap); if (!buf) return false;
buf[0] = '\0'; builder->js = js; builder->buf = buf; builder->len = cap; builder->n = 0; builder->growable = true; builder->inline_mode = false; builder->bare_mode = false; builder->first = true; builder->closed = false; builder->did_indent = false; builder->error_stack = NULL; builder->error_stack_len = 0; return true;}
void js_inspect_builder_dispose(js_inspect_builder_t *builder) { if (builder->growable) free(builder->buf); builder->buf = NULL; builder->len = 0; builder->n = 0;}
ant_value_t js_inspect_builder_result(js_inspect_builder_t *builder) { ant_value_t out = js_mkstr(builder->js, builder->buf ? builder->buf : "", builder->n); js_inspect_builder_dispose(builder); return out;}
static inline char *js_inspect_builder_write_ptr(js_inspect_builder_t *builder, size_t *avail) { return fixed_buf_write_ptr(builder->buf, builder->len, builder->n, avail);}
static bool js_inspect_builder_reserve(js_inspect_builder_t *builder, size_t extra) { if (!builder->growable) return true;
size_t needed = builder->n + extra + 1; if (needed <= builder->len) return true;
size_t new_cap = builder->len ? builder->len : 128; while (new_cap < needed) new_cap *= 2;
char *new_buf = realloc(builder->buf, new_cap); if (!new_buf) return false;
builder->buf = new_buf; builder->len = new_cap; return true;}
static bool js_inspect_append(js_inspect_builder_t *builder, const char *src, size_t srclen) { if (builder->growable) { if (!js_inspect_builder_reserve(builder, srclen)) return false; memcpy(builder->buf + builder->n, src, srclen); builder->n += srclen; builder->buf[builder->n] = '\0'; return true; }
size_t avail = 0; char *dst = js_inspect_builder_write_ptr(builder, &avail); builder->n += cpy(dst, avail, src, srclen); return true;}
static bool __attribute__((format(printf, 2, 0)))js_inspect_vappendf(js_inspect_builder_t *builder, const char *fmt, va_list args) { if (builder->growable) { va_list copy; va_copy(copy, args); int needed = vsnprintf(NULL, 0, fmt, copy); va_end(copy); if (needed < 0) return false; if (!js_inspect_builder_reserve(builder, (size_t)needed)) return false; vsnprintf(builder->buf + builder->n, builder->len - builder->n, fmt, args); builder->n += (size_t)needed; return true; }
size_t avail = 0; char *dst = js_inspect_builder_write_ptr(builder, &avail); int needed = vsnprintf(dst, avail, fmt, args); if (needed < 0) return false; builder->n += (size_t)needed; return true;}
static bool __attribute__((format(printf, 2, 3)))js_inspect_appendf(js_inspect_builder_t *builder, const char *fmt, ...) { va_list args; va_start(args, fmt); bool ok = js_inspect_vappendf(builder, fmt, args); va_end(args); return ok;}
static bool js_inspect_append_indent(js_inspect_builder_t *builder, int indent) { for (int i = 0; i < indent; i++) if (!js_inspect_append(builder, " ", 2)) return false; return true;}
static bool js_inspect_append_tostr(js_inspect_builder_t *builder, ant_value_t value) { if (!builder->growable) { size_t avail = 0; char *dst = js_inspect_builder_write_ptr(builder, &avail); builder->n += tostr(builder->js, value, dst, avail); return true; }
size_t cap = 128; char *tmp = malloc(cap); if (!tmp) return false;
for (;;) { size_t written = tostr(builder->js, value, tmp, cap); if (written < cap) { bool ok = js_inspect_append(builder, tmp, written); free(tmp); return ok; } size_t new_cap = written + 1; char *new_tmp = realloc(tmp, new_cap); if (!new_tmp) { free(tmp); return false; } tmp = new_tmp; cap = new_cap; }}
static bool js_inspect_append_key_interned(js_inspect_builder_t *builder, const char *key, size_t klen) { if (!builder->growable) { size_t avail = 0; char *dst = js_inspect_builder_write_ptr(builder, &avail); builder->n += strkey_interned(builder->js, key, klen, dst, avail); return true; }
size_t cap = klen + 16; char *tmp = malloc(cap); if (!tmp) return false;
for (;;) { size_t written = strkey_interned(builder->js, key, klen, tmp, cap); if (written < cap) { bool ok = js_inspect_append(builder, tmp, written); free(tmp); return ok; } size_t new_cap = written + 1; char *new_tmp = realloc(tmp, new_cap); if (!new_tmp) { free(tmp); return false; } tmp = new_tmp; cap = new_cap; }}
static bool __attribute__((format(printf, 3, 0)))js_inspect_vheader_for(js_inspect_builder_t *builder, ant_value_t obj, const char *fmt, va_list args) { bool ok = js_inspect_vappendf(builder, fmt, args); if (!ok) return false;
if (is_object_type(obj)) { int prop_count = 0; bool inline_mode = is_small_object(builder->js, obj, &prop_count); if (prop_count == 0) { if (!js_inspect_append(builder, " {}", 3)) return false; builder->inline_mode = false; builder->first = true; builder->closed = true; builder->did_indent = false; return true; } if (inline_mode) { if (!js_inspect_append(builder, " { ", 3)) return false; builder->inline_mode = true; builder->first = true; builder->closed = false; builder->did_indent = false; return true; }}
if (!js_inspect_append(builder, " {\n", 3)) return false;
builder->inline_mode = false; builder->first = true; builder->closed = false; builder->did_indent = false;
return true;}
bool js_inspect_header_for(js_inspect_builder_t *builder, ant_value_t obj, const char *fmt, ...) { va_list args; va_start(args, fmt); bool ok = js_inspect_vheader_for(builder, obj, fmt, args); va_end(args); return ok;}
bool js_inspect_header(js_inspect_builder_t *builder, const char *fmt, ...) { va_list args; va_start(args, fmt); bool ok = js_inspect_vheader_for(builder, js_mkundef(), fmt, args); va_end(args); return ok;}
bool js_inspect_tagged_header(js_inspect_builder_t *builder, ant_value_t obj, const char *tag, size_t tag_len) { ant_offset_t class_len = 0; const char *class_name = get_class_name(builder->js, obj, &class_len, NULL); if (class_name && (size_t)class_len == tag_len && memcmp(class_name, tag, tag_len) == 0) return js_inspect_plain_header(builder, obj); int prop_count = 0; bool inline_mode = is_small_object(builder->js, obj, &prop_count);
if (vtype(js_get_proto(builder->js, obj)) == kTypeNull) { class_name = "[Object: null prototype]"; class_len = 24; } else if (!class_name || class_len == 0) { class_name = "Object"; class_len = 6; } if (!js_inspect_append(builder, class_name, (size_t)class_len)) return false; if (!js_inspect_append(builder, " [", 2)) return false; if (!js_inspect_append(builder, tag, tag_len)) return false;
if (prop_count == 0) { if (!js_inspect_append(builder, "] {}", 4)) return false; builder->closed = true; return true; }
if (!js_inspect_append(builder, inline_mode ? "] { " : "] {\n", 4)) return false; builder->inline_mode = inline_mode; builder->first = true; builder->closed = false; builder->did_indent = false; return true;}
// TODO: modularizebool js_inspect_plain_header(js_inspect_builder_t *builder, ant_value_t obj) { ant_t *js = builder->js; int prop_count = 0; bool inline_mode = is_small_object(js, obj, &prop_count);
ant_value_t proto_val = js_get_proto(js, obj); bool is_null_proto = (vtype(proto_val) == kTypeNull); bool proto_is_null_proto = false; const char *class_name = NULL; ant_offset_t class_name_len = 0;
do { if (is_null_proto) break; uint8_t pt = vtype(proto_val); if (pt != kTypeObject && pt != kTypeFunction) break; ant_value_t proto_proto = js_get_proto(js, proto_val); ant_value_t object_proto = js->sym.object_proto; proto_is_null_proto = (vtype(proto_proto) == kTypeNull) && (vdata(proto_val) != vdata(object_proto)); class_name = get_class_name(js, obj, &class_name_len, "Object"); } while (0);
if (prop_count == 0) { if (is_null_proto) { if (!js_inspect_append(builder, "[Object: null prototype] {}", 27)) return false; } else if (class_name && class_name_len > 0) { if (!js_inspect_append(builder, class_name, class_name_len)) return false; if (proto_is_null_proto) { if (!js_inspect_append(builder, " <[Object: null prototype] {}> {}", 33)) return false; } else if (!js_inspect_append(builder, " {}", 3)) return false; } else if (proto_is_null_proto) { if (!js_inspect_append(builder, "<[Object: null prototype] {}> {}", 32)) return false; } else if (!js_inspect_append(builder, "{}", 2)) return false;
builder->closed = true; return true; }
if (is_null_proto) { if (!js_inspect_append(builder, "[Object: null prototype] ", 25)) return false; } else if (class_name && class_name_len > 0) { if (!js_inspect_append(builder, class_name, class_name_len)) return false; if (proto_is_null_proto) { if (!js_inspect_append(builder, " <[Object: null prototype] {}> ", 31)) return false; } else if (!js_inspect_append(builder, " ", 1)) return false; } else if (proto_is_null_proto) { if (!js_inspect_append(builder, "<[Object: null prototype] {}> ", 30)) return false; }
if (!js_inspect_append(builder, inline_mode ? "{ " : "{\n", 2)) return false; builder->inline_mode = inline_mode; builder->first = true; builder->closed = false; builder->did_indent = false; return true;}
static bool js_inspect_property_prefix(js_inspect_builder_t *builder) { if (builder->bare_mode) return js_inspect_append(builder, "\n ", 2); if (!builder->first && !js_inspect_append(builder, builder->inline_mode ? ", " : ",\n", 2)) return false; builder->first = false; return builder->inline_mode || js_inspect_append_indent(builder, builder->js->stringify->indent);}
static void js_inspect_begin_body(js_inspect_builder_t *builder) { if (!builder->bare_mode && !builder->inline_mode && !builder->did_indent) { builder->js->stringify->indent++; builder->did_indent = true; }}
bool js_inspect_field(js_inspect_builder_t *builder, const char *key, ant_value_t value) { if (builder->closed) return true; js_inspect_begin_body(builder); if (!js_inspect_property_prefix(builder)) return false; if (!js_inspect_append(builder, key, strlen(key))) return false; if (!js_inspect_append(builder, ": ", 2)) return false; return js_inspect_append_tostr(builder, value);}
static bool js_inspect_contains(const char *hay, size_t hay_len, const char *needle, size_t needle_len) { if (needle_len == 0) return true; for (size_t i = 0; i + needle_len <= hay_len; i++) if (hay[i] == needle[0] && memcmp(hay + i, needle, needle_len) == 0) return true; return false;}
static bool js_inspect_error_key_in_stack(js_inspect_builder_t *builder, const char *key, size_t klen, ant_value_t val) { if (!builder->error_stack || vtype(val) != kTypeString) return false; if (!streq(key, klen, "name", 4) && !streq(key, klen, "message", 7) && !streq(key, klen, "stack", 5)) return false; size_t len = 0; const char *text = js_getstr(builder->js, val, &len); return text && js_inspect_contains(builder->error_stack, builder->error_stack_len, text, len);}
static bool js_inspect_append_prop_key(js_inspect_builder_t *builder, const ant_shape_prop_t *prop, const char *key, size_t klen) { if (prop->type != ANT_SHAPE_KEY_SYMBOL) return js_inspect_append_key_interned(builder, key, klen); return js_inspect_append_tostr(builder, mkval(kTypeSymbol, prop->key.sym_off));}
static bool js_inspect_emit_hidden(void *ctx, ant_value_t key, ant_value_t desc) { js_inspect_builder_t *builder = ctx; ant_t *js = builder->js; if (!js_inspect_property_prefix(builder)) return false; if (!js_inspect_append(builder, "[", 1)) return false;
if (vtype(key) == kTypeSymbol) { const char *text = js_str(js, key); if (!js_inspect_append(builder, text, strlen(text))) return false; } else { size_t key_len = 0; const char *text = js_getstr(js, key, &key_len); if (!js_inspect_append(builder, text, key_len)) return false; } if (!js_inspect_append(builder, "]: ", 3)) return false;
bool getter = vtype(js_get(js, desc, "get")) != kTypeUndefined; bool setter = vtype(js_get(js, desc, "set")) != kTypeUndefined; if (getter && setter) return js_inspect_append(builder, "[Getter/Setter]", 15); if (getter) return js_inspect_append(builder, "[Getter]", 8); if (setter) return js_inspect_append(builder, "[Setter]", 8); return js_inspect_append_tostr(builder, js_get(js, desc, "value"));}
bool js_inspect_hidden_props(js_inspect_builder_t *builder, ant_value_t obj) { if (!js_inspect_showing_hidden(builder->js)) return true; js_inspect_begin_body(builder); return js_inspect_each_hidden(builder->js, obj, js_inspect_emit_hidden, builder);}
bool js_inspect_hidden_trailer(js_inspect_builder_t *builder, ant_value_t obj) { bool nested = false; if (js_inspect_hidden_count(builder->js, obj, &nested) == 0) return true;
if (!js_inspect_append(builder, nested ? " {\n" : " { ", 3)) return false; builder->inline_mode = !nested; builder->first = true; builder->closed = false; builder->did_indent = false;
return js_inspect_hidden_props(builder, obj) && js_inspect_close(builder);}
static bool js_inspect_shape_entry(js_inspect_builder_t *builder, ant_value_t as_obj, ant_object_t *ptr, uint32_t i) { ant_t *js = builder->js; const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i); if (!prop) return true; if ((ant_shape_get_attrs(ptr->shape, i) & ANT_PROP_ATTR_ENUMERABLE) == 0) return true; ant_value_t val = (i < ptr->prop_count) ? ant_object_prop_get_unchecked(ptr, i) : js_mkundef(); bool is_sym = prop->type == ANT_SHAPE_KEY_SYMBOL; const char *key = is_sym ? NULL : prop->key.interned; ant_offset_t klen = is_sym ? 0 : (ant_offset_t)intern_length(key); if (ptr && ptr->flags.is_exotic) { prop_meta_t meta; bool found = is_sym ? lookup_symbol_prop_meta(js, as_obj, prop->key.sym_off, &meta) : lookup_string_prop_meta(js, as_obj, key, (size_t)klen, &meta); if (found && !meta.enumerable) return true; }
if (prop->has_getter || prop->has_setter) { if (!js_inspect_property_prefix(builder)) return false; if (!js_inspect_append_prop_key(builder, prop, key, (size_t)klen)) return false; if (!js_inspect_append(builder, ": ", 2)) return false; if (prop->has_getter && prop->has_setter) { if (!js_inspect_append(builder, "[Getter/Setter]", 15)) return false; } else if (prop->has_getter) { if (!js_inspect_append(builder, "[Getter]", 8)) return false; } else if (!js_inspect_append(builder, "[Setter]", 8)) return false; return true; }
if (is_sym) { if (!js_inspect_property_prefix(builder)) return false; if (!js_inspect_append_prop_key(builder, prop, key, 0)) return false; if (!js_inspect_append(builder, ": ", 2)) return false; return js_inspect_append_tostr(builder, val); }
if (js_inspect_error_key_in_stack(builder, key, (size_t)klen, val)) return true; if (!js_inspect_property_prefix(builder)) return false;
bool is_special_global = false; if (vtype(val) == kTypeUndefined && streq(key, klen, "undefined", 9)) { is_special_global = true; } else if (vtype(val) == kTypeNumber) { double d = tod(val); if (isinf(d) && d > 0 && streq(key, klen, "Infinity", 8)) { is_special_global = true; } else if (isnan(d) && streq(key, klen, "NaN", 3)) { is_special_global = true; } }
if (!is_special_global) { if (!js_inspect_append_key_interned(builder, key, (size_t)klen)) return false; if (!js_inspect_append(builder, ": ", 2)) return false; } return js_inspect_append_tostr(builder, val);}
typedef struct { const char *key; uint32_t index;} js_inspect_sorted_key_t;
static int js_inspect_sorted_key_cmp(const void *a, const void *b) { const js_inspect_sorted_key_t *x = a, *y = b; if (!x->key != !y->key) return x->key ? -1 : 1; int c = x->key ? strcmp(x->key, y->key) : 0; return c ? c : (x->index > y->index) - (x->index < y->index);}
static bool js_inspect_shape_sorted(js_inspect_builder_t *builder, ant_value_t as_obj, ant_object_t *ptr, uint32_t count) { js_inspect_sorted_key_t stack_keys[32]; js_inspect_sorted_key_t *keys = count <= 32 ? stack_keys : malloc(count * sizeof(*keys)); if (!keys) return false;
for (uint32_t i = 0; i < count; i++) { const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i); bool named = prop && prop->type != ANT_SHAPE_KEY_SYMBOL; keys[i] = (js_inspect_sorted_key_t){ named ? prop->key.interned : NULL, i }; } qsort(keys, count, sizeof(*keys), js_inspect_sorted_key_cmp);
bool ok = true; for (uint32_t i = 0; ok && i < count; i++) ok = js_inspect_shape_entry(builder, as_obj, ptr, keys[i].index); if (keys != stack_keys) free(keys); return ok;}
bool js_inspect_object_body(js_inspect_builder_t *builder, ant_value_t obj) { if (builder->closed) return true; js_inspect_begin_body(builder);
ant_t *js = builder->js; ant_value_t as_obj = js_as_obj(obj); ant_object_t *ptr = js_obj_ptr(as_obj); uintptr_t obj_off = (uintptr_t)vdata(as_obj); uint32_t shape_count = (ptr && ptr->shape) ? ant_shape_count(ptr->shape) : 0;
if (shape_count > 1 && js_inspect_has_flag(js, ANT_INSPECT_SORTED)) { if (!js_inspect_shape_sorted(builder, as_obj, ptr, shape_count)) return false; } else for (uint32_t i = 0; i < shape_count; i++) if (!js_inspect_shape_entry(builder, as_obj, ptr, i)) return false;
if (ptr && ptr->flags.is_exotic) { descriptor_entry_t *desc, *tmp; HASH_ITER(hh, js->desc_registry, desc, tmp) { if (desc->obj_off != obj_off) continue; if (!desc->enumerable) continue; if (!desc->has_getter && !desc->has_setter) continue;
if (!js_inspect_property_prefix(builder)) return false; if (!js_inspect_append(builder, desc->prop_name, desc->prop_len)) return false; if (!js_inspect_append(builder, ": ", 2)) return false;
if (desc->has_getter && desc->has_setter) { if (!js_inspect_append(builder, "[Getter/Setter]", 15)) return false; } else if (desc->has_getter) { if (!js_inspect_append(builder, "[Getter]", 8)) return false; } else if (!js_inspect_append(builder, "[Setter]", 8)) return false; } }
return js_inspect_hidden_props(builder, obj);}
bool js_inspect_trailing_props(js_inspect_builder_t *builder, ant_value_t obj) { int prop_count = 0; bool inline_mode = is_small_object(builder->js, obj, &prop_count); if (prop_count == 0) return true;
if (!js_inspect_append(builder, inline_mode ? " { " : " {\n", 3)) return false; builder->inline_mode = inline_mode; builder->first = true; builder->closed = false; builder->did_indent = false;
return js_inspect_object_body(builder, obj) && js_inspect_close(builder);}
bool js_inspect_close(js_inspect_builder_t *builder) { if (builder->closed) return true;
if (builder->did_indent) { builder->js->stringify->indent--; builder->did_indent = false; }
if (builder->inline_mode) { if (!js_inspect_append(builder, " }", 2)) return false; } else { if (!builder->first && !js_inspect_append(builder, "\n", 1)) return false; if (!js_inspect_append_indent(builder, builder->js->stringify->indent)) return false; if (!js_inspect_append(builder, "}", 1)) return false; }
builder->closed = true; return true;}
static inline bool js_inspect_is_nested(ant_value_t value) { uint8_t t = vtype(value); return t == kTypeObject || t == kTypeArray || t == kTypeFunction;}
static bool js_inspect_collection_header( js_inspect_builder_t *builder, ant_value_t obj, const char *kind, size_t count, bool nested) { ant_offset_t class_len = 0; const char *class_name = get_class_name(builder->js, obj, &class_len, kind); bool ok = class_name ? js_inspect_appendf(builder, "%.*s(%zu) [%s]", (int)class_len, class_name, count, kind) : js_inspect_appendf(builder, "%s(%zu)", kind, count); if (!ok) return false; int prop_count = 0; bool small = is_small_object(builder->js, obj, &prop_count); builder->first = true; builder->did_indent = false; if (count == 0 && prop_count == 0) { builder->inline_mode = false; builder->closed = true; return js_inspect_append(builder, " {}", 3); } builder->inline_mode = small && !nested && count + (size_t)prop_count <= 4; builder->closed = false; return js_inspect_append(builder, builder->inline_mode ? " { " : " {\n", 3);}
static bool js_inspect_more_items(js_inspect_builder_t *builder, size_t count) { if (count <= ANT_INSPECT_MAX_ENTRIES) return true; size_t more = count - ANT_INSPECT_MAX_ENTRIES; return js_inspect_property_prefix(builder) && js_inspect_appendf(builder, "... %zu more item%s", more, more == 1 ? "" : "s");}
bool js_inspect_map(js_inspect_builder_t *builder, ant_value_t obj, map_entry_t **map) { map_entry_t *head = *map; size_t count = HASH_COUNT(head), shown = 0; bool nested = false; map_entry_t *entry, *tmp; HASH_ITER(hh, head, entry, tmp) { if (shown++ == ANT_INSPECT_MAX_ENTRIES) break; if (js_inspect_is_nested(entry->key_val) || js_inspect_is_nested(entry->value)) { nested = true; break; } } if (!js_inspect_collection_header(builder, obj, "Map", count, nested)) return false; if (builder->closed) return true; js_inspect_begin_body(builder); map_iterator_state_t it; collections_map_iter_open(&it, map, ITER_TYPE_MAP_ENTRIES); bool ok = true; for (shown = 0; ok && shown < ANT_INSPECT_MAX_ENTRIES && (entry = map_iter_take(&it)); shown++) ok = js_inspect_property_prefix(builder) && js_inspect_append_tostr(builder, entry->key_val) && js_inspect_append(builder, " => ", 4) && js_inspect_append_tostr(builder, entry->value); collections_map_iter_close(&it); if (!ok) return false; return js_inspect_more_items(builder, count) && js_inspect_object_body(builder, obj) && js_inspect_close(builder);}
bool js_inspect_set(js_inspect_builder_t *builder, ant_value_t obj, set_entry_t **set) { set_entry_t *head = *set; size_t count = HASH_COUNT(head), shown = 0; bool nested = false; set_entry_t *entry, *tmp; HASH_ITER(hh, head, entry, tmp) { if (shown++ == ANT_INSPECT_MAX_ENTRIES) break; if (js_inspect_is_nested(entry->value)) { nested = true; break; } } if (!js_inspect_collection_header(builder, obj, "Set", count, nested)) return false; if (builder->closed) return true; js_inspect_begin_body(builder); set_iterator_state_t it; collections_set_iter_open(&it, set, ITER_TYPE_SET_VALUES); bool ok = true; for (shown = 0; ok && shown < ANT_INSPECT_MAX_ENTRIES && (entry = set_iter_take(&it)); shown++) ok = js_inspect_property_prefix(builder) && js_inspect_append_tostr(builder, entry->value); collections_set_iter_close(&it); if (!ok) return false; return js_inspect_more_items(builder, count) && js_inspect_object_body(builder, obj) && js_inspect_close(builder);}
static ant_value_t js_inspect_error_to_string(ant_t *js, ant_value_t obj) { ant_value_t text = js_error_header_text(js, obj); if (!is_err(text)) return text; js_take_thrown(js, text); return js_mkstr(js, "Error", 5);}
static bool js_inspect_append_error_stack(js_inspect_builder_t *builder, ant_value_t obj, const char *stack, size_t stack_len) { ant_t *js = builder->js; size_t skip = 0;
ant_offset_t class_len = 0; const char *class_name = get_class_name(js, obj, &class_len, NULL); ant_value_t name_val = js_getprop_fallback_len(js, obj, "name", 4); if (is_err(name_val)) name_val = js_take_thrown(js, name_val);
size_t name_len = 0; const char *name = vtype(name_val) == kTypeString ? js_getstr(js, name_val, &name_len) : NULL;
bool improve = class_name && name && name_len >= 5 && memcmp(name + name_len - 5, "Error", 5) == 0 && stack_len >= name_len && memcmp(stack, name, name_len) == 0 && (stack_len == name_len || stack[name_len] == ':' || stack[name_len] == '\n') && !((size_t)class_len == name_len && memcmp(class_name, name, name_len) == 0);
bool bracket = !js_inspect_contains(stack, stack_len, "\n at", 7); if (bracket && !js_inspect_append(builder, "[", 1)) return false;
if (improve) { if (!js_inspect_append(builder, class_name, (size_t)class_len)) return false; if (!js_inspect_contains(class_name, (size_t)class_len, name, name_len)) { if (!js_inspect_append(builder, " [", 2)) return false; if (!js_inspect_append(builder, name, name_len)) return false; if (!js_inspect_append(builder, "]", 1)) return false; } skip = name_len; }
int indent = js->stringify->indent; for (const char *p = stack + skip, *end = stack + stack_len; p < end;) { const char *nl = memchr(p, '\n', (size_t)(end - p)); size_t run = nl ? (size_t)(nl - p) + 1 : (size_t)(end - p); if (!js_inspect_append(builder, p, run)) return false; if (nl && !js_inspect_append_indent(builder, indent)) return false; p += run; }
return !bracket || js_inspect_append(builder, "]", 1);}
static bool js_inspect_hidden_own(ant_t *js, ant_value_t obj, const char *key, size_t klen, ant_value_t *out) { prop_meta_t meta; if (!lookup_string_prop_meta(js, js_as_obj(obj), key, klen, &meta) || meta.enumerable) return false; return js_try_get_own_data_prop(js, obj, key, klen, out);}
bool js_inspect_error(js_inspect_builder_t *builder, ant_value_t obj) { ant_t *js = builder->js; GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, obj);
ant_value_t stack = js_getprop_fallback_len(js, obj, "stack", 5); if (is_err(stack)) stack = js_take_thrown(js, stack); if (vtype(stack) != kTypeString) stack = js_inspect_error_to_string(js, obj); GC_ROOT_PIN(js, stack);
size_t stack_len = 0; const char *stack_text = js_getstr(js, stack, &stack_len); bool ok = stack_text && js_inspect_append_error_stack(builder, obj, stack_text, stack_len);
size_t mark = builder->n; if (ok) ok = js_inspect_append(builder, " {\n", 3); builder->inline_mode = false; builder->first = true; builder->closed = false; builder->did_indent = false; builder->error_stack = stack_text; builder->error_stack_len = stack_len;
if (ok) ok = js_inspect_object_body(builder, obj);
ant_value_t hidden; if (ok && js_inspect_hidden_own(js, obj, "cause", 5, &hidden)) ok = js_inspect_field(builder, "[cause]", hidden); if (ok && js_inspect_hidden_own(js, obj, "errors", 6, &hidden)) ok = js_inspect_field(builder, "[errors]", hidden);
builder->error_stack = NULL; builder->error_stack_len = 0;
if (ok && builder->first) { if (builder->did_indent) js->stringify->indent--; builder->did_indent = false; builder->closed = true; builder->n = mark; if (mark < builder->len) builder->buf[mark] = '\0'; } else if (ok) ok = js_inspect_close(builder);
GC_ROOT_RESTORE(js, root_mark); return ok;}