MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

migrate to dynamic Symbol.inspect

+742 -342
+24
include/internal.h
··· 358 358 ant_value_t setprop_interned(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v); 359 359 ant_value_t js_define_property(ant_t *js, ant_value_t obj, ant_value_t prop, ant_value_t descriptor, bool reflect_mode); 360 360 361 + // TODO: move into builder.c 362 + typedef struct { 363 + ant_t *js; 364 + char *buf; 365 + size_t len; 366 + size_t n; 367 + bool growable; 368 + bool inline_mode; 369 + bool first; 370 + bool closed; 371 + bool did_indent; 372 + } js_inspect_builder_t; 373 + 374 + void js_inspect_builder_init_fixed(js_inspect_builder_t *builder, ant_t *js, char *buf, size_t len, size_t initial_n); 375 + bool js_inspect_builder_init_dynamic(js_inspect_builder_t *builder, ant_t *js, size_t initial_cap); 376 + void js_inspect_builder_dispose(js_inspect_builder_t *builder); 377 + ant_value_t js_inspect_builder_result(js_inspect_builder_t *builder); 378 + 379 + __attribute__((format(printf, 2, 3))) 380 + bool js_inspect_header(js_inspect_builder_t *builder, const char *fmt, ...); 381 + bool js_inspect_tagged_header(js_inspect_builder_t *builder, const char *tag, size_t tag_len); 382 + bool js_inspect_object_body(js_inspect_builder_t *builder, ant_value_t obj); 383 + bool js_inspect_close(js_inspect_builder_t *builder); 384 + 361 385 ant_value_t js_define_own_prop(ant_t *js, ant_value_t obj, const char *key, size_t klen, ant_value_t v); 362 386 ant_value_t js_instance_proto_from_new_target(ant_t *js, ant_value_t fallback_proto); 363 387
+599 -328
src/ant.c
··· 35 35 #include <uv.h> 36 36 #include <assert.h> 37 37 #include <pcre2.h> 38 + #include <stdarg.h> 38 39 #include <stdio.h> 39 40 #include <stdlib.h> 40 41 #include <string.h> ··· 709 710 static int multiref_next_id = 0; 710 711 711 712 static void scan_refs(ant_t *js, ant_value_t value); 713 + static ant_value_t strobj_call_custom_inspect(ant_t *js, ant_value_t obj); 712 714 713 715 static int find_multiref(ant_value_t obj) { 714 716 for (int i = 0; i < multiref_count; i++) { ··· 1125 1127 return count <= 4 && !has_nested; 1126 1128 } 1127 1129 1128 - // todo: split into smaller functions 1129 - static size_t strobj(ant_t *js, ant_value_t obj, char *buf, size_t len) { 1130 - if (is_date_instance(obj)) return strdate(js, obj, buf, len); 1130 + void js_inspect_builder_init_fixed(js_inspect_builder_t *builder, ant_t *js, char *buf, size_t len, size_t initial_n) { 1131 + builder->js = js; 1132 + builder->buf = buf; 1133 + builder->len = len; 1134 + builder->n = initial_n; 1135 + builder->growable = false; 1136 + builder->inline_mode = false; 1137 + builder->first = true; 1138 + builder->closed = false; 1139 + builder->did_indent = false; 1140 + } 1141 + 1142 + bool js_inspect_builder_init_dynamic(js_inspect_builder_t *builder, ant_t *js, size_t initial_cap) { 1143 + size_t cap = initial_cap ? initial_cap : 128; 1144 + char *buf = malloc(cap); 1145 + if (!buf) return false; 1146 + 1147 + buf[0] = '\0'; 1148 + builder->js = js; 1149 + builder->buf = buf; 1150 + builder->len = cap; 1151 + builder->n = 0; 1152 + builder->growable = true; 1153 + builder->inline_mode = false; 1154 + builder->first = true; 1155 + builder->closed = false; 1156 + builder->did_indent = false; 1131 1157 1132 - int ref = get_circular_ref(obj); 1133 - if (ref) return ref > 0 ? (size_t) snprintf(buf, len, "[Circular *%d]", ref) : cpy(buf, len, "[Circular]", 10); 1158 + return true; 1159 + } 1160 + 1161 + void js_inspect_builder_dispose(js_inspect_builder_t *builder) { 1162 + if (builder->growable) free(builder->buf); 1163 + builder->buf = NULL; 1164 + builder->len = 0; 1165 + builder->n = 0; 1166 + } 1167 + 1168 + ant_value_t js_inspect_builder_result(js_inspect_builder_t *builder) { 1169 + ant_value_t out = js_mkstr(builder->js, builder->buf ? builder->buf : "", builder->n); 1170 + js_inspect_builder_dispose(builder); 1171 + return out; 1172 + } 1173 + 1174 + static bool js_inspect_builder_reserve(js_inspect_builder_t *builder, size_t extra) { 1175 + if (!builder->growable) return true; 1176 + 1177 + size_t needed = builder->n + extra + 1; 1178 + if (needed <= builder->len) return true; 1179 + 1180 + size_t new_cap = builder->len ? builder->len : 128; 1181 + while (new_cap < needed) new_cap *= 2; 1182 + 1183 + char *new_buf = realloc(builder->buf, new_cap); 1184 + if (!new_buf) return false; 1185 + 1186 + builder->buf = new_buf; 1187 + builder->len = new_cap; 1134 1188 1135 - push_stringify(obj); 1189 + return true; 1190 + } 1191 + 1192 + static bool js_inspect_append(js_inspect_builder_t *builder, const char *src, size_t srclen) { 1193 + if (builder->growable) { 1194 + if (!js_inspect_builder_reserve(builder, srclen)) return false; 1195 + memcpy(builder->buf + builder->n, src, srclen); 1196 + builder->n += srclen; 1197 + builder->buf[builder->n] = '\0'; 1198 + return true; 1199 + } 1200 + 1201 + builder->n += cpy(builder->buf + builder->n, REMAIN(builder->n, builder->len), src, srclen); 1202 + return true; 1203 + } 1204 + 1205 + static bool __attribute__((format(printf, 2, 0))) 1206 + js_inspect_vappendf(js_inspect_builder_t *builder, const char *fmt, va_list args) { 1207 + if (builder->growable) { 1208 + va_list copy; 1209 + va_copy(copy, args); 1210 + int needed = vsnprintf(NULL, 0, fmt, copy); 1211 + va_end(copy); 1212 + if (needed < 0) return false; 1213 + if (!js_inspect_builder_reserve(builder, (size_t)needed)) return false; 1214 + vsnprintf(builder->buf + builder->n, builder->len - builder->n, fmt, args); 1215 + builder->n += (size_t)needed; 1216 + return true; 1217 + } 1218 + 1219 + int needed = vsnprintf(builder->buf + builder->n, REMAIN(builder->n, builder->len), fmt, args); 1220 + if (needed < 0) return false; 1221 + builder->n += (size_t)needed; 1136 1222 1137 - size_t n = 0; 1138 - int self_ref = get_self_ref(obj); 1139 - if (self_ref) { 1140 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "<ref *%d> ", self_ref); 1223 + return true; 1224 + } 1225 + 1226 + static bool __attribute__((format(printf, 2, 3))) 1227 + js_inspect_appendf(js_inspect_builder_t *builder, const char *fmt, ...) { 1228 + va_list args; 1229 + va_start(args, fmt); 1230 + bool ok = js_inspect_vappendf(builder, fmt, args); 1231 + va_end(args); 1232 + return ok; 1233 + } 1234 + 1235 + static bool js_inspect_append_indent(js_inspect_builder_t *builder, int indent) { 1236 + for (int i = 0; i < indent; i++) if (!js_inspect_append(builder, " ", 2)) return false; 1237 + return true; 1238 + } 1239 + 1240 + static bool js_inspect_append_tostr(js_inspect_builder_t *builder, ant_value_t value) { 1241 + if (!builder->growable) { 1242 + builder->n += tostr(builder->js, value, builder->buf + builder->n, REMAIN(builder->n, builder->len)); 1243 + return true; 1141 1244 } 1142 - 1143 - ant_value_t tag_sym = get_toStringTag_sym(); 1144 - ant_value_t tag_val = (vtype(tag_sym) == T_SYMBOL) ? lkp_sym_proto_val(js, obj, (ant_offset_t)vdata(tag_sym)) : js_mkundef(); 1145 - bool is_map = false, is_set = false, is_arraybuffer = false; 1146 - ant_offset_t tlen = 0, toff = 0; 1147 - const char *tag_str = NULL; 1148 - int prop_count = 0; 1149 - bool inline_mode = false; 1150 - 1151 - if (vtype(tag_val) != T_STR) goto print_plain_object; 1152 - 1153 - toff = vstr(js, tag_val, &tlen); 1154 - tag_str = (const char *)(uintptr_t)(toff); 1155 - is_map = (tlen == 3 && memcmp(tag_str, "Map", 3) == 0); 1156 - is_set = (tlen == 3 && memcmp(tag_str, "Set", 3) == 0); 1157 - is_arraybuffer = (tlen >= 11 && memcmp(tag_str + tlen - 11, "ArrayBuffer", 11) == 0); 1158 - 1159 - TypedArrayData *ta = buffer_get_typedarray_data(obj); 1160 - if (ta && ta->buffer) { 1161 - const char *type_name = NULL; 1162 - size_t type_len = 0; 1163 - 1164 - ant_value_t proto = js_get_proto(js, obj); 1165 - ant_value_t buffer_proto = get_ctor_proto(js, "Buffer", 6); 1166 - if (vtype(proto) == T_OBJ && vtype(buffer_proto) == T_OBJ && vdata(proto) == vdata(buffer_proto)) { 1167 - type_name = "Buffer"; 1168 - type_len = 6; 1169 - } else if (ta->type <= TYPED_ARRAY_BIGUINT64) { 1170 - type_name = buffer_typedarray_type_name(ta->type); 1171 - type_len = strlen(type_name); 1172 - } else { 1173 - type_name = "TypedArray"; 1174 - type_len = 10; 1245 + 1246 + size_t cap = 128; 1247 + char *tmp = malloc(cap); 1248 + if (!tmp) return false; 1249 + 1250 + for (;;) { 1251 + size_t written = tostr(builder->js, value, tmp, cap); 1252 + if (written < cap) { 1253 + bool ok = js_inspect_append(builder, tmp, written); 1254 + free(tmp); 1255 + return ok; 1175 1256 } 1176 1257 1177 - n += cpy(buf + n, REMAIN(n, len), type_name, type_len); 1178 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "(%zu) ", ta->length); 1179 - n += cpy(buf + n, REMAIN(n, len), "[ ", 2); 1180 - 1181 - uint8_t *data = ta->buffer->data + ta->byte_offset; 1182 - 1183 - for (size_t i = 0; i < ta->length && i < 100; i++) { 1184 - if (i > 0) n += cpy(buf + n, REMAIN(n, len), ", ", 2); 1185 - 1186 - switch (ta->type) { 1187 - case TYPED_ARRAY_INT8: 1188 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%d", (int)((int8_t*)data)[i]); 1189 - break; 1190 - case TYPED_ARRAY_UINT8: 1191 - case TYPED_ARRAY_UINT8_CLAMPED: 1192 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", (unsigned)data[i]); 1193 - break; 1194 - case TYPED_ARRAY_INT16: 1195 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%d", (int)((int16_t*)data)[i]); 1196 - break; 1197 - case TYPED_ARRAY_UINT16: 1198 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", (unsigned)((uint16_t*)data)[i]); 1199 - break; 1200 - case TYPED_ARRAY_INT32: 1201 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%d", ((int32_t*)data)[i]); 1202 - break; 1203 - case TYPED_ARRAY_UINT32: 1204 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", ((uint32_t*)data)[i]); 1205 - break; 1206 - case TYPED_ARRAY_FLOAT16: 1207 - n += (size_t) snprintf( 1208 - buf + n, REMAIN(n, len), "%g", half_to_double(((uint16_t*)data)[i]) 1209 - ); 1210 - break; 1211 - case TYPED_ARRAY_FLOAT32: 1212 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%g", (double)((float*)data)[i]); 1213 - break; 1214 - case TYPED_ARRAY_FLOAT64: 1215 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%g", ((double*)data)[i]); 1216 - break; 1217 - case TYPED_ARRAY_BIGINT64: 1218 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%lldn", (long long)((int64_t*)data)[i]); 1219 - break; 1220 - case TYPED_ARRAY_BIGUINT64: 1221 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%llun", (unsigned long long)((uint64_t*)data)[i]); 1222 - break; 1223 - default: 1224 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", (unsigned)data[i]); 1225 - break; 1226 - } 1258 + size_t new_cap = written + 1; 1259 + char *new_tmp = realloc(tmp, new_cap); 1260 + if (!new_tmp) { 1261 + free(tmp); 1262 + return false; 1227 1263 } 1228 - 1229 - if (ta->length > 100) n += cpy(buf + n, REMAIN(n, len), ", ...", 5); 1230 - n += cpy(buf + n, REMAIN(n, len), " ]", 2); 1231 - pop_stringify(); 1232 - return n; 1264 + tmp = new_tmp; 1265 + cap = new_cap; 1233 1266 } 1234 - 1235 - if (is_arraybuffer) { 1236 - ArrayBufferData *ab_data = buffer_get_arraybuffer_data(obj); 1237 - if (ab_data) { 1238 - size_t bytelen = ab_data ? ab_data->length : 0; 1239 - 1240 - n += cpy(buf + n, REMAIN(n, len), tag_str, tlen); 1241 - n += cpy(buf + n, REMAIN(n, len), " {\n", 3); 1242 - n += cpy(buf + n, REMAIN(n, len), " [Uint8Contents]: <", 20); 1243 - 1244 - if (ab_data && ab_data->data && bytelen > 0) { 1245 - for (size_t i = 0; i < bytelen; i++) { 1246 - if (i > 0) n += cpy(buf + n, REMAIN(n, len), " ", 1); 1247 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%02x", ab_data->data[i]); 1248 - } 1249 - } 1250 - 1251 - n += cpy(buf + n, REMAIN(n, len), ">,\n", 3); 1252 - n += cpy(buf + n, REMAIN(n, len), " [byteLength]: ", 16); 1253 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", bytelen); 1254 - n += cpy(buf + n, REMAIN(n, len), "\n}", 2); 1255 - pop_stringify(); 1256 - return n; 1257 - } 1267 + } 1268 + 1269 + static bool js_inspect_append_key_interned(js_inspect_builder_t *builder, const char *key, size_t klen) { 1270 + if (!builder->growable) { 1271 + builder->n += strkey_interned(builder->js, key, klen, builder->buf + builder->n, REMAIN(builder->n, builder->len)); 1272 + return true; 1258 1273 } 1259 - 1260 - bool is_dataview = (tlen == 8 && memcmp(tag_str, "DataView", 8) == 0); 1261 - if (is_dataview) { 1262 - DataViewData *dv = buffer_get_dataview_data(obj); 1263 - if (dv && dv->buffer) { 1264 - n += cpy(buf + n, REMAIN(n, len), "DataView {\n", 11); 1265 - n += cpy(buf + n, REMAIN(n, len), " [byteLength]: ", 16); 1266 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", dv->byte_length); 1267 - n += cpy(buf + n, REMAIN(n, len), ",\n", 2); 1268 - n += cpy(buf + n, REMAIN(n, len), " [byteOffset]: ", 16); 1269 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", dv->byte_offset); 1270 - n += cpy(buf + n, REMAIN(n, len), ",\n", 2); 1271 - n += cpy(buf + n, REMAIN(n, len), " [buffer]: ArrayBuffer {\n", 26); 1272 - n += cpy(buf + n, REMAIN(n, len), " [Uint8Contents]: <", 22); 1273 - 1274 - if (dv->buffer->data && dv->buffer->length > 0) { 1275 - for (size_t i = 0; i < dv->buffer->length; i++) { 1276 - if (i > 0) n += cpy(buf + n, REMAIN(n, len), " ", 1); 1277 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%02x", dv->buffer->data[i]); 1278 - } 1279 - } 1280 - 1281 - n += cpy(buf + n, REMAIN(n, len), ">,\n", 3); 1282 - n += cpy(buf + n, REMAIN(n, len), " [byteLength]: ", 18); 1283 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", dv->buffer->length); 1284 - n += cpy(buf + n, REMAIN(n, len), "\n }\n}", 6); 1285 - pop_stringify(); 1286 - return n; 1274 + 1275 + size_t cap = klen + 16; 1276 + char *tmp = malloc(cap); 1277 + if (!tmp) return false; 1278 + 1279 + for (;;) { 1280 + size_t written = strkey_interned(builder->js, key, klen, tmp, cap); 1281 + if (written < cap) { 1282 + bool ok = js_inspect_append(builder, tmp, written); 1283 + free(tmp); 1284 + return ok; 1287 1285 } 1288 - } 1289 - 1290 - if (is_map) { 1291 - ant_value_t map_val = js_get_slot(obj, SLOT_MAP); 1292 - if (vtype(map_val) == T_UNDEF) goto print_tagged_object; 1293 1286 1294 - map_entry_t **map_ptr = (map_entry_t**)(size_t)tod(map_val); 1295 - n += cpy(buf + n, REMAIN(n, len), "Map(", 4); 1296 - 1297 - unsigned int count = 0; 1298 - if (map_ptr && *map_ptr) count = HASH_COUNT(*map_ptr); 1299 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", count); 1300 - n += cpy(buf + n, REMAIN(n, len), ") ", 2); 1301 - 1302 - if (count == 0) { 1303 - n += cpy(buf + n, REMAIN(n, len), "{}", 2); 1304 - } else { 1305 - n += cpy(buf + n, REMAIN(n, len), "{\n", 2); 1306 - stringify_indent++; 1307 - bool first = true; 1308 - if (map_ptr && *map_ptr) { 1309 - map_entry_t *entry, *tmp; 1310 - HASH_ITER(hh, *map_ptr, entry, tmp) { 1311 - if (!first) n += cpy(buf + n, REMAIN(n, len), ",\n", 2); 1312 - first = false; 1313 - n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1314 - n += tostr(js, entry->key_val, buf + n, REMAIN(n, len)); 1315 - n += cpy(buf + n, REMAIN(n, len), " => ", 4); 1316 - n += tostr(js, entry->value, buf + n, REMAIN(n, len)); 1317 - } 1318 - } 1319 - stringify_indent--; 1320 - n += cpy(buf + n, REMAIN(n, len), "\n", 1); 1321 - n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1322 - n += cpy(buf + n, REMAIN(n, len), "}", 1); 1323 - } 1324 - pop_stringify(); 1325 - return n; 1326 - } 1327 - 1328 - if (is_set) { 1329 - ant_value_t set_val = js_get_slot(obj, SLOT_SET); 1330 - if (vtype(set_val) == T_UNDEF) goto print_tagged_object; 1331 - 1332 - set_entry_t **set_ptr = (set_entry_t**)(size_t)tod(set_val); 1333 - n += cpy(buf + n, REMAIN(n, len), "Set(", 4); 1334 - 1335 - unsigned int count = 0; 1336 - if (set_ptr && *set_ptr) count = HASH_COUNT(*set_ptr); 1337 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", count); 1338 - n += cpy(buf + n, REMAIN(n, len), ") ", 2); 1339 - 1340 - if (count == 0) { 1341 - n += cpy(buf + n, REMAIN(n, len), "{}", 2); 1342 - } else { 1343 - n += cpy(buf + n, REMAIN(n, len), "{\n", 2); 1344 - stringify_indent++; 1345 - bool first = true; 1346 - if (set_ptr && *set_ptr) { 1347 - set_entry_t *entry, *tmp; 1348 - HASH_ITER(hh, *set_ptr, entry, tmp) { 1349 - if (!first) n += cpy(buf + n, REMAIN(n, len), ",\n", 2); 1350 - first = false; 1351 - n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1352 - n += tostr(js, entry->value, buf + n, REMAIN(n, len)); 1353 - } 1354 - } 1355 - stringify_indent--; 1356 - n += cpy(buf + n, REMAIN(n, len), "\n", 1); 1357 - n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1358 - n += cpy(buf + n, REMAIN(n, len), "}", 1); 1287 + size_t new_cap = written + 1; 1288 + char *new_tmp = realloc(tmp, new_cap); 1289 + if (!new_tmp) { 1290 + free(tmp); 1291 + return false; 1359 1292 } 1360 - pop_stringify(); 1361 - return n; 1362 - } 1363 - 1364 - if (tag_str) { 1365 - bool is_timeout = (tlen == 7 && memcmp(tag_str, "Timeout", 7) == 0); 1366 - bool is_interval = (tlen == 8 && memcmp(tag_str, "Interval", 8) == 0); 1367 - if (is_timeout || is_interval) { 1368 - ant_value_t id_val = js_get_slot(obj, SLOT_DATA); 1369 - int timer_id = vtype(id_val) == T_NUM ? (int)js_getnum(id_val) : 0; 1370 - n += cpy(buf + n, REMAIN(n, len), tag_str, tlen); 1371 - n += (size_t) snprintf(buf + n, REMAIN(n, len), " (%d) {\n", timer_id); 1372 - goto continue_object_print; 1293 + tmp = new_tmp; 1294 + cap = new_cap; 1373 1295 } 1374 - bool is_blob = (tlen == 4 && memcmp(tag_str, "Blob", 4) == 0); 1375 - bool is_file = (tlen == 4 && memcmp(tag_str, "File", 4) == 0); 1376 - if (is_blob || is_file) { 1377 - blob_data_t *bd = blob_get_data(obj); 1378 - n += cpy(buf + n, REMAIN(n, len), is_file ? "File" : "Blob", 4); 1379 - n += cpy(buf + n, REMAIN(n, len), " { size: ", 9); 1380 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", bd ? bd->size : 0); 1381 - n += cpy(buf + n, REMAIN(n, len), ", type: '", 9); 1382 - if (bd && bd->type) n += cpy(buf + n, REMAIN(n, len), bd->type, strlen(bd->type)); 1383 - n += cpy(buf + n, REMAIN(n, len), "'", 1); 1384 - if (is_file) { 1385 - n += cpy(buf + n, REMAIN(n, len), ", name: '", 9); 1386 - if (bd && bd->name) n += cpy(buf + n, REMAIN(n, len), bd->name, strlen(bd->name)); 1387 - n += cpy(buf + n, REMAIN(n, len), "'", 1); 1388 - n += cpy(buf + n, REMAIN(n, len), ", lastModified: ", 16); 1389 - n += (size_t) snprintf(buf + n, REMAIN(n, len), "%" PRId64, bd ? bd->last_modified : 0); 1390 - } 1391 - n += cpy(buf + n, REMAIN(n, len), " }", 2); 1392 - pop_stringify(); 1393 - return n; 1394 - }} 1296 + } 1395 1297 1396 - print_tagged_object: 1397 - n += cpy(buf + n, REMAIN(n, len), "Object [", 8); 1398 - n += cpy(buf + n, REMAIN(n, len), (const char *)(uintptr_t)(toff), tlen); 1399 - n += cpy(buf + n, REMAIN(n, len), "] {\n", 4); 1400 - goto continue_object_print; 1298 + bool js_inspect_header(js_inspect_builder_t *builder, const char *fmt, ...) { 1299 + va_list args; 1300 + va_start(args, fmt); 1301 + bool ok = js_inspect_vappendf(builder, fmt, args); 1302 + va_end(args); 1303 + if (!ok) return false; 1304 + if (!js_inspect_append(builder, " {\n", 3)) return false; 1305 + 1306 + builder->inline_mode = false; 1307 + builder->first = true; 1308 + builder->closed = false; 1309 + builder->did_indent = false; 1401 1310 1402 - print_plain_object: 1403 - inline_mode = is_small_object(js, obj, &prop_count); 1311 + return true; 1312 + } 1313 + 1314 + bool js_inspect_tagged_header(js_inspect_builder_t *builder, const char *tag, size_t tag_len) { 1315 + if (!js_inspect_append(builder, "Object [", 8)) return false; 1316 + if (!js_inspect_append(builder, tag, tag_len)) return false; 1317 + if (!js_inspect_append(builder, "] {\n", 4)) return false; 1318 + 1319 + builder->inline_mode = false; 1320 + builder->first = true; 1321 + builder->closed = false; 1322 + builder->did_indent = false; 1404 1323 1324 + return true; 1325 + } 1326 + 1327 + // TODO: modularize 1328 + static bool js_inspect_plain_header(js_inspect_builder_t *builder, ant_value_t obj) { 1329 + ant_t *js = builder->js; 1330 + int prop_count = 0; 1331 + bool inline_mode = is_small_object(js, obj, &prop_count); 1332 + 1405 1333 ant_value_t proto_val = js_get_proto(js, obj); 1406 1334 bool is_null_proto = (vtype(proto_val) == T_NULL); 1407 1335 bool proto_is_null_proto = false; 1408 1336 const char *class_name = NULL; 1409 1337 ant_offset_t class_name_len = 0; 1410 - 1338 + 1411 1339 do { 1412 1340 if (is_null_proto) break; 1413 1341 uint8_t pt = vtype(proto_val); ··· 1415 1343 1416 1344 ant_value_t proto_proto = js_get_proto(js, proto_val); 1417 1345 ant_value_t object_proto = js->sym.object_proto; 1418 - proto_is_null_proto = (vtype(proto_proto) == T_NULL) && 1419 - (vdata(proto_val) != vdata(object_proto)); 1346 + proto_is_null_proto = (vtype(proto_proto) == T_NULL) && (vdata(proto_val) != vdata(object_proto)); 1420 1347 1421 1348 class_name = get_class_name(js, obj, &class_name_len, "Object"); 1422 1349 } while (0); 1423 - 1350 + 1424 1351 if (prop_count == 0) { 1425 1352 if (is_null_proto) { 1426 - n += cpy(buf + n, REMAIN(n, len), "[Object: null prototype] {}", 27); 1353 + if (!js_inspect_append(builder, "[Object: null prototype] {}", 27)) return false; 1427 1354 } else if (class_name && class_name_len > 0) { 1428 - n += cpy(buf + n, REMAIN(n, len), class_name, class_name_len); 1355 + if (!js_inspect_append(builder, class_name, class_name_len)) return false; 1429 1356 if (proto_is_null_proto) { 1430 - n += cpy(buf + n, REMAIN(n, len), " <[Object: null prototype] {}> {}", 33); 1431 - } else n += cpy(buf + n, REMAIN(n, len), " {}", 3); 1357 + if (!js_inspect_append(builder, " <[Object: null prototype] {}> {}", 33)) return false; 1358 + } else if (!js_inspect_append(builder, " {}", 3)) return false; 1432 1359 } else if (proto_is_null_proto) { 1433 - n += cpy(buf + n, REMAIN(n, len), "<[Object: null prototype] {}> {}", 32); 1434 - } else n += cpy(buf + n, REMAIN(n, len), "{}", 2); 1435 - pop_stringify(); 1436 - return n; 1360 + if (!js_inspect_append(builder, "<[Object: null prototype] {}> {}", 32)) return false; 1361 + } else if (!js_inspect_append(builder, "{}", 2)) return false; 1362 + 1363 + builder->closed = true; 1364 + return true; 1437 1365 } 1438 - 1366 + 1439 1367 if (is_null_proto) { 1440 - n += cpy(buf + n, REMAIN(n, len), "[Object: null prototype] ", 25); 1368 + if (!js_inspect_append(builder, "[Object: null prototype] ", 25)) return false; 1441 1369 } else if (class_name && class_name_len > 0) { 1442 - n += cpy(buf + n, REMAIN(n, len), class_name, class_name_len); 1370 + if (!js_inspect_append(builder, class_name, class_name_len)) return false; 1443 1371 if (proto_is_null_proto) { 1444 - n += cpy(buf + n, REMAIN(n, len), " <[Object: null prototype] {}> ", 31); 1445 - } else n += cpy(buf + n, REMAIN(n, len), " ", 1); 1372 + if (!js_inspect_append(builder, " <[Object: null prototype] {}> ", 31)) return false; 1373 + } else if (!js_inspect_append(builder, " ", 1)) return false; 1446 1374 } else if (proto_is_null_proto) { 1447 - n += cpy(buf + n, REMAIN(n, len), "<[Object: null prototype] {}> ", 30); 1375 + if (!js_inspect_append(builder, "<[Object: null prototype] {}> ", 30)) return false; 1448 1376 } 1377 + 1378 + if (!js_inspect_append(builder, inline_mode ? "{ " : "{\n", 2)) return false; 1379 + builder->inline_mode = inline_mode; 1380 + builder->first = true; 1381 + builder->closed = false; 1382 + builder->did_indent = false; 1449 1383 1450 - n += cpy(buf + n, REMAIN(n, len), inline_mode ? "{ " : "{\n", 2); 1451 - 1452 - continue_object_print:; 1453 - 1454 - if (!inline_mode) stringify_indent++; 1455 - bool first = true; 1456 - 1384 + return true; 1385 + } 1386 + 1387 + bool js_inspect_object_body(js_inspect_builder_t *builder, ant_value_t obj) { 1388 + if (builder->closed) return true; 1389 + 1390 + if (!builder->inline_mode && !builder->did_indent) { 1391 + stringify_indent++; 1392 + builder->did_indent = true; 1393 + } 1394 + 1395 + bool first = builder->first; 1396 + ant_t *js = builder->js; 1397 + ant_value_t tag_sym = get_toStringTag_sym(); 1457 1398 ant_value_t as_obj = js_as_obj(obj); 1458 1399 ant_object_t *ptr = js_obj_ptr(as_obj); 1459 1400 uintptr_t obj_off = (uintptr_t)vdata(as_obj); ··· 1463 1404 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i); 1464 1405 if (!prop) continue; 1465 1406 if ((ant_shape_get_attrs(ptr->shape, i) & ANT_PROP_ATTR_ENUMERABLE) == 0) continue; 1466 - 1467 1407 ant_value_t val = (i < ptr->prop_count) ? ant_object_prop_get_unchecked(ptr, i) : js_mkundef(); 1468 - 1408 + 1469 1409 if (prop->type == ANT_SHAPE_KEY_SYMBOL) { 1470 1410 ant_offset_t sym_off = prop->key.sym_off; 1471 1411 if (vtype(tag_sym) == T_SYMBOL && sym_off == (ant_offset_t)vdata(tag_sym)) continue; 1472 - 1412 + 1473 1413 if (ptr && ptr->is_exotic) { 1474 1414 prop_meta_t meta; 1475 1415 if (lookup_symbol_prop_meta(as_obj, sym_off, &meta) && !meta.enumerable) continue; 1476 1416 } 1477 - 1417 + 1478 1418 ant_value_t sym = mkval(T_SYMBOL, sym_off); 1479 - 1480 - if (!first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); 1419 + 1420 + if (!first && !js_inspect_append(builder, builder->inline_mode ? ", " : ",\n", 2)) return false; 1481 1421 first = false; 1482 - if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1483 - n += cpy(buf + n, REMAIN(n, len), "[", 1); 1484 - n += tostr(js, sym, buf + n, REMAIN(n, len)); 1485 - n += cpy(buf + n, REMAIN(n, len), "]: ", 3); 1486 - n += tostr(js, val, buf + n, REMAIN(n, len)); 1422 + if (!builder->inline_mode && !js_inspect_append_indent(builder, stringify_indent)) return false; 1423 + if (!js_inspect_append(builder, "[", 1)) return false; 1424 + if (!js_inspect_append_tostr(builder, sym)) return false; 1425 + if (!js_inspect_append(builder, "]: ", 3)) return false; 1426 + if (!js_inspect_append_tostr(builder, val)) return false; 1487 1427 continue; 1488 1428 } 1489 1429 ··· 1495 1435 } 1496 1436 1497 1437 if (prop->has_getter || prop->has_setter) { 1498 - if (!first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); 1438 + if (!first && !js_inspect_append(builder, builder->inline_mode ? ", " : ",\n", 2)) return false; 1499 1439 first = false; 1500 - if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1501 - n += strkey_interned(js, key, (size_t)klen, buf + n, REMAIN(n, len)); 1502 - n += cpy(buf + n, REMAIN(n, len), ": ", 2); 1503 - if (prop->has_getter && prop->has_setter) 1504 - n += cpy(buf + n, REMAIN(n, len), "[Getter/Setter]", 15); 1505 - else if (prop->has_getter) 1506 - n += cpy(buf + n, REMAIN(n, len), "[Getter]", 8); 1507 - else 1508 - n += cpy(buf + n, REMAIN(n, len), "[Setter]", 8); 1440 + if (!builder->inline_mode && !js_inspect_append_indent(builder, stringify_indent)) return false; 1441 + if (!js_inspect_append_key_interned(builder, key, (size_t)klen)) return false; 1442 + if (!js_inspect_append(builder, ": ", 2)) return false; 1443 + if (prop->has_getter && prop->has_setter) { 1444 + if (!js_inspect_append(builder, "[Getter/Setter]", 15)) return false; 1445 + } else if (prop->has_getter) { 1446 + if (!js_inspect_append(builder, "[Getter]", 8)) return false; 1447 + } else if (!js_inspect_append(builder, "[Setter]", 8)) return false; 1509 1448 continue; 1510 1449 } 1511 1450 1512 - if (!first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); 1451 + if (!first && !js_inspect_append(builder, builder->inline_mode ? ", " : ",\n", 2)) return false; 1513 1452 first = false; 1514 - if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1453 + if (!builder->inline_mode && !js_inspect_append_indent(builder, stringify_indent)) return false; 1515 1454 1516 1455 bool is_special_global = false; 1517 1456 if (vtype(val) == T_UNDEF && streq(key, klen, "undefined", 9)) { ··· 1526 1465 } 1527 1466 1528 1467 if (is_special_global) { 1529 - n += tostr(js, val, buf + n, REMAIN(n, len)); 1468 + if (!js_inspect_append_tostr(builder, val)) return false; 1530 1469 } else { 1531 - n += strkey_interned(js, key, (size_t)klen, buf + n, REMAIN(n, len)); 1532 - n += cpy(buf + n, REMAIN(n, len), ": ", 2); 1533 - n += tostr(js, val, buf + n, REMAIN(n, len)); 1470 + if (!js_inspect_append_key_interned(builder, key, (size_t)klen)) return false; 1471 + if (!js_inspect_append(builder, ": ", 2)) return false; 1472 + if (!js_inspect_append_tostr(builder, val)) return false; 1534 1473 } 1535 1474 } 1536 - 1475 + 1537 1476 if (ptr && ptr->is_exotic) { 1538 1477 descriptor_entry_t *desc, *tmp; 1539 1478 HASH_ITER(hh, desc_registry, desc, tmp) { ··· 1541 1480 if (!desc->enumerable) continue; 1542 1481 if (!desc->has_getter && !desc->has_setter) continue; 1543 1482 1544 - if (!first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); 1483 + if (!first && !js_inspect_append(builder, builder->inline_mode ? ", " : ",\n", 2)) return false; 1545 1484 first = false; 1546 - if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1547 - n += cpy(buf + n, REMAIN(n, len), desc->prop_name, desc->prop_len); 1548 - n += cpy(buf + n, REMAIN(n, len), ": ", 2); 1485 + if (!builder->inline_mode && !js_inspect_append_indent(builder, stringify_indent)) return false; 1486 + if (!js_inspect_append(builder, desc->prop_name, desc->prop_len)) return false; 1487 + if (!js_inspect_append(builder, ": ", 2)) return false; 1549 1488 1550 1489 if (desc->has_getter && desc->has_setter) { 1551 - n += cpy(buf + n, REMAIN(n, len), "[Getter/Setter]", 15); 1490 + if (!js_inspect_append(builder, "[Getter/Setter]", 15)) return false; 1552 1491 } else if (desc->has_getter) { 1553 - n += cpy(buf + n, REMAIN(n, len), "[Getter]", 8); 1554 - } else n += cpy(buf + n, REMAIN(n, len), "[Setter]", 8); 1492 + if (!js_inspect_append(builder, "[Getter]", 8)) return false; 1493 + } else if (!js_inspect_append(builder, "[Setter]", 8)) return false; 1555 1494 } 1556 1495 } 1496 + 1497 + builder->first = first; 1498 + return true; 1499 + } 1500 + 1501 + bool js_inspect_close(js_inspect_builder_t *builder) { 1502 + if (builder->closed) return true; 1503 + 1504 + if (builder->did_indent) { 1505 + stringify_indent--; 1506 + builder->did_indent = false; 1507 + } 1508 + 1509 + if (builder->inline_mode) { 1510 + if (!js_inspect_append(builder, " }", 2)) return false; 1511 + } else { 1512 + if (!builder->first && !js_inspect_append(builder, "\n", 1)) return false; 1513 + if (!js_inspect_append_indent(builder, stringify_indent)) return false; 1514 + if (!js_inspect_append(builder, "}", 1)) return false; 1515 + } 1516 + 1517 + builder->closed = true; 1518 + return true; 1519 + } 1520 + 1521 + // todo: split into smaller functions 1522 + static size_t strobj(ant_t *js, ant_value_t obj, char *buf, size_t len) { 1523 + int ref = get_circular_ref(obj); 1524 + if (ref) return ref > 0 ? (size_t) snprintf(buf, len, "[Circular *%d]", ref) : cpy(buf, len, "[Circular]", 10); 1557 1525 1558 - if (!inline_mode) stringify_indent--; 1559 - if (inline_mode) { 1560 - n += cpy(buf + n, REMAIN(n, len), " }", 2); 1561 - } else { 1562 - if (!first) n += cpy(buf + n, REMAIN(n, len), "\n", 1); 1563 - n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1564 - n += cpy(buf + n, REMAIN(n, len), "}", 1); 1526 + push_stringify(obj); 1527 + 1528 + size_t n = 0; 1529 + int self_ref = get_self_ref(obj); 1530 + if (self_ref) { 1531 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "<ref *%d> ", self_ref); 1532 + } 1533 + 1534 + ant_value_t inspect_val = strobj_call_custom_inspect(js, obj); 1535 + if (vtype(inspect_val) == T_STR) { 1536 + size_t slen = 0; 1537 + const char *s = js_getstr(js, inspect_val, &slen); 1538 + n += cpy(buf + n, REMAIN(n, len), s ? s : "", slen); 1539 + pop_stringify(); 1540 + return n; 1541 + } 1542 + 1543 + if (is_date_instance(obj)) { 1544 + n += strdate(js, obj, buf + n, REMAIN(n, len)); 1545 + pop_stringify(); 1546 + return n; 1547 + } 1548 + 1549 + ant_value_t tag_sym = get_toStringTag_sym(); 1550 + ant_value_t tag_val = (vtype(tag_sym) == T_SYMBOL) ? lkp_sym_proto_val(js, obj, (ant_offset_t)vdata(tag_sym)) : js_mkundef(); 1551 + bool is_map = false, is_set = false, is_arraybuffer = false; 1552 + ant_offset_t tlen = 0, toff = 0; 1553 + const char *tag_str = NULL; 1554 + 1555 + if (vtype(tag_val) == T_STR) { 1556 + toff = vstr(js, tag_val, &tlen); 1557 + tag_str = (const char *)(uintptr_t)(toff); 1558 + is_map = (tlen == 3 && memcmp(tag_str, "Map", 3) == 0); 1559 + is_set = (tlen == 3 && memcmp(tag_str, "Set", 3) == 0); 1560 + is_arraybuffer = (tlen >= 11 && memcmp(tag_str + tlen - 11, "ArrayBuffer", 11) == 0); 1561 + 1562 + TypedArrayData *ta = buffer_get_typedarray_data(obj); 1563 + if (ta && ta->buffer) { 1564 + const char *type_name = NULL; 1565 + size_t type_len = 0; 1566 + 1567 + ant_value_t proto = js_get_proto(js, obj); 1568 + ant_value_t buffer_proto = get_ctor_proto(js, "Buffer", 6); 1569 + if (vtype(proto) == T_OBJ && vtype(buffer_proto) == T_OBJ && vdata(proto) == vdata(buffer_proto)) { 1570 + type_name = "Buffer"; 1571 + type_len = 6; 1572 + } else if (ta->type <= TYPED_ARRAY_BIGUINT64) { 1573 + type_name = buffer_typedarray_type_name(ta->type); 1574 + type_len = strlen(type_name); 1575 + } else { 1576 + type_name = "TypedArray"; 1577 + type_len = 10; 1578 + } 1579 + 1580 + n += cpy(buf + n, REMAIN(n, len), type_name, type_len); 1581 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "(%zu) ", ta->length); 1582 + n += cpy(buf + n, REMAIN(n, len), "[ ", 2); 1583 + 1584 + uint8_t *data = ta->buffer->data + ta->byte_offset; 1585 + 1586 + for (size_t i = 0; i < ta->length && i < 100; i++) { 1587 + if (i > 0) n += cpy(buf + n, REMAIN(n, len), ", ", 2); 1588 + 1589 + switch (ta->type) { 1590 + case TYPED_ARRAY_INT8: 1591 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%d", (int)((int8_t*)data)[i]); 1592 + break; 1593 + case TYPED_ARRAY_UINT8: 1594 + case TYPED_ARRAY_UINT8_CLAMPED: 1595 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", (unsigned)data[i]); 1596 + break; 1597 + case TYPED_ARRAY_INT16: 1598 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%d", (int)((int16_t*)data)[i]); 1599 + break; 1600 + case TYPED_ARRAY_UINT16: 1601 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", (unsigned)((uint16_t*)data)[i]); 1602 + break; 1603 + case TYPED_ARRAY_INT32: 1604 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%d", ((int32_t*)data)[i]); 1605 + break; 1606 + case TYPED_ARRAY_UINT32: 1607 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", ((uint32_t*)data)[i]); 1608 + break; 1609 + case TYPED_ARRAY_FLOAT16: 1610 + n += (size_t) snprintf( 1611 + buf + n, REMAIN(n, len), "%g", half_to_double(((uint16_t*)data)[i]) 1612 + ); 1613 + break; 1614 + case TYPED_ARRAY_FLOAT32: 1615 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%g", (double)((float*)data)[i]); 1616 + break; 1617 + case TYPED_ARRAY_FLOAT64: 1618 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%g", ((double*)data)[i]); 1619 + break; 1620 + case TYPED_ARRAY_BIGINT64: 1621 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%lldn", (long long)((int64_t*)data)[i]); 1622 + break; 1623 + case TYPED_ARRAY_BIGUINT64: 1624 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%llun", (unsigned long long)((uint64_t*)data)[i]); 1625 + break; 1626 + default: 1627 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", (unsigned)data[i]); 1628 + break; 1629 + } 1630 + } 1631 + 1632 + if (ta->length > 100) n += cpy(buf + n, REMAIN(n, len), ", ...", 5); 1633 + n += cpy(buf + n, REMAIN(n, len), " ]", 2); 1634 + pop_stringify(); 1635 + return n; 1636 + } 1637 + 1638 + if (is_arraybuffer) { 1639 + ArrayBufferData *ab_data = buffer_get_arraybuffer_data(obj); 1640 + if (ab_data) { 1641 + size_t bytelen = ab_data ? ab_data->length : 0; 1642 + 1643 + n += cpy(buf + n, REMAIN(n, len), tag_str, tlen); 1644 + n += cpy(buf + n, REMAIN(n, len), " {\n", 3); 1645 + n += cpy(buf + n, REMAIN(n, len), " [Uint8Contents]: <", 20); 1646 + 1647 + if (ab_data && ab_data->data && bytelen > 0) { 1648 + for (size_t i = 0; i < bytelen; i++) { 1649 + if (i > 0) n += cpy(buf + n, REMAIN(n, len), " ", 1); 1650 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%02x", ab_data->data[i]); 1651 + } 1652 + } 1653 + 1654 + n += cpy(buf + n, REMAIN(n, len), ">,\n", 3); 1655 + n += cpy(buf + n, REMAIN(n, len), " [byteLength]: ", 16); 1656 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", bytelen); 1657 + n += cpy(buf + n, REMAIN(n, len), "\n}", 2); 1658 + pop_stringify(); 1659 + return n; 1660 + } 1661 + } 1662 + 1663 + bool is_dataview = (tlen == 8 && memcmp(tag_str, "DataView", 8) == 0); 1664 + if (is_dataview) { 1665 + DataViewData *dv = buffer_get_dataview_data(obj); 1666 + if (dv && dv->buffer) { 1667 + n += cpy(buf + n, REMAIN(n, len), "DataView {\n", 11); 1668 + n += cpy(buf + n, REMAIN(n, len), " [byteLength]: ", 16); 1669 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", dv->byte_length); 1670 + n += cpy(buf + n, REMAIN(n, len), ",\n", 2); 1671 + n += cpy(buf + n, REMAIN(n, len), " [byteOffset]: ", 16); 1672 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", dv->byte_offset); 1673 + n += cpy(buf + n, REMAIN(n, len), ",\n", 2); 1674 + n += cpy(buf + n, REMAIN(n, len), " [buffer]: ArrayBuffer {\n", 26); 1675 + n += cpy(buf + n, REMAIN(n, len), " [Uint8Contents]: <", 22); 1676 + 1677 + if (dv->buffer->data && dv->buffer->length > 0) { 1678 + for (size_t i = 0; i < dv->buffer->length; i++) { 1679 + if (i > 0) n += cpy(buf + n, REMAIN(n, len), " ", 1); 1680 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%02x", dv->buffer->data[i]); 1681 + } 1682 + } 1683 + 1684 + n += cpy(buf + n, REMAIN(n, len), ">,\n", 3); 1685 + n += cpy(buf + n, REMAIN(n, len), " [byteLength]: ", 18); 1686 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", dv->buffer->length); 1687 + n += cpy(buf + n, REMAIN(n, len), "\n }\n}", 6); 1688 + pop_stringify(); 1689 + return n; 1690 + } 1691 + } 1692 + 1693 + if (is_map) { 1694 + ant_value_t map_val = js_get_slot(obj, SLOT_MAP); 1695 + if (vtype(map_val) != T_UNDEF) { 1696 + map_entry_t **map_ptr = (map_entry_t**)(size_t)tod(map_val); 1697 + n += cpy(buf + n, REMAIN(n, len), "Map(", 4); 1698 + 1699 + unsigned int count = 0; 1700 + if (map_ptr && *map_ptr) count = HASH_COUNT(*map_ptr); 1701 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", count); 1702 + n += cpy(buf + n, REMAIN(n, len), ") ", 2); 1703 + 1704 + if (count == 0) { 1705 + n += cpy(buf + n, REMAIN(n, len), "{}", 2); 1706 + } else { 1707 + n += cpy(buf + n, REMAIN(n, len), "{\n", 2); 1708 + stringify_indent++; 1709 + bool first = true; 1710 + if (map_ptr && *map_ptr) { 1711 + map_entry_t *entry, *tmp; 1712 + HASH_ITER(hh, *map_ptr, entry, tmp) { 1713 + if (!first) n += cpy(buf + n, REMAIN(n, len), ",\n", 2); 1714 + first = false; 1715 + n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1716 + n += tostr(js, entry->key_val, buf + n, REMAIN(n, len)); 1717 + n += cpy(buf + n, REMAIN(n, len), " => ", 4); 1718 + n += tostr(js, entry->value, buf + n, REMAIN(n, len)); 1719 + } 1720 + } 1721 + stringify_indent--; 1722 + n += cpy(buf + n, REMAIN(n, len), "\n", 1); 1723 + n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1724 + n += cpy(buf + n, REMAIN(n, len), "}", 1); 1725 + } 1726 + pop_stringify(); 1727 + return n; 1728 + } 1729 + } 1730 + 1731 + if (is_set) { 1732 + ant_value_t set_val = js_get_slot(obj, SLOT_SET); 1733 + if (vtype(set_val) != T_UNDEF) { 1734 + set_entry_t **set_ptr = (set_entry_t**)(size_t)tod(set_val); 1735 + n += cpy(buf + n, REMAIN(n, len), "Set(", 4); 1736 + 1737 + unsigned int count = 0; 1738 + if (set_ptr && *set_ptr) count = HASH_COUNT(*set_ptr); 1739 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", count); 1740 + n += cpy(buf + n, REMAIN(n, len), ") ", 2); 1741 + 1742 + if (count == 0) { 1743 + n += cpy(buf + n, REMAIN(n, len), "{}", 2); 1744 + } else { 1745 + n += cpy(buf + n, REMAIN(n, len), "{\n", 2); 1746 + stringify_indent++; 1747 + bool first = true; 1748 + if (set_ptr && *set_ptr) { 1749 + set_entry_t *entry, *tmp; 1750 + HASH_ITER(hh, *set_ptr, entry, tmp) { 1751 + if (!first) n += cpy(buf + n, REMAIN(n, len), ",\n", 2); 1752 + first = false; 1753 + n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1754 + n += tostr(js, entry->value, buf + n, REMAIN(n, len)); 1755 + } 1756 + } 1757 + stringify_indent--; 1758 + n += cpy(buf + n, REMAIN(n, len), "\n", 1); 1759 + n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1760 + n += cpy(buf + n, REMAIN(n, len), "}", 1); 1761 + } 1762 + pop_stringify(); 1763 + return n; 1764 + } 1765 + } 1766 + 1767 + if (tag_str) { 1768 + bool is_blob = (tlen == 4 && memcmp(tag_str, "Blob", 4) == 0); 1769 + bool is_file = (tlen == 4 && memcmp(tag_str, "File", 4) == 0); 1770 + if (is_blob || is_file) { 1771 + blob_data_t *bd = blob_get_data(obj); 1772 + n += cpy(buf + n, REMAIN(n, len), is_file ? "File" : "Blob", 4); 1773 + n += cpy(buf + n, REMAIN(n, len), " { size: ", 9); 1774 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", bd ? bd->size : 0); 1775 + n += cpy(buf + n, REMAIN(n, len), ", type: '", 9); 1776 + if (bd && bd->type) n += cpy(buf + n, REMAIN(n, len), bd->type, strlen(bd->type)); 1777 + n += cpy(buf + n, REMAIN(n, len), "'", 1); 1778 + if (is_file) { 1779 + n += cpy(buf + n, REMAIN(n, len), ", name: '", 9); 1780 + if (bd && bd->name) n += cpy(buf + n, REMAIN(n, len), bd->name, strlen(bd->name)); 1781 + n += cpy(buf + n, REMAIN(n, len), "'", 1); 1782 + n += cpy(buf + n, REMAIN(n, len), ", lastModified: ", 16); 1783 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%" PRId64, bd ? bd->last_modified : 0); 1784 + } 1785 + n += cpy(buf + n, REMAIN(n, len), " }", 2); 1786 + pop_stringify(); 1787 + return n; 1788 + } 1789 + } 1790 + 1791 + js_inspect_builder_t builder; 1792 + js_inspect_builder_init_fixed(&builder, js, buf, len, n); 1793 + bool ok = js_inspect_tagged_header(&builder, (const char *)(uintptr_t)toff, (size_t)tlen); 1794 + if (ok) ok = js_inspect_object_body(&builder, obj); 1795 + if (ok) ok = js_inspect_close(&builder); 1796 + n = builder.n; 1797 + pop_stringify(); 1798 + return n; 1565 1799 } 1800 + 1801 + js_inspect_builder_t builder; 1802 + js_inspect_builder_init_fixed(&builder, js, buf, len, n); 1803 + bool ok = js_inspect_plain_header(&builder, obj); 1804 + if (ok) ok = js_inspect_object_body(&builder, obj); 1805 + if (ok) ok = js_inspect_close(&builder); 1806 + n = builder.n; 1566 1807 pop_stringify(); 1808 + 1567 1809 return n; 1568 1810 } 1569 1811 ··· 6560 6802 js->thrown_exists = saved->thrown_exists; 6561 6803 js->thrown_value = saved->thrown_value; 6562 6804 js->thrown_stack = saved->thrown_stack; 6805 + } 6806 + 6807 + static ant_value_t strobj_call_custom_inspect(ant_t *js, ant_value_t obj) { 6808 + ant_value_t inspect_sym = get_inspect_sym(); 6809 + if (vtype(inspect_sym) != T_SYMBOL) return js_mkundef(); 6810 + 6811 + ant_value_t inspect_fn = lkp_sym_proto_val(js, obj, (ant_offset_t)vdata(inspect_sym)); 6812 + if (!is_callable(inspect_fn)) return js_mkundef(); 6813 + 6814 + js_exception_state_t saved = js_save_exception(js); 6815 + ant_value_t depth_arg = js_mknum((double)(MAX_STRINGIFY_DEPTH - stringify_depth)); 6816 + ant_value_t result; 6817 + 6818 + if (vtype(inspect_fn) == T_CFUNC) { 6819 + ant_value_t saved_this = js->this_val; 6820 + js->this_val = obj; 6821 + result = ((ant_value_t (*)(ant_t *, ant_value_t *, int))vdata(inspect_fn))(js, &depth_arg, 1); 6822 + js->this_val = saved_this; 6823 + } else { 6824 + result = sv_vm_call(js->vm, js, inspect_fn, obj, &depth_arg, 1, NULL, false); 6825 + } 6826 + 6827 + if (is_err(result) || js->thrown_exists || vtype(result) != T_STR) { 6828 + js_restore_exception(js, &saved); 6829 + return js_mkundef(); 6830 + } 6831 + 6832 + js_restore_exception(js, &saved); 6833 + return result; 6563 6834 } 6564 6835 6565 6836 ant_value_t js_define_property(ant_t *js, ant_value_t obj, ant_value_t prop, ant_value_t descriptor, bool reflect_mode) {
+54 -14
src/modules/timer.c
··· 8 8 9 9 #include "errors.h" 10 10 #include "runtime.h" 11 + #include "internal.h" 11 12 12 13 #include "silver/engine.h" 13 14 #include "gc/roots.h" ··· 76 77 .active_timer_count = 0, 77 78 }; 78 79 80 + static ant_value_t g_timeout_proto = 0; 81 + static ant_value_t g_interval_proto = 0; 82 + 79 83 static void add_timer_entry(timer_entry_t *entry) { 80 84 entry->next = timer_state.timers; 81 85 entry->prev = NULL; ··· 115 119 return js_get_slot(js_getthis(js), SLOT_DATA); 116 120 } 117 121 122 + static ant_value_t timer_inspect(ant_t *js, ant_value_t *args, int nargs) { 123 + ant_value_t this_obj = js_getthis(js); 124 + ant_value_t id_val = js_get_slot(this_obj, SLOT_DATA); 125 + int timer_id = vtype(id_val) == T_NUM ? (int)js_getnum(id_val) : 0; 126 + 127 + ant_value_t tag_val = js_get_sym(js, this_obj, get_toStringTag_sym()); 128 + const char *tag = vtype(tag_val) == T_STR ? js_getstr(js, tag_val, NULL) : "Timeout"; 129 + 130 + js_inspect_builder_t builder; 131 + if (!js_inspect_builder_init_dynamic(&builder, js, 128)) { 132 + return js_mkerr(js, "out of memory"); 133 + } 134 + 135 + bool ok = js_inspect_header(&builder, "%s (%d)", tag, timer_id); 136 + if (ok) ok = js_inspect_object_body(&builder, this_obj); 137 + if (ok) ok = js_inspect_close(&builder); 138 + 139 + if (!ok) { 140 + js_inspect_builder_dispose(&builder); 141 + return js_mkerr(js, "out of memory"); 142 + } 143 + 144 + return js_inspect_builder_result(&builder); 145 + } 146 + 118 147 static ant_value_t js_timer_ref(ant_t *js, ant_value_t *args, int nargs) { 119 148 ant_value_t this_obj = js_getthis(js); 120 149 timer_entry_t *entry = find_timer_entry_by_id((int)js_getnum(js_get_slot(this_obj, SLOT_DATA))); ··· 139 168 140 169 static ant_value_t timer_make_object(ant_t *js, int id, double delay_ms, int is_interval, ant_value_t callback) { 141 170 ant_value_t obj = js_mkobj(js); 171 + ant_value_t proto = is_interval ? g_interval_proto : g_timeout_proto; 172 + 173 + if (is_object_type(proto)) js_set_proto_init(obj, proto); 142 174 143 175 js_set(js, obj, "delay", js_mknum(delay_ms)); 144 176 js_set(js, obj, "repeat", is_interval ? js_mknum(delay_ms) : js_mknull()); 145 177 146 178 js_set(js, obj, "callback", callback); 147 179 js_set_descriptor(js, obj, "callback", 8, JS_DESC_W | JS_DESC_C); 148 - 149 - js_set(js, obj, "ref", js_mkfun(js_timer_ref)); 150 - js_set_descriptor(js, obj, "ref", 3, JS_DESC_W | JS_DESC_C); 151 - 152 - js_set(js, obj, "unref", js_mkfun(js_timer_unref)); 153 - js_set_descriptor(js, obj, "unref", 5, JS_DESC_W | JS_DESC_C); 154 - 155 - js_set(js, obj, "hasRef", js_mkfun(js_timer_has_ref)); 156 - js_set_descriptor(js, obj, "hasRef", 6, JS_DESC_W | JS_DESC_C); 157 - 158 - js_set_sym(js, obj, get_toStringTag_sym(), js_mkstr(js, is_interval 159 - ? "Interval" 160 - : "Timeout", is_interval ? 8 : 7) 161 - ); 162 180 163 181 js_set_slot(obj, SLOT_DATA, js_mknum((double)id)); 164 182 js_set_sym(js, obj, get_toPrimitive_sym(), js_mkfun(timer_to_primitive)); ··· 773 791 void init_timer_module() { 774 792 ant_t *js = rt->js; 775 793 timer_state.js = js; 794 + 795 + g_timeout_proto = js_mkobj(js); 796 + g_interval_proto = js_mkobj(js); 797 + gc_register_root(&g_timeout_proto); 798 + gc_register_root(&g_interval_proto); 799 + 800 + js_set_proto_init(g_timeout_proto, js->sym.object_proto); 801 + js_set(js, g_timeout_proto, "ref", js_mkfun(js_timer_ref)); 802 + js_set(js, g_timeout_proto, "unref", js_mkfun(js_timer_unref)); 803 + js_set(js, g_timeout_proto, "hasRef", js_mkfun(js_timer_has_ref)); 804 + js_set_sym(js, g_timeout_proto, get_toStringTag_sym(), js_mkstr(js, "Timeout", 7)); 805 + js_set_sym(js, g_timeout_proto, get_inspect_sym(), js_mkfun(timer_inspect)); 806 + 807 + js_set_proto_init(g_interval_proto, js->sym.object_proto); 808 + js_set(js, g_interval_proto, "ref", js_mkfun(js_timer_ref)); 809 + js_set(js, g_interval_proto, "unref", js_mkfun(js_timer_unref)); 810 + js_set(js, g_interval_proto, "hasRef", js_mkfun(js_timer_has_ref)); 811 + js_set_sym(js, g_interval_proto, get_toStringTag_sym(), js_mkstr(js, "Interval", 8)); 812 + js_set_sym(js, g_interval_proto, get_inspect_sym(), js_mkfun(timer_inspect)); 813 + 776 814 timers_define_common(js, js_glob(js)); 777 815 } 778 816 ··· 802 840 } 803 841 804 842 void gc_mark_timers(ant_t *js, gc_mark_fn mark) { 843 + if (is_object_type(g_timeout_proto)) mark(js, g_timeout_proto); 844 + if (is_object_type(g_interval_proto)) mark(js, g_interval_proto); 805 845 for (timer_entry_t *t = timer_state.timers; t; t = t->next) { 806 846 mark(js, t->callback); 807 847 for (int i = 0; i < t->nargs; i++) mark(js, t->args[i]);
+4
src/types/ant.d.ts
··· 33 33 | 'sunos' 34 34 | 'os/2'; 35 35 36 + interface SymbolConstructor { 37 + readonly inspect: symbol; 38 + } 39 + 36 40 interface AntPoolInfo { 37 41 used: number; 38 42 capacity: number;
+61
tests/test_inspect_custom.cjs
··· 1 + const { inspect } = require('node:util'); 2 + 3 + function assert(cond, msg) { 4 + if (!cond) throw new Error(msg); 5 + } 6 + 7 + assert(typeof Symbol.inspect === 'symbol', 'expected Symbol.inspect to exist'); 8 + assert(Symbol.inspect !== Symbol.for('ant.inspect'), 'expected Symbol.inspect to stay separate from the global registry'); 9 + assert(Symbol.keyFor(Symbol.inspect) === undefined, 'expected Symbol.inspect to not be registry-backed'); 10 + 11 + class Connection { 12 + constructor(host, port, state) { 13 + this.host = host; 14 + this.port = port; 15 + this.state = state; 16 + } 17 + 18 + [Symbol.inspect]() { 19 + return `Connection { ${this.host}:${this.port} (${this.state}) }`; 20 + } 21 + } 22 + 23 + class FallbackConnection { 24 + constructor(host) { 25 + this.host = host; 26 + } 27 + 28 + [Symbol.inspect]() { 29 + throw new Error('boom'); 30 + } 31 + } 32 + 33 + const blob = new Blob(['hi'], { type: 'text/plain' }); 34 + const file = new File(['hi'], 'note.txt', { type: 'text/plain', lastModified: 42 }); 35 + const timeout = setTimeout(() => {}, 1); 36 + const interval = setInterval(() => {}, 5); 37 + const timeoutInspect = inspect(timeout); 38 + const intervalInspect = inspect(interval); 39 + 40 + assert(inspect(new Connection('localhost', 3000, 'open')) === 'Connection { localhost:3000 (open) }', 'expected custom inspect result'); 41 + assert(inspect(blob) === "Blob { size: 2, type: 'text/plain' }", 'expected Blob custom inspect output'); 42 + assert(inspect(file) === "File { size: 2, type: 'text/plain', name: 'note.txt', lastModified: 42 }", 'expected File custom inspect output'); 43 + assert( 44 + timeoutInspect === 'Timeout (1) {\n delay: 1,\n repeat: null,\n [Symbol(Symbol.toPrimitive)]: [native code]\n}', 45 + `expected legacy Timeout inspect output, got: ${timeoutInspect}` 46 + ); 47 + assert( 48 + intervalInspect === 'Interval (2) {\n delay: 5,\n repeat: 5,\n [Symbol(Symbol.toPrimitive)]: [native code]\n}', 49 + `expected legacy Interval inspect output, got: ${intervalInspect}` 50 + ); 51 + 52 + clearTimeout(timeout); 53 + clearInterval(interval); 54 + 55 + const fallback = inspect(new FallbackConnection('db.internal')); 56 + assert( 57 + fallback === "FallbackConnection { host: 'db.internal' }", 58 + `expected fallback formatting after inspect throw, got: ${fallback}` 59 + ); 60 + 61 + console.log('PASS');