Merge branch 'quicken_mcode' into gen_dylib

This commit is contained in:
2026-02-16 00:35:40 -06:00
29 changed files with 160180 additions and 877679 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -588,7 +588,8 @@ JSValue JS_ParseJSON (JSContext *ctx, const char *buf, size_t buf_len,
JSValue JS_ParseJSON2 (JSContext *ctx, const char *buf, size_t buf_len,
const char *filename, int flags);
JSValue JS_JSONStringify (JSContext *ctx, JSValue obj,
JSValue replacer, JSValue space0);
JSValue replacer, JSValue space0,
JS_BOOL compact_arrays);
/* ============================================================
9. Intrinsic Wrappers (JS_Cell* / JS_Array*)

View File

@@ -1492,6 +1492,10 @@ void gc_scan_object (JSContext *ctx, void *ptr, uint8_t *from_base, uint8_t *fro
allow_grow: if true, grow heap when recovery is poor
alloc_size: the allocation that triggered GC — used to size the new block */
int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
#ifdef DUMP_GC_TIMING
struct timespec gc_t0, gc_t1;
clock_gettime(CLOCK_MONOTONIC, &gc_t0);
#endif
JSRuntime *rt = ctx->rt;
size_t old_used = ctx->heap_free - ctx->heap_base;
size_t old_heap_size = ctx->current_block_size;
@@ -1692,6 +1696,16 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
ctx->gc_bytes_copied += new_used;
size_t recovered = old_used > new_used ? old_used - new_used : 0;
#ifdef DUMP_GC_TIMING
clock_gettime(CLOCK_MONOTONIC, &gc_t1);
double gc_ms = (gc_t1.tv_sec - gc_t0.tv_sec) * 1000.0 +
(gc_t1.tv_nsec - gc_t0.tv_nsec) / 1e6;
fprintf(stderr, "GC #%u: %.2f ms | copied %zu KB | old %zu KB -> new %zu KB | recovered %zu KB (%.0f%%)\n",
ctx->gc_count, gc_ms,
new_used / 1024, old_used / 1024, new_size / 1024,
recovered / 1024,
old_used > 0 ? (100.0 * recovered / old_used) : 0.0);
#endif
ctx->heap_base = to_base;
ctx->heap_free = to_free;
@@ -5614,6 +5628,8 @@ typedef struct JSONStringifyContext {
JSValue gap;
JSValue empty;
JSGCRef b_root; /* GC root for buffer - use JSC_B_GET/SET macros */
BOOL compact_arrays;
BOOL in_compact_array;
} JSONStringifyContext;
/* Macros to access the buffer from the rooted JSValue */
@@ -5718,7 +5734,7 @@ static int js_json_to_str (JSContext *ctx, JSONStringifyContext *jsc, JSValue ho
}
indent1_ref.val = JS_ConcatString (ctx, indent_ref.val, jsc->gap);
if (JS_IsException (indent1_ref.val)) goto exception;
if (!JS_IsEmptyString (jsc->gap)) {
if (!JS_IsEmptyString (jsc->gap) && !jsc->in_compact_array) {
sep_ref.val = JS_ConcatString3 (ctx, "\n", indent1_ref.val, "");
if (JS_IsException (sep_ref.val)) goto exception;
sep1_ref.val = js_new_string8 (ctx, " ");
@@ -5733,12 +5749,49 @@ static int js_json_to_str (JSContext *ctx, JSONStringifyContext *jsc, JSValue ho
if (ret < 0) goto exception;
if (ret) {
if (js_get_length64 (ctx, &len, val_ref.val)) goto exception;
/* Check if this is a leaf array for compact mode.
Leaf = no element is an array, and no element is an object
that has array-valued properties. */
BOOL was_compact = jsc->in_compact_array;
if (jsc->compact_arrays && !jsc->in_compact_array && !JS_IsEmptyString (jsc->gap)) {
BOOL is_leaf = TRUE;
for (i = 0; i < len && is_leaf; i++) {
v = JS_GetPropertyNumber (ctx, val_ref.val, i);
if (JS_IsException (v)) goto exception;
if (JS_IsArray (v) > 0) {
is_leaf = FALSE;
} else if (mist_is_gc_object (v) && !JS_IsText (v)) {
/* Element is an object — check if any property is an array */
v_ref.val = v;
prop_ref.val = JS_GetOwnPropertyNames (ctx, v_ref.val);
if (!JS_IsException (prop_ref.val)) {
int64_t nprops;
if (!js_get_length64 (ctx, &nprops, prop_ref.val)) {
for (int64_t j = 0; j < nprops && is_leaf; j++) {
JSValue key = JS_GetPropertyNumber (ctx, prop_ref.val, j);
if (!JS_IsException (key)) {
JSValue pval = JS_GetPropertyValue (ctx, v_ref.val, key);
if (JS_IsArray (pval) > 0) is_leaf = FALSE;
}
}
}
}
v_ref.val = JS_NULL;
prop_ref.val = JS_NULL;
}
}
if (is_leaf) jsc->in_compact_array = TRUE;
}
JSC_B_PUTC (jsc, '[');
for (i = 0; i < len; i++) {
if (i > 0) {
JSC_B_PUTC (jsc, ',');
}
JSC_B_CONCAT (jsc, sep_ref.val);
if (jsc->in_compact_array && !was_compact) {
if (i > 0) JSC_B_PUTC (jsc, ' ');
} else {
JSC_B_CONCAT (jsc, sep_ref.val);
}
v = JS_GetPropertyNumber (ctx, val_ref.val, i);
if (JS_IsException (v)) goto exception;
v_ref.val = v; /* root v — JS_ToString below can trigger GC */
@@ -5751,11 +5804,12 @@ static int js_json_to_str (JSContext *ctx, JSONStringifyContext *jsc, JSValue ho
if (JS_IsNull (v)) v = JS_NULL;
if (js_json_to_str (ctx, jsc, val_ref.val, v, indent1_ref.val)) goto exception;
}
if (len > 0 && !JS_IsEmptyString (jsc->gap)) {
if (len > 0 && !JS_IsEmptyString (jsc->gap) && !jsc->in_compact_array) {
JSC_B_PUTC (jsc, '\n');
JSC_B_CONCAT (jsc, indent_ref.val);
}
JSC_B_PUTC (jsc, ']');
jsc->in_compact_array = was_compact;
} else {
if (!JS_IsNull (jsc->property_list))
tab_ref.val = jsc->property_list;
@@ -5789,7 +5843,7 @@ static int js_json_to_str (JSContext *ctx, JSONStringifyContext *jsc, JSValue ho
has_content = TRUE;
}
}
if (has_content && !JS_IsEmptyString (jsc->gap)) {
if (has_content && !JS_IsEmptyString (jsc->gap) && !jsc->in_compact_array) {
JSC_B_PUTC (jsc, '\n');
JSC_B_CONCAT (jsc, indent_ref.val);
}
@@ -5853,7 +5907,7 @@ exception:
return -1;
}
JSValue JS_JSONStringify (JSContext *ctx, JSValue obj, JSValue replacer, JSValue space0) {
JSValue JS_JSONStringify (JSContext *ctx, JSValue obj, JSValue replacer, JSValue space0, BOOL compact_arrays) {
JSONStringifyContext jsc_s, *jsc = &jsc_s;
JSValue val, v, space, ret, wrapper;
int res;
@@ -5871,6 +5925,8 @@ JSValue JS_JSONStringify (JSContext *ctx, JSValue obj, JSValue replacer, JSValue
jsc->property_list = JS_NULL;
jsc->gap = JS_NULL;
jsc->empty = JS_KEY_empty;
jsc->compact_arrays = compact_arrays;
jsc->in_compact_array = FALSE;
ret = JS_NULL;
wrapper = JS_NULL;
@@ -11146,9 +11202,14 @@ static JSValue js_cell_json_encode (JSContext *ctx, JSValue this_val, int argc,
if (argc < 1)
return JS_ThrowTypeError (ctx, "json.encode requires at least 1 argument");
JSValue replacer = argc > 1 ? argv[1] : JS_NULL;
JSValue space = argc > 2 ? argv[2] : JS_NewInt32 (ctx, 1);
JSValue result = JS_JSONStringify (ctx, argv[0], replacer, space);
BOOL pretty = argc <= 1 || JS_ToBool (ctx, argv[1]);
JSValue space = pretty ? JS_NewInt32 (ctx, 2) : JS_NULL;
JSValue replacer = JS_NULL;
if (argc > 2 && JS_IsFunction (argv[2]))
replacer = argv[2];
else if (argc > 3 && JS_IsArray (argv[3]))
replacer = argv[3];
JSValue result = JS_JSONStringify (ctx, argv[0], replacer, space, pretty);
return result;
}

View File

@@ -1428,7 +1428,7 @@ TEST(stringify_json_object) {
obj_ref.val = JS_NewObject(ctx);
JSValue v1 = JS_NewInt32(ctx, 1);
JS_SetPropertyStr(ctx, obj_ref.val, "a", v1);
JSValue str = JS_JSONStringify(ctx, obj_ref.val, JS_NULL, JS_NULL);
JSValue str = JS_JSONStringify(ctx, obj_ref.val, JS_NULL, JS_NULL, 0);
JS_PopGCRef(ctx, &obj_ref);
ASSERT(JS_IsText(str));
const char *s = JS_ToCString(ctx, str);
@@ -1444,7 +1444,7 @@ TEST(stringify_json_array) {
arr_ref.val = JS_NewArray(ctx);
JS_ArrayPush(ctx, &arr_ref.val, JS_NewInt32(ctx, 1));
JS_ArrayPush(ctx, &arr_ref.val, JS_NewInt32(ctx, 2));
JSValue str = JS_JSONStringify(ctx, arr_ref.val, JS_NULL, JS_NULL);
JSValue str = JS_JSONStringify(ctx, arr_ref.val, JS_NULL, JS_NULL, 0);
JS_PopGCRef(ctx, &arr_ref);
ASSERT(JS_IsText(str));
const char *s = JS_ToCString(ctx, str);