fixing gc bugs; nearly idempotent

This commit is contained in:
2026-02-15 13:14:26 -06:00
parent 7de20b39da
commit ebd624b772
22 changed files with 656663 additions and 184850 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1607,7 +1607,7 @@
[
"get",
3,
27,
15,
1,
1,
1
@@ -1892,7 +1892,7 @@
[
"get",
3,
27,
15,
1,
1,
1
@@ -3354,7 +3354,7 @@
[
"get",
14,
42,
43,
1,
249,
22
@@ -3369,7 +3369,7 @@
[
"get",
15,
42,
43,
1,
250,
18
@@ -3490,7 +3490,7 @@
[
"get",
24,
59,
60,
1,
255,
20
@@ -3536,7 +3536,7 @@
[
"get",
27,
59,
60,
1,
258,
16
@@ -4606,7 +4606,7 @@
[
"record",
5,
0
5
],
[
"access",
@@ -4778,7 +4778,7 @@
[
"record",
5,
0
5
],
[
"access",
@@ -4950,7 +4950,7 @@
[
"record",
5,
0
5
],
[
"access",
@@ -5122,7 +5122,7 @@
[
"record",
5,
0
5
],
[
"access",
@@ -5294,7 +5294,7 @@
[
"record",
5,
0
5
],
[
"access",
@@ -5466,7 +5466,7 @@
[
"record",
5,
0
5
],
[
"access",
@@ -7829,7 +7829,7 @@
[
"get",
6,
44,
45,
1,
470,
15
@@ -8016,7 +8016,7 @@
[
"put",
5,
44,
45,
1,
478,
48
@@ -8024,7 +8024,7 @@
[
"get",
7,
71,
28,
1,
478,
58
@@ -8118,7 +8118,7 @@
[
"put",
5,
44,
45,
1,
479,
48
@@ -8126,7 +8126,7 @@
[
"get",
7,
71,
28,
1,
479,
58
@@ -8220,7 +8220,7 @@
[
"put",
5,
44,
45,
1,
480,
48
@@ -8228,7 +8228,7 @@
[
"get",
7,
71,
28,
1,
480,
58
@@ -8322,7 +8322,7 @@
[
"put",
5,
44,
45,
1,
481,
48
@@ -8330,7 +8330,7 @@
[
"get",
7,
71,
28,
1,
481,
58
@@ -8424,7 +8424,7 @@
[
"put",
5,
44,
45,
1,
482,
48
@@ -8432,7 +8432,7 @@
[
"get",
7,
71,
28,
1,
482,
58
@@ -8526,7 +8526,7 @@
[
"put",
5,
44,
45,
1,
483,
48
@@ -8534,7 +8534,7 @@
[
"get",
7,
71,
28,
1,
483,
58
@@ -8621,7 +8621,7 @@
[
"get",
7,
44,
45,
1,
488,
16
@@ -8844,7 +8844,7 @@
[
"put",
5,
44,
45,
1,
498,
47
@@ -8867,7 +8867,7 @@
[
"get",
8,
7,
8,
1,
498,
73
@@ -8961,7 +8961,7 @@
[
"put",
5,
44,
45,
1,
499,
47
@@ -8984,7 +8984,7 @@
[
"get",
8,
7,
8,
1,
499,
73
@@ -9078,7 +9078,7 @@
[
"put",
5,
44,
45,
1,
500,
47
@@ -9101,7 +9101,7 @@
[
"get",
8,
7,
8,
1,
500,
74
@@ -9195,7 +9195,7 @@
[
"put",
5,
44,
45,
1,
501,
47
@@ -9218,7 +9218,7 @@
[
"get",
8,
7,
8,
1,
501,
74
@@ -9312,7 +9312,7 @@
[
"put",
5,
44,
45,
1,
502,
47
@@ -9335,7 +9335,7 @@
[
"get",
8,
7,
8,
1,
502,
74
@@ -9429,7 +9429,7 @@
[
"put",
5,
44,
45,
1,
503,
47
@@ -9452,7 +9452,7 @@
[
"get",
8,
7,
8,
1,
503,
74
@@ -9986,9 +9986,8 @@
}
],
"main": {
"nr_args": 0,
"nr_slots": 143,
"nr_close_slots": 0,
"nr_slots": 142,
"instructions": [
[
"access",
@@ -9999,35 +9998,35 @@
],
[
"access",
42,
43,
3,
11,
16
],
[
"access",
59,
60,
35,
12,
15
],
[
"access",
43,
44,
15,
13,
20
],
[
"access",
61,
41,
27,
14,
21
],
[
"null",
44,
45,
17,
12
],
@@ -10045,7 +10044,7 @@
],
[
"access",
27,
15,
4503599627370495,
23,
21
@@ -10073,7 +10072,7 @@
],
[
"move",
15,
13,
75,
35,
17
@@ -10087,7 +10086,7 @@
],
[
"move",
53,
54,
76,
44,
15
@@ -10115,7 +10114,7 @@
],
[
"move",
48,
49,
78,
56,
20
@@ -10143,7 +10142,7 @@
],
[
"move",
65,
61,
80,
68,
19
@@ -10199,7 +10198,7 @@
],
[
"move",
13,
12,
84,
125,
15
@@ -10213,7 +10212,7 @@
],
[
"move",
12,
71,
85,
132,
19
@@ -10241,7 +10240,7 @@
],
[
"move",
41,
42,
87,
188,
15
@@ -10255,7 +10254,7 @@
],
[
"move",
60,
62,
88,
195,
16
@@ -10297,7 +10296,7 @@
],
[
"move",
5,
4,
91,
218,
11
@@ -10325,7 +10324,7 @@
],
[
"move",
50,
51,
93,
228,
11
@@ -10381,7 +10380,7 @@
],
[
"move",
62,
63,
97,
338,
10
@@ -10395,7 +10394,7 @@
],
[
"move",
45,
46,
98,
343,
10
@@ -10423,7 +10422,7 @@
],
[
"move",
63,
64,
100,
353,
10
@@ -10437,7 +10436,7 @@
],
[
"move",
64,
65,
101,
358,
10
@@ -10451,7 +10450,7 @@
],
[
"move",
51,
52,
102,
368,
11
@@ -10479,7 +10478,7 @@
],
[
"move",
28,
27,
104,
380,
11
@@ -10507,7 +10506,7 @@
],
[
"move",
54,
55,
106,
396,
12
@@ -10535,7 +10534,7 @@
],
[
"move",
56,
57,
108,
411,
11
@@ -10549,7 +10548,7 @@
],
[
"move",
47,
48,
109,
416,
12
@@ -10563,7 +10562,7 @@
],
[
"move",
52,
53,
110,
421,
11
@@ -10605,7 +10604,7 @@
],
[
"move",
4,
5,
113,
442,
14
@@ -10703,7 +10702,7 @@
],
[
"move",
8,
7,
120,
465,
14
@@ -10717,7 +10716,7 @@
],
[
"move",
71,
28,
121,
469,
17
@@ -10773,7 +10772,7 @@
],
[
"move",
57,
58,
125,
481,
16
@@ -10801,7 +10800,7 @@
],
[
"move",
46,
47,
127,
483,
16
@@ -10815,7 +10814,7 @@
],
[
"move",
7,
8,
128,
487,
16
@@ -10913,7 +10912,7 @@
],
[
"move",
55,
56,
135,
506,
15
@@ -10927,7 +10926,7 @@
],
[
"move",
49,
50,
136,
514,
15
@@ -10941,7 +10940,7 @@
],
[
"move",
58,
59,
137,
523,
20
@@ -10949,7 +10948,7 @@
[
"record",
138,
0
65
],
[
"access",
@@ -10969,7 +10968,7 @@
[
"store_field",
138,
42,
43,
"js_false",
538,
13
@@ -10977,7 +10976,7 @@
[
"store_field",
138,
59,
60,
"js_true",
539,
12
@@ -10997,10 +10996,17 @@
540,
17
],
[
"access",
141,
27,
541,
18
],
[
"store_field",
138,
61,
141,
"js_empty_text",
541,
18
@@ -11016,7 +11022,7 @@
[
"store_field",
138,
15,
13,
"is_number",
544,
14
@@ -11024,7 +11030,7 @@
[
"store_field",
138,
53,
54,
"is_null",
545,
12
@@ -11040,7 +11046,7 @@
[
"store_field",
138,
48,
49,
"is_exception",
547,
17
@@ -11056,7 +11062,7 @@
[
"store_field",
138,
65,
61,
"is_imm_text",
549,
16
@@ -11088,7 +11094,7 @@
[
"store_field",
138,
13,
12,
"get_ptr",
554,
12
@@ -11096,7 +11102,7 @@
[
"store_field",
138,
12,
71,
"get_float64",
555,
16
@@ -11112,7 +11118,7 @@
[
"store_field",
138,
41,
42,
"new_int",
558,
12
@@ -11120,7 +11126,7 @@
[
"store_field",
138,
60,
62,
"new_bool",
559,
13
@@ -11144,7 +11150,7 @@
[
"store_field",
138,
5,
4,
"sub",
563,
8
@@ -11160,7 +11166,7 @@
[
"store_field",
138,
50,
51,
"div",
565,
8
@@ -11184,7 +11190,7 @@
[
"store_field",
138,
62,
63,
"ne",
569,
7
@@ -11192,7 +11198,7 @@
[
"store_field",
138,
45,
46,
"lt",
570,
7
@@ -11208,7 +11214,7 @@
[
"store_field",
138,
63,
64,
"gt",
572,
7
@@ -11216,7 +11222,7 @@
[
"store_field",
138,
64,
65,
"ge",
573,
7
@@ -11224,7 +11230,7 @@
[
"store_field",
138,
51,
52,
"neg",
575,
8
@@ -11240,7 +11246,7 @@
[
"store_field",
138,
28,
27,
"dec",
577,
8
@@ -11256,7 +11262,7 @@
[
"store_field",
138,
54,
55,
"bnot",
579,
9
@@ -11272,7 +11278,7 @@
[
"store_field",
138,
56,
57,
"bor",
582,
8
@@ -11280,7 +11286,7 @@
[
"store_field",
138,
47,
48,
"bxor",
583,
9
@@ -11288,7 +11294,7 @@
[
"store_field",
138,
52,
53,
"shl",
584,
8
@@ -11312,7 +11318,7 @@
[
"store_field",
138,
4,
5,
"concat",
588,
11
@@ -11360,7 +11366,7 @@
[
"store_field",
138,
8,
7,
"ge_int",
595,
11
@@ -11392,7 +11398,7 @@
[
"store_field",
138,
57,
58,
"le_float",
600,
13
@@ -11408,7 +11414,7 @@
[
"store_field",
138,
46,
47,
"ge_float",
602,
13
@@ -11464,7 +11470,7 @@
[
"store_field",
138,
55,
56,
"eq_bool",
611,
12
@@ -11472,7 +11478,7 @@
[
"store_field",
138,
49,
50,
"ne_bool",
612,
12
@@ -11480,7 +11486,7 @@
[
"store_field",
138,
58,
59,
"is_identical",
614,
17
@@ -11493,7 +11499,8 @@
],
"_nop_ur_1",
"_nop_ur_2"
]
],
"nr_args": 0
},
"filename": "qbe.cm",
"data": {}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,19 +2,18 @@
"name": "toolchains.cm",
"functions": [],
"main": {
"nr_args": 0,
"nr_close_slots": 0,
"nr_slots": 304,
"nr_close_slots": 0,
"instructions": [
[
"record",
1,
0
22
],
[
"record",
2,
0
12
],
[
"access",
@@ -218,7 +217,7 @@
[
"array",
20,
0,
7,
13,
132
],
@@ -324,7 +323,7 @@
[
"array",
27,
0,
6,
14,
107
],
@@ -389,7 +388,7 @@
[
"record",
28,
0
10
],
[
"access",
@@ -535,7 +534,7 @@
[
"array",
40,
0,
3,
25,
74
],
@@ -578,7 +577,7 @@
[
"array",
42,
0,
1,
26,
19
],
@@ -608,7 +607,7 @@
[
"record",
43,
0
11
],
[
"access",
@@ -786,7 +785,7 @@
[
"record",
55,
0
11
],
[
"access",
@@ -964,7 +963,7 @@
[
"record",
67,
0
10
],
[
"access",
@@ -1127,7 +1126,7 @@
[
"record",
78,
0
10
],
[
"access",
@@ -1290,7 +1289,7 @@
[
"record",
89,
0
10
],
[
"access",
@@ -1429,7 +1428,7 @@
[
"array",
100,
0,
2,
87,
27
],
@@ -1472,7 +1471,7 @@
[
"array",
103,
0,
2,
88,
32
],
@@ -1509,7 +1508,7 @@
[
"record",
104,
0
10
],
[
"access",
@@ -1648,7 +1647,7 @@
[
"array",
115,
0,
2,
99,
27
],
@@ -1691,7 +1690,7 @@
[
"array",
118,
0,
2,
100,
32
],
@@ -1728,7 +1727,7 @@
[
"record",
119,
0
10
],
[
"access",
@@ -1874,7 +1873,7 @@
[
"array",
131,
0,
3,
111,
129
],
@@ -1931,7 +1930,7 @@
[
"array",
135,
0,
3,
112,
134
],
@@ -1975,7 +1974,7 @@
[
"record",
136,
0
10
],
[
"access",
@@ -2114,7 +2113,7 @@
[
"array",
147,
0,
2,
123,
27
],
@@ -2157,7 +2156,7 @@
[
"array",
150,
0,
2,
124,
32
],
@@ -2194,7 +2193,7 @@
[
"record",
151,
0
10
],
[
"access",
@@ -2333,7 +2332,7 @@
[
"array",
162,
0,
2,
135,
27
],
@@ -2376,7 +2375,7 @@
[
"array",
165,
0,
2,
136,
32
],
@@ -2413,7 +2412,7 @@
[
"record",
166,
0
10
],
[
"access",
@@ -2559,7 +2558,7 @@
[
"array",
178,
0,
3,
147,
131
],
@@ -2616,7 +2615,7 @@
[
"array",
182,
0,
3,
148,
136
],
@@ -2660,7 +2659,7 @@
[
"record",
183,
0
10
],
[
"access",
@@ -2799,7 +2798,7 @@
[
"array",
194,
0,
2,
159,
27
],
@@ -2842,7 +2841,7 @@
[
"array",
197,
0,
2,
160,
32
],
@@ -2879,7 +2878,7 @@
[
"record",
198,
0
10
],
[
"access",
@@ -3018,7 +3017,7 @@
[
"array",
209,
0,
2,
171,
27
],
@@ -3061,7 +3060,7 @@
[
"array",
212,
0,
2,
172,
32
],
@@ -3098,7 +3097,7 @@
[
"record",
213,
0
10
],
[
"access",
@@ -3244,7 +3243,7 @@
[
"array",
225,
0,
3,
183,
127
],
@@ -3301,7 +3300,7 @@
[
"array",
229,
0,
3,
184,
132
],
@@ -3345,7 +3344,7 @@
[
"record",
230,
0
10
],
[
"access",
@@ -3484,7 +3483,7 @@
[
"array",
241,
0,
2,
195,
27
],
@@ -3527,7 +3526,7 @@
[
"array",
244,
0,
2,
196,
32
],
@@ -3564,7 +3563,7 @@
[
"record",
245,
0
10
],
[
"access",
@@ -3703,7 +3702,7 @@
[
"array",
256,
0,
2,
207,
27
],
@@ -3746,7 +3745,7 @@
[
"array",
259,
0,
2,
208,
32
],
@@ -3783,7 +3782,7 @@
[
"record",
260,
0
10
],
[
"access",
@@ -3922,7 +3921,7 @@
[
"array",
271,
0,
2,
219,
27
],
@@ -3965,7 +3964,7 @@
[
"array",
274,
0,
2,
220,
32
],
@@ -4002,7 +4001,7 @@
[
"record",
275,
0
10
],
[
"access",
@@ -4141,7 +4140,7 @@
[
"array",
286,
0,
2,
231,
27
],
@@ -4184,7 +4183,7 @@
[
"array",
289,
0,
2,
232,
32
],
@@ -4221,7 +4220,7 @@
[
"record",
290,
0
10
],
[
"access",
@@ -4415,7 +4414,8 @@
],
"_nop_ur_1",
"_nop_ur_2"
]
],
"nr_args": 0
},
"filename": "toolchains.cm",
"data": {}

File diff suppressed because it is too large Load Diff

View File

@@ -2062,7 +2062,7 @@ var parse = function(tokens, src, filename, tokenizer) {
enclosing = sem_find_func_scope(scope)
if (enclosing != null) enclosing.has_inner_func = true
name = stmt.name
if (name != null) sem_add_var(scope, name, {make: "function", fn_nr: scope.function_nr})
if (name != null && sem_find_var(scope, name) == null) sem_add_var(scope, name, {make: "function", fn_nr: scope.function_nr})
fn_nr_val = stmt.function_nr
if (fn_nr_val == null) fn_nr_val = scope.function_nr
fn_scope = make_scope(scope, fn_nr_val, {is_func: true})

View File

@@ -774,15 +774,31 @@ void *js_malloc (JSContext *ctx, size_t size) {
size = (size + 7) & ~7;
#ifdef FORCE_GC_AT_MALLOC
/* Force GC on every allocation for testing - but don't grow heap unless needed */
int need_space = (uint8_t *)ctx->heap_free + size > (uint8_t *)ctx->heap_end;
if (ctx_gc(ctx, need_space, size) < 0) {
JS_ThrowOutOfMemory(ctx);
return NULL;
}
if ((uint8_t *)ctx->heap_free + size > (uint8_t *)ctx->heap_end) {
JS_ThrowOutOfMemory(ctx);
return NULL;
/* Force GC on every allocation for testing */
{
int need_space = (uint8_t *)ctx->heap_free + size > (uint8_t *)ctx->heap_end;
if (ctx_gc(ctx, need_space, size) < 0) {
JS_ThrowOutOfMemory(ctx);
return NULL;
}
/* If still no room after GC, grow and retry (same logic as normal path) */
if ((uint8_t *)ctx->heap_free + size > (uint8_t *)ctx->heap_end) {
size_t live = (size_t)((uint8_t *)ctx->heap_free - (uint8_t *)ctx->heap_base);
size_t need = live + size;
size_t ns = ctx->current_block_size;
while (ns < need && ns < buddy_max_block(&ctx->rt->buddy))
ns *= 2;
ctx->next_block_size = ns;
if (ctx_gc (ctx, 1, size) < 0) {
JS_ThrowOutOfMemory (ctx);
return NULL;
}
ctx->next_block_size = ctx->current_block_size;
if ((uint8_t *)ctx->heap_free + size > (uint8_t *)ctx->heap_end) {
JS_ThrowOutOfMemory (ctx);
return NULL;
}
}
}
#else
/* Check if we have space in current block */
@@ -1700,11 +1716,18 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
#endif
/* If <40% recovered, grow next block size for future allocations.
First poor recovery: double. Consecutive poor: quadruple. */
First poor recovery: double. Consecutive poor: quadruple.
Skip under FORCE_GC_AT_MALLOC — forced GC on every allocation creates
artificially poor recovery (no time to accumulate garbage), which
would cause runaway exponential heap growth. */
#ifdef DUMP_GC
int will_grow = 0;
#endif
#ifdef FORCE_GC_AT_MALLOC
if (0) {
#else
if (allow_grow && recovered > 0 && old_used > 0 && recovered < old_used * 2 / 5) {
#endif
size_t factor = ctx->gc_poor_streak >= 1 ? 4 : 2;
size_t grown = new_size * factor;
if (grown <= buddy_max_block(&ctx->rt->buddy)) {
@@ -2407,28 +2430,37 @@ JSValue JS_NewStringLen (JSContext *ctx, const char *buf, size_t buf_len) {
JSValue JS_ConcatString3 (JSContext *ctx, const char *str1, JSValue str2, const char *str3) {
JSText *b;
int len1, len3, str2_len;
JSGCRef str2_ref;
if (!JS_IsText (str2)) {
str2 = JS_ToString (ctx, str2);
if (JS_IsException (str2)) goto fail;
}
str2_len = js_string_value_len (str2);
/* Root str2 — pretext_init/pretext_write8/pretext_concat_value allocate
and can trigger GC, which would move the heap string str2 points to */
JS_PushGCRef (ctx, &str2_ref);
str2_ref.val = str2;
str2_len = js_string_value_len (str2_ref.val);
len1 = strlen (str1);
len3 = strlen (str3);
b = pretext_init (ctx, len1 + str2_len + len3);
if (!b) goto fail;
if (!b) goto fail_pop;
b = pretext_write8 (ctx, b, (const uint8_t *)str1, len1);
if (!b) goto fail;
b = pretext_concat_value (ctx, b, str2);
if (!b) goto fail;
if (!b) goto fail_pop;
b = pretext_concat_value (ctx, b, str2_ref.val);
if (!b) goto fail_pop;
b = pretext_write8 (ctx, b, (const uint8_t *)str3, len3);
if (!b) goto fail;
if (!b) goto fail_pop;
JS_PopGCRef (ctx, &str2_ref);
return pretext_end (ctx, b);
fail_pop:
JS_PopGCRef (ctx, &str2_ref);
fail:
return JS_EXCEPTION;
}
@@ -2555,13 +2587,25 @@ int js_string_compare_value_nocase (JSContext *ctx, JSValue op1, JSValue op2) {
JSValue JS_ConcatString (JSContext *ctx, JSValue op1, JSValue op2) {
if (unlikely (!JS_IsText (op1))) {
/* Root op2 across JS_ToString which can trigger GC */
JSGCRef op2_guard;
JS_PushGCRef (ctx, &op2_guard);
op2_guard.val = op2;
op1 = JS_ToString (ctx, op1);
op2 = op2_guard.val;
JS_PopGCRef (ctx, &op2_guard);
if (JS_IsException (op1)) {
return JS_EXCEPTION;
}
}
if (unlikely (!JS_IsText (op2))) {
/* Root op1 across JS_ToString which can trigger GC */
JSGCRef op1_guard;
JS_PushGCRef (ctx, &op1_guard);
op1_guard.val = op1;
op2 = JS_ToString (ctx, op2);
op1 = op1_guard.val;
JS_PopGCRef (ctx, &op1_guard);
if (JS_IsException (op2)) {
return JS_EXCEPTION;
}
@@ -3095,7 +3139,6 @@ JSValue JS_GetOwnPropertyNames (JSContext *ctx, JSValue obj) {
return JS_EXCEPTION;
}
/* Reading slots is GC-safe - no allocation */
JSRecord *rec = JS_VALUE_GET_OBJ (obj);
mask = (uint32_t)objhdr_cap56 (rec->mist_hdr);
@@ -3107,22 +3150,31 @@ JSValue JS_GetOwnPropertyNames (JSContext *ctx, JSValue obj) {
if (count == 0) return JS_NewArrayLen (ctx, 0);
/* Collect keys into stack buffer (JSValues are just uint64_t) */
JSValue *keys = alloca (count * sizeof (JSValue));
/* Root obj — JS_NewArrayLen allocates and can trigger GC, which
moves the record. Re-read keys from the moved record after. */
JSGCRef obj_ref;
JS_PushGCRef (ctx, &obj_ref);
obj_ref.val = obj;
JSValue arr = JS_NewArrayLen (ctx, count);
if (JS_IsException (arr)) {
JS_PopGCRef (ctx, &obj_ref);
return JS_EXCEPTION;
}
/* Re-read record pointer after possible GC */
rec = JS_VALUE_GET_OBJ (obj_ref.val);
mask = (uint32_t)objhdr_cap56 (rec->mist_hdr);
uint32_t idx = 0;
for (i = 1; i <= mask; i++) {
JSValue k = rec->slots[i].key;
if (JS_IsText (k)) keys[idx++] = k;
}
/* Now allocate and fill - GC point, but keys are on stack */
JSValue arr = JS_NewArrayLen (ctx, count);
if (JS_IsException (arr)) return JS_EXCEPTION;
for (i = 0; i < count; i++) {
JS_SetPropertyNumber (ctx, arr, i, keys[i]);
if (JS_IsText (k)) {
JS_SetPropertyNumber (ctx, arr, idx++, k);
}
}
JS_PopGCRef (ctx, &obj_ref);
return arr;
}
@@ -4111,17 +4163,22 @@ static JSValue JS_ToStringCheckObject (JSContext *ctx, JSValue val) {
}
static JSValue JS_ToQuotedString (JSContext *ctx, JSValue val1) {
JSValue val;
int i, len;
uint32_t c;
JSText *b;
char buf[16];
JSGCRef val_ref;
val = JS_ToStringCheckObject (ctx, val1);
JSValue val = JS_ToStringCheckObject (ctx, val1);
if (JS_IsException (val)) return val;
/* Root val — pretext_init/pretext_putc allocate and can trigger GC,
which would move the heap string val points to */
JS_PushGCRef (ctx, &val_ref);
val_ref.val = val;
/* Use js_string_value_len to handle both immediate and heap strings */
len = js_string_value_len (val);
len = js_string_value_len (val_ref.val);
b = pretext_init (ctx, len + 2);
if (!b) goto fail;
@@ -4129,7 +4186,7 @@ static JSValue JS_ToQuotedString (JSContext *ctx, JSValue val1) {
b = pretext_putc (ctx, b, '\"');
if (!b) goto fail;
for (i = 0; i < len; i++) {
c = js_string_value_get (val, i);
c = js_string_value_get (val_ref.val, i);
switch (c) {
case '\t':
c = 't';
@@ -4168,8 +4225,10 @@ static JSValue JS_ToQuotedString (JSContext *ctx, JSValue val1) {
}
b = pretext_putc (ctx, b, '\"');
if (!b) goto fail;
JS_PopGCRef (ctx, &val_ref);
return pretext_end (ctx, b);
fail:
JS_PopGCRef (ctx, &val_ref);
return JS_EXCEPTION;
}
@@ -5484,7 +5543,10 @@ static JSValue cjson_to_jsvalue (JSContext *ctx, const cJSON *item) {
arr_ref.val = JS_NewArrayLen (ctx, n);
for (int i = 0; i < n; i++) {
cJSON *child = cJSON_GetArrayItem (item, i);
JS_SetPropertyNumber (ctx, arr_ref.val, i, cjson_to_jsvalue (ctx, child));
/* Evaluate recursive call before reading arr_ref.val — the call
allocates and can trigger GC which moves the array */
JSValue elem = cjson_to_jsvalue (ctx, child);
JS_SetPropertyNumber (ctx, arr_ref.val, i, elem);
}
JSValue result = arr_ref.val;
JS_DeleteGCRef (ctx, &arr_ref);
@@ -5495,7 +5557,8 @@ static JSValue cjson_to_jsvalue (JSContext *ctx, const cJSON *item) {
JS_AddGCRef (ctx, &obj_ref);
obj_ref.val = JS_NewObject (ctx);
for (cJSON *child = item->child; child; child = child->next) {
JS_SetPropertyStr (ctx, obj_ref.val, child->string, cjson_to_jsvalue (ctx, child));
JSValue val = cjson_to_jsvalue (ctx, child);
JS_SetPropertyStr (ctx, obj_ref.val, child->string, val);
}
JSValue result = obj_ref.val;
JS_DeleteGCRef (ctx, &obj_ref);
@@ -5776,6 +5839,7 @@ JSValue JS_JSONStringify (JSContext *ctx, JSValue obj, JSValue replacer, JSValue
int res;
int64_t i, j, n;
JSGCRef obj_ref;
JSLocalRef *saved_local_frame = JS_GetLocalFrame (ctx);
/* Root obj since GC can happen during stringify setup */
JS_PushGCRef (ctx, &obj_ref);
@@ -5790,6 +5854,18 @@ JSValue JS_JSONStringify (JSContext *ctx, JSValue obj, JSValue replacer, JSValue
ret = JS_NULL;
wrapper = JS_NULL;
/* Root all jsc fields that hold heap objects — GC can fire during
stringify and would move these objects without updating the struct */
JSLocalRef lr_stack, lr_plist, lr_gap, lr_replacer;
lr_stack.ptr = &jsc->stack;
JS_PushLocalRef (ctx, &lr_stack);
lr_plist.ptr = &jsc->property_list;
JS_PushLocalRef (ctx, &lr_plist);
lr_gap.ptr = &jsc->gap;
JS_PushLocalRef (ctx, &lr_gap);
lr_replacer.ptr = &jsc->replacer_func;
JS_PushLocalRef (ctx, &lr_replacer);
/* Root the buffer for GC safety */
JS_PushGCRef (ctx, &jsc->b_root);
{
@@ -5869,6 +5945,8 @@ exception:
done1:
done:
JS_PopGCRef (ctx, &jsc->b_root);
/* Restore local refs pushed for jsc fields */
ctx->top_local_ref = saved_local_frame;
JS_PopGCRef (ctx, &obj_ref);
return ret;
}
@@ -7020,9 +7098,14 @@ JSValue js_cell_text_codepoint (JSContext *ctx, JSValue this_val, int argc, JSVa
* file. */
static JSText *pt_concat_value_to_string_free (JSContext *ctx, JSText *b, JSValue v) {
JSGCRef s_ref;
JSValue s = JS_ToString (ctx, v);
if (JS_IsException (s)) return NULL;
b = pretext_concat_value (ctx, b, s);
/* Root s — pretext_concat_value can trigger GC and move the heap string */
JS_PushGCRef (ctx, &s_ref);
s_ref.val = s;
b = pretext_concat_value (ctx, b, s_ref.val);
JS_PopGCRef (ctx, &s_ref);
return b;
}

View File

@@ -226,6 +226,7 @@ var streamline = function(ir, log) {
// [pos1, type1, pos2, type2] for operand positions to merge.
// =========================================================
var param_rules = {
add: [2, T_NUM, 3, T_NUM],
subtract: [2, T_NUM, 3, T_NUM], multiply: [2, T_NUM, 3, T_NUM],
divide: [2, T_NUM, 3, T_NUM], modulo: [2, T_NUM, 3, T_NUM],
pow: [2, T_NUM, 3, T_NUM], negate: [2, T_NUM],