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.

at master 754 lines 22 kB view raw
1#include <stdlib.h> 2#include <stdint.h> 3#include <string.h> 4#include <math.h> 5#include <yyjson.h> 6#include <uthash.h> 7 8#include "gc/roots.h" 9#include "utf8.h" 10#include "errors.h" 11#include "runtime.h" 12#include "internal.h" 13 14#include "silver/engine.h" 15#include "modules/json.h" 16#include "modules/symbol.h" 17 18typedef struct { 19 const char *key; 20 size_t key_len; 21 ant_offset_t prop_off; 22 UT_hash_handle hh; 23} json_key_entry_t; 24 25static inline bool json_value_needs_temp_root(ant_value_t value) { 26 if (value <= NANBOX_PREFIX) return false; 27 28 static const uint32_t mask = 29 (1u << T_STR) | (1u << T_OBJ) | (1u << T_ARR) | (1u << T_FUNC) | 30 (1u << T_PROMISE) | (1u << T_GENERATOR) | (1u << T_SYMBOL) | (1u << T_BIGINT); 31 32 uint8_t t = vtype(value); 33 return t < 32 && (mask >> t) & 1; 34} 35 36static inline bool json_temp_pin(gc_temp_root_scope_t *roots, ant_value_t value) { 37 if (!json_value_needs_temp_root(value)) return true; 38 return gc_temp_root_handle_valid(gc_temp_root_add(roots, value)); 39} 40 41static inline ant_value_t json_parse_oom(ant_t *js) { 42 return js_mkerr(js, "JSON.parse() failed: out of memory"); 43} 44 45static inline ant_value_t json_stringify_oom(ant_t *js) { 46 return js_mkerr(js, "JSON.stringify() failed: out of memory"); 47} 48 49static ant_value_t yyjson_to_jsval(ant_t *js, yyjson_val *val, gc_temp_root_scope_t *roots) { 50 if (!val) return js_mkundef(); 51 52 switch (yyjson_get_type(val)) { 53 case YYJSON_TYPE_NULL: return js_mknull(); 54 case YYJSON_TYPE_BOOL: return js_bool(yyjson_get_bool(val)); 55 56 case YYJSON_TYPE_STR: { 57 ant_value_t str = js_mkstr(js, yyjson_get_str(val), yyjson_get_len(val)); 58 if (is_err(str)) return str; 59 if (!json_temp_pin(roots, str)) return json_parse_oom(js); 60 return str; 61 } 62 63 case YYJSON_TYPE_NUM: { 64 if (yyjson_is_sint(val)) return js_mknum((double)yyjson_get_sint(val)); 65 if (yyjson_is_uint(val)) return js_mknum((double)yyjson_get_uint(val)); 66 return js_mknum(yyjson_get_real(val)); 67 } 68 69 case YYJSON_TYPE_ARR: { 70 ant_value_t arr = js_mkarr(js); 71 if (is_err(arr)) return arr; 72 if (!json_temp_pin(roots, arr)) return json_parse_oom(js); 73 size_t idx, max; 74 yyjson_val *item; 75 76 yyjson_arr_foreach(val, idx, max, item) { 77 ant_value_t elem = yyjson_to_jsval(js, item, roots); 78 if (is_err(elem)) return elem; 79 js_arr_push(js, arr, elem); 80 } 81 82 return arr; 83 } 84 85 case YYJSON_TYPE_OBJ: { 86 ant_value_t obj = js_newobj(js); 87 if (is_err(obj)) return obj; 88 if (!json_temp_pin(roots, obj)) return json_parse_oom(js); 89 90 size_t idx, max; yyjson_val *key, *item; 91 json_key_entry_t *hash = NULL, *entry, *tmp; 92 93 yyjson_obj_foreach(val, idx, max, key, item) { 94 const char *k = yyjson_get_str(key); 95 96 size_t klen = yyjson_get_len(key); 97 ant_value_t v = yyjson_to_jsval(js, item, roots); 98 if (is_err(v)) { 99 HASH_ITER(hh, hash, entry, tmp) 100 HASH_DEL(hash, entry); free(entry); 101 return v; 102 } 103 104 HASH_FIND(hh, hash, k, klen, entry); 105 if (entry) js_saveval(js, entry->prop_off, v); else { 106 ant_offset_t off = js_mkprop_fast_off(js, obj, k, klen, v); 107 if (off == 0) { 108 HASH_ITER(hh, hash, entry, tmp) 109 HASH_DEL(hash, entry); free(entry); 110 return json_parse_oom(js); 111 } 112 entry = malloc(sizeof(json_key_entry_t)); 113 if (!entry) { 114 HASH_ITER(hh, hash, entry, tmp) 115 HASH_DEL(hash, entry); free(entry); 116 return json_parse_oom(js); 117 } 118 entry->key = k; entry->key_len = klen; entry->prop_off = off; 119 HASH_ADD_KEYPTR(hh, hash, entry->key, entry->key_len, entry); 120 }} 121 122 HASH_ITER(hh, hash, entry, tmp) 123 HASH_DEL(hash, entry); free(entry); 124 125 return obj; 126 } 127 128 default: return js_mkundef(); } 129} 130 131typedef struct { 132 ant_t *js; 133 ant_value_t *stack; 134 ant_value_t replacer_func; 135 ant_value_t replacer_arr; 136 ant_value_t error; 137 ant_value_t holder; 138 139 gc_temp_root_scope_t temp_roots; 140 gc_temp_root_handle_t error_handle; 141 gc_temp_root_handle_t holder_handle; 142 143 int stack_size; 144 int stack_cap; 145 int replacer_arr_len; 146 int has_cycle; 147} json_cycle_ctx; 148 149static inline bool json_has_abort(json_cycle_ctx *ctx) { 150 return ctx->has_cycle || vtype(ctx->error) != T_UNDEF; 151} 152 153static inline ant_value_t json_normalize_error(ant_value_t value) { 154 if (is_err(value) && vdata(value) != 0) return js_as_obj(value); 155 return value; 156} 157 158static void json_set_error(json_cycle_ctx *ctx, ant_value_t value) { 159 ctx->error = value; 160 gc_temp_root_set(ctx->error_handle, value); 161} 162 163static inline bool json_ctx_pin_value(json_cycle_ctx *ctx, ant_value_t value) { 164 if (json_temp_pin(&ctx->temp_roots, value)) return true; 165 json_set_error(ctx, json_stringify_oom(ctx->js)); 166 return false; 167} 168 169static inline void json_set_holder(json_cycle_ctx *ctx, ant_value_t value) { 170 ctx->holder = value; 171 gc_temp_root_set(ctx->holder_handle, value); 172} 173 174static void json_capture_error(json_cycle_ctx *ctx, ant_value_t value) { 175 if (vtype(ctx->error) != T_UNDEF) return; 176 if (ctx->js->thrown_exists) { 177 json_set_error(ctx, ctx->js->thrown_value); 178 ctx->js->thrown_exists = false; 179 ctx->js->thrown_value = js_mkundef(); 180 return; 181 } 182 json_set_error(ctx, json_normalize_error(value)); 183} 184 185static yyjson_mut_val *json_string_to_yyjson(ant_t *js, yyjson_mut_doc *doc, ant_value_t value) { 186 size_t byte_len = 0; 187 char *str = js_getstr(js, value, &byte_len); 188 size_t raw_len = 0; 189 char *raw = utf8_json_quote(str, byte_len, &raw_len); 190 if (!raw) goto oom; 191 yyjson_mut_val *out = yyjson_mut_rawncpy(doc, raw, raw_len); 192 free(raw); 193 return out; 194 195oom: 196 free(raw); 197 return NULL; 198} 199 200static int json_cycle_check(json_cycle_ctx *ctx, ant_value_t val) { 201 for (int i = 0; i < ctx->stack_size; i++) 202 if (ctx->stack[i] == val) { ctx->has_cycle = 1; return 1; } 203 return 0; 204} 205 206static void json_cycle_push(json_cycle_ctx *ctx, ant_value_t val) { 207 if (ctx->stack_size >= ctx->stack_cap) { 208 ctx->stack_cap = ctx->stack_cap ? ctx->stack_cap * 2 : 16; 209 ctx->stack = realloc(ctx->stack, ctx->stack_cap * sizeof(ant_value_t)); 210 } 211 ctx->stack[ctx->stack_size++] = val; 212} 213 214static inline void json_cycle_pop(json_cycle_ctx *ctx) { 215 if (ctx->stack_size > 0) ctx->stack_size--; 216} 217 218static inline int key_matches(const char *a, size_t a_len, const char *b, size_t b_len) { 219 return a_len == b_len && memcmp(a, b, a_len) == 0; 220} 221 222static inline bool json_is_array(ant_value_t value) { 223 return vtype(value) == T_ARR; 224} 225 226static inline ant_value_t json_snapshot_keys(ant_t *js, ant_value_t value) { 227 if (!is_special_object(value)) return js_mkarr(js); 228 return js_for_in_keys(js, value); 229} 230 231static int is_key_in_replacer_arr(ant_t *js, json_cycle_ctx *ctx, const char *key, size_t key_len) { 232 if (!is_special_object(ctx->replacer_arr)) return 1; 233 234 for (int i = 0; i < ctx->replacer_arr_len; i++) { 235 char idxstr[32]; 236 snprintf(idxstr, sizeof(idxstr), "%d", i); 237 238 ant_value_t item = js_get(js, ctx->replacer_arr, idxstr); 239 int type = vtype(item); 240 241 if (type == T_STR) { 242 size_t item_len; 243 char *item_str = js_getstr(js, item, &item_len); 244 if (key_matches(item_str, item_len, key, key_len)) return 1; 245 } else if (type == T_NUM) { 246 char numstr[32]; 247 snprintf(numstr, sizeof(numstr), "%.0f", js_getnum(item)); 248 if (key_matches(numstr, strlen(numstr), key, key_len)) return 1; 249 }} 250 251 return 0; 252} 253 254static yyjson_mut_val *ant_value_to_yyjson_with_key( 255 ant_t *js, yyjson_mut_doc *doc, const char *key, 256 ant_value_t val, json_cycle_ctx *ctx, int in_array 257); 258 259static ant_value_t apply_reviver( 260 ant_t *js, ant_value_t holder, 261 const char *key, ant_value_t reviver, 262 gc_temp_root_scope_t *roots 263); 264 265static ant_value_t json_apply_tojson( 266 ant_t *js, 267 const char *key, 268 ant_value_t val, 269 json_cycle_ctx *ctx 270) { 271 if (!is_special_object(val)) return val; 272 ant_value_t toJSON = js_get(js, val, "toJSON"); 273 274 if (is_err(toJSON)) { 275 json_capture_error(ctx, toJSON); 276 return js_mkundef(); 277 } 278 279 if (!is_callable(toJSON)) return val; 280 ant_value_t key_arg = js_mkstr(js, key, strlen(key)); 281 if (is_err(key_arg)) { 282 json_capture_error(ctx, key_arg); 283 return js_mkundef(); 284 } 285 286 if (!json_ctx_pin_value(ctx, key_arg)) return js_mkundef(); 287 ant_value_t args[1] = { key_arg }; 288 289 ant_value_t transformed = sv_vm_call( 290 js->vm, js, 291 toJSON, val, 292 args, 1, NULL, false 293 ); 294 295 if (is_err(transformed)) { 296 json_capture_error(ctx, transformed); 297 return js_mkundef(); 298 } 299 if (!json_ctx_pin_value(ctx, transformed)) return js_mkundef(); 300 301 return transformed; 302} 303 304static ant_value_t json_apply_replacer( 305 ant_t *js, 306 const char *key, 307 ant_value_t val, 308 json_cycle_ctx *ctx 309) { 310 if (!is_callable(ctx->replacer_func)) return val; 311 ant_value_t key_arg = js_mkstr(js, key, strlen(key)); 312 if (is_err(key_arg)) { 313 json_capture_error(ctx, key_arg); 314 return js_mkundef(); 315 } 316 if (!json_ctx_pin_value(ctx, key_arg)) return js_mkundef(); 317 ant_value_t args[2] = { key_arg, val }; 318 319 ant_value_t transformed = sv_vm_call( 320 js->vm, js, 321 ctx->replacer_func, ctx->holder, 322 args, 2, NULL, false 323 ); 324 325 if (is_err(transformed)) { 326 json_capture_error(ctx, transformed); 327 return js_mkundef(); 328 } 329 if (!json_ctx_pin_value(ctx, transformed)) return js_mkundef(); 330 331 return transformed; 332} 333 334static inline ant_value_t json_create_root_holder(ant_t *js, ant_value_t value, json_cycle_ctx *ctx) { 335 ant_value_t holder = js_mkobj(js); 336 if (is_err(holder)) return holder; 337 if (!json_ctx_pin_value(ctx, holder)) return js_mkundef(); 338 js_set(js, holder, "", value); 339 return holder; 340} 341 342static yyjson_mut_val *json_array_to_yyjson( 343 ant_t *js, yyjson_mut_doc *doc, ant_value_t val, json_cycle_ctx *ctx 344) { 345 yyjson_mut_val *arr = yyjson_mut_arr(doc); 346 ant_offset_t length = js_arr_len(js, val); 347 ant_value_t saved_holder = ctx->holder; 348 349 json_set_holder(ctx, val); 350 for (ant_offset_t i = 0; i < length; i++) { 351 char idxstr[32]; 352 uint_to_str(idxstr, sizeof(idxstr), (uint64_t)i); 353 ant_value_t elem = js_arr_get(js, val, i); 354 yyjson_mut_val *item = ant_value_to_yyjson_with_key(js, doc, idxstr, elem, ctx, 1); 355 if (json_has_abort(ctx)) { 356 json_set_holder(ctx, saved_holder); 357 return NULL; 358 } 359 yyjson_mut_arr_add_val(arr, item); 360 } 361 362 json_set_holder(ctx, saved_holder); 363 return arr; 364} 365 366static yyjson_mut_val *json_object_to_yyjson( 367 ant_t *js, yyjson_mut_doc *doc, ant_value_t val, json_cycle_ctx *ctx 368) { 369 yyjson_mut_val *obj = yyjson_mut_obj(doc); 370 ant_value_t keys = json_snapshot_keys(js, val); 371 ant_value_t saved_holder = ctx->holder; 372 373 if (is_err(keys)) { 374 json_capture_error(ctx, keys); 375 return NULL; 376 } 377 if (!json_ctx_pin_value(ctx, keys)) return NULL; 378 379 json_set_holder(ctx, val); 380 ant_offset_t key_count = js_arr_len(js, keys); 381 382 for (ant_offset_t i = 0; i < key_count; i++) { 383 ant_value_t key_val = js_arr_get(js, keys, i); 384 size_t key_len = 0; 385 char *key = js_getstr(js, key_val, &key_len); 386 387 if (!key) continue; 388 if (!is_key_in_replacer_arr(js, ctx, key, key_len)) continue; 389 390 ant_value_t prop = js_get(js, val, key); 391 if (is_err(prop)) { 392 json_capture_error(ctx, prop); 393 json_set_holder(ctx, saved_holder); 394 return NULL; 395 } 396 397 yyjson_mut_val *jval = ant_value_to_yyjson_with_key(js, doc, key, prop, ctx, 0); 398 if (json_has_abort(ctx)) { 399 json_set_holder(ctx, saved_holder); 400 return NULL; 401 } 402 403 if (jval == YYJSON_SKIP_VALUE) continue; 404 yyjson_mut_obj_add(obj, yyjson_mut_strncpy(doc, key, key_len), jval); 405 } 406 407 json_set_holder(ctx, saved_holder); 408 return obj; 409} 410 411static yyjson_mut_val *ant_value_to_yyjson_impl(ant_t *js, yyjson_mut_doc *doc, ant_value_t val, json_cycle_ctx *ctx, int in_array) { 412 int type = vtype(val); 413 yyjson_mut_val *result = NULL; 414 415 switch (type) { 416 case T_NULL: return yyjson_mut_null(doc); 417 case T_BOOL: return yyjson_mut_bool(doc, val == js_true); 418 419 case T_UNDEF: return in_array ? yyjson_mut_null(doc) : YYJSON_SKIP_VALUE; 420 case T_FUNC: return in_array ? yyjson_mut_null(doc) : YYJSON_SKIP_VALUE; 421 case T_SYMBOL: return in_array ? yyjson_mut_null(doc) : YYJSON_SKIP_VALUE; 422 423 case T_NUM: { 424 double num = js_getnum(val); 425 if (isnan(num) || isinf(num)) return yyjson_mut_null(doc); 426 if ( 427 num >= (double)INT64_MIN && 428 num < (double)INT64_MAX && 429 num == (double)(int64_t)num 430 ) return yyjson_mut_sint(doc, (int64_t)num); 431 return yyjson_mut_real(doc, num); 432 } 433 434 case T_STR: { 435 return json_string_to_yyjson(js, doc, val); 436 } 437 438 case T_OBJ: 439 case T_ARR: break; 440 default: return yyjson_mut_null(doc); 441 } 442 443 if (json_cycle_check(ctx, val)) return NULL; 444 json_cycle_push(ctx, val); 445 446 result = json_is_array(val) 447 ? json_array_to_yyjson(js, doc, val, ctx) 448 : json_object_to_yyjson(js, doc, val, ctx); 449 450 json_cycle_pop(ctx); 451 return result; 452} 453 454static yyjson_mut_val *ant_value_to_yyjson_with_key( 455 ant_t *js, yyjson_mut_doc *doc, const char *key, 456 ant_value_t val, json_cycle_ctx *ctx, int in_array 457) { 458 val = json_apply_tojson(js, key, val, ctx); 459 if (json_has_abort(ctx)) return NULL; 460 461 val = json_apply_replacer(js, key, val, ctx); 462 if (json_has_abort(ctx)) return NULL; 463 464 return ant_value_to_yyjson_impl(js, doc, val, ctx, in_array); 465} 466 467static yyjson_mut_val *ant_value_to_yyjson(ant_t *js, yyjson_mut_doc *doc, ant_value_t val, json_cycle_ctx *ctx) { 468 return ant_value_to_yyjson_with_key(js, doc, "", val, ctx, 0); 469} 470 471static ant_value_t apply_reviver_call( 472 ant_t *js, 473 ant_value_t holder, 474 const char *key, 475 ant_value_t reviver, 476 gc_temp_root_scope_t *roots 477) { 478 ant_value_t key_str = js_mkstr(js, key, strlen(key)); 479 if (is_err(key_str)) return key_str; 480 if (!json_temp_pin(roots, key_str)) return json_parse_oom(js); 481 ant_value_t current_value = js_get(js, holder, key); 482 ant_value_t call_args[2] = { key_str, current_value }; 483 484 ant_value_t result = sv_vm_call( 485 js->vm, js, reviver, holder, 486 call_args, 2, NULL, false 487 ); 488 if (!is_err(result) && !json_temp_pin(roots, result)) return json_parse_oom(js); 489 490 return result; 491} 492 493static void apply_reviver_to_array( 494 ant_t *js, 495 ant_value_t value, 496 ant_value_t reviver, 497 gc_temp_root_scope_t *roots 498) { 499 ant_offset_t length = js_arr_len(js, value); 500 501 for (ant_offset_t i = 0; i < length; i++) { 502 char idxstr[32]; 503 size_t idx_len = uint_to_str(idxstr, sizeof(idxstr), (uint64_t)i); 504 ant_value_t new_elem = apply_reviver(js, value, idxstr, reviver, roots); 505 if (vtype(new_elem) == T_UNDEF) js_delete_prop(js, value, idxstr, idx_len); 506 else { 507 ant_value_t key_val = js_mkstr(js, idxstr, idx_len); 508 if (is_err(key_val)) return; 509 if (!json_temp_pin(roots, key_val)) return; 510 js_setprop(js, value, key_val, new_elem); 511 }} 512} 513 514static void apply_reviver_to_object( 515 ant_t *js, 516 ant_value_t value, 517 ant_value_t reviver, 518 gc_temp_root_scope_t *roots 519) { 520 ant_value_t keys = json_snapshot_keys(js, value); 521 if (is_err(keys) || vtype(keys) != T_ARR) return; 522 if (!json_temp_pin(roots, keys)) return; 523 524 ant_offset_t key_count = js_arr_len(js, keys); 525 for (ant_offset_t i = 0; i < key_count; i++) { 526 ant_value_t key_val = js_arr_get(js, keys, i); 527 size_t key_len = 0; 528 char *key = js_getstr(js, key_val, &key_len); 529 if (!key) continue; 530 ant_value_t new_val = apply_reviver(js, value, key, reviver, roots); 531 if (vtype(new_val) == T_UNDEF) js_delete_prop(js, value, key, key_len); 532 else js_set(js, value, key, new_val); 533 } 534} 535 536static ant_value_t apply_reviver( 537 ant_t *js, 538 ant_value_t holder, 539 const char *key, 540 ant_value_t reviver, 541 gc_temp_root_scope_t *roots 542) { 543 ant_value_t val = js_get(js, holder, key); 544 545 if (json_is_array(val)) apply_reviver_to_array(js, val, reviver, roots); 546 else if (vtype(val) == T_OBJ) apply_reviver_to_object(js, val, reviver, roots); 547 548 return apply_reviver_call(js, holder, key, reviver, roots); 549} 550 551ant_value_t js_json_parse(ant_t *js, ant_value_t *args, int nargs) { 552 if (nargs < 1) return js_mkerr(js, "JSON.parse() requires at least 1 argument"); 553 if (vtype(args[0]) != T_STR) return js_mkerr(js, "JSON.parse() argument must be a string"); 554 gc_temp_root_scope_t temp_roots; 555 gc_temp_root_scope_begin(js, &temp_roots); 556 557 size_t len; 558 char *json_str = js_getstr(js, args[0], &len); 559 560 yyjson_doc *doc = yyjson_read(json_str, len, 0); 561 562 if (!doc) { 563 gc_temp_root_scope_end(&temp_roots); 564 return js_mkerr_typed(js, JS_ERR_SYNTAX, "JSON.parse: unexpected character"); 565 } 566 567 ant_value_t result = yyjson_to_jsval(js, yyjson_doc_get_root(doc), &temp_roots); 568 yyjson_doc_free(doc); 569 if (is_err(result)) { 570 gc_temp_root_scope_end(&temp_roots); 571 return result; 572 } 573 574 if (nargs >= 2 && is_callable(args[1])) { 575 ant_value_t reviver = args[1]; 576 if (!json_temp_pin(&temp_roots, reviver)) { 577 gc_temp_root_scope_end(&temp_roots); 578 return json_parse_oom(js); 579 } 580 ant_value_t root = js_mkobj(js); 581 if (is_err(root)) { 582 gc_temp_root_scope_end(&temp_roots); 583 return root; 584 } 585 if (!json_temp_pin(&temp_roots, root)) { 586 gc_temp_root_scope_end(&temp_roots); 587 return json_parse_oom(js); 588 } 589 js_set(js, root, "", result); 590 result = apply_reviver(js, root, "", reviver, &temp_roots); 591 } 592 593 gc_temp_root_scope_end(&temp_roots); 594 return result; 595} 596 597ant_value_t json_parse_value(ant_t *js, ant_value_t value) { 598 ant_value_t args[1] = { value }; 599 return js_json_parse(js, args, 1); 600} 601 602static yyjson_write_flag get_write_flags(ant_value_t *args, int nargs) { 603 if (nargs < 3) return 0; 604 605 int type = vtype(args[2]); 606 if (type == T_UNDEF || type == T_NULL) return 0; 607 if (type != T_NUM) return YYJSON_WRITE_PRETTY; 608 609 int indent = (int)js_getnum(args[2]); 610 if (indent <= 0) return 0; 611 if (indent == 2) return YYJSON_WRITE_PRETTY_TWO_SPACES; 612 613 return YYJSON_WRITE_PRETTY; 614} 615 616ant_value_t js_json_stringify(ant_t *js, ant_value_t *args, int nargs) { 617 ant_value_t result; 618 yyjson_mut_doc *doc = NULL; 619 620 json_cycle_ctx ctx = { 621 .js = js, 622 .replacer_func = js_mkundef(), 623 .replacer_arr = js_mkundef(), 624 .error = js_mkundef(), 625 .holder = js_mkundef(), 626 }; 627 628 char *json_str = NULL; 629 size_t len; 630 ant_value_t root_holder = js_mkundef(); 631 632 if (nargs < 1) return js_mkerr(js, "JSON.stringify() requires at least 1 argument"); 633 gc_temp_root_scope_begin(js, &ctx.temp_roots); 634 ctx.error_handle = gc_temp_root_add(&ctx.temp_roots, ctx.error); 635 ctx.holder_handle = gc_temp_root_add(&ctx.temp_roots, ctx.holder); 636 637 if (!gc_temp_root_handle_valid(ctx.error_handle) || !gc_temp_root_handle_valid(ctx.holder_handle)) { 638 gc_temp_root_scope_end(&ctx.temp_roots); 639 return json_stringify_oom(js); 640 } 641 642 if (!json_ctx_pin_value(&ctx, args[0])) { 643 result = ctx.error; 644 goto cleanup; 645 } 646 647 int top_type = vtype(args[0]); 648 649 if (nargs < 2 && top_type == T_STR) { 650 size_t byte_len = 0; 651 size_t raw_len = 0; 652 653 char *str = js_getstr(js, args[0], &byte_len); 654 char *raw = utf8_json_quote(str, byte_len, &raw_len); 655 656 if (!raw) { 657 result = js_mkerr(js, "JSON.stringify() failed: out of memory"); 658 goto cleanup; 659 } 660 result = js_mkstr(js, raw, raw_len); 661 free(raw); 662 goto cleanup; 663 } 664 665 if (nargs >= 2) { 666 ant_value_t replacer = args[1]; 667 if (is_callable(replacer)) { 668 ctx.replacer_func = replacer; 669 if (!json_ctx_pin_value(&ctx, replacer)) { 670 result = ctx.error; 671 goto cleanup; 672 }} 673 674 else if (is_special_object(replacer)) { 675 ant_value_t len_val = js_get(js, replacer, "length"); 676 677 if (vtype(len_val) == T_NUM) { 678 ctx.replacer_arr = replacer; 679 ctx.replacer_arr_len = (int)js_getnum(len_val); 680 if (!json_ctx_pin_value(&ctx, replacer)) { 681 result = ctx.error; 682 goto cleanup; 683 } 684 }}} 685 686 doc = yyjson_mut_doc_new(NULL); 687 if (!doc) { 688 result = js_mkerr(js, "JSON.stringify() failed: out of memory"); 689 goto cleanup; 690 } 691 692 root_holder = json_create_root_holder(js, args[0], &ctx); 693 if (is_err(root_holder)) { 694 result = root_holder; 695 goto cleanup; 696 } 697 698 if (vtype(root_holder) == T_UNDEF && vtype(ctx.error) != T_UNDEF) { 699 result = ctx.error; 700 goto cleanup; 701 } 702 703 json_set_holder(&ctx, root_holder); 704 yyjson_mut_val *root = ant_value_to_yyjson(js, doc, args[0], &ctx); 705 706 if (vtype(ctx.error) != T_UNDEF) { 707 ant_value_t error = json_normalize_error(ctx.error); 708 result = is_err(error) ? error : js_throw(js, error); 709 goto cleanup; 710 } 711 712 if (ctx.has_cycle) { 713 result = js_mkerr_typed(js, JS_ERR_TYPE, "Converting circular structure to JSON"); 714 goto cleanup; 715 } 716 717 if (root == YYJSON_SKIP_VALUE) { 718 result = js_mkundef(); 719 goto cleanup; 720 } 721 722 yyjson_mut_doc_set_root(doc, root); 723 json_str = yyjson_mut_write(doc, get_write_flags(args, nargs), &len); 724 725 if (!json_str) { 726 result = js_mkerr(js, "JSON.stringify() failed: write error"); 727 goto cleanup; 728 } 729 730 result = js_mkstr(js, json_str, len); 731 732cleanup: 733 free(json_str); 734 free(ctx.stack); 735 yyjson_mut_doc_free(doc); 736 gc_temp_root_scope_end(&ctx.temp_roots); 737 return result; 738} 739 740ant_value_t json_stringify_value(ant_t *js, ant_value_t value) { 741 ant_value_t args[1] = { value }; 742 return js_json_stringify(js, args, 1); 743} 744 745void init_json_module() { 746 ant_t *js = rt->js; 747 ant_value_t json_obj = js_mkobj(js); 748 749 js_set(js, json_obj, "parse", js_mkfun(js_json_parse)); 750 js_set(js, json_obj, "stringify", js_mkfun(js_json_stringify)); 751 752 js_set_sym(js, json_obj, get_toStringTag_sym(), js_mkstr(js, "JSON", 4)); 753 js_set(js, js_glob(js), "JSON", json_obj); 754}