@scriptc/cli-darwin-x64 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/THIRD_PARTY_NOTICES +1 -1
- package/dist/bin/scriptc +0 -0
- package/dist/bin/scriptc.json +1 -1
- package/dist/lib/declarations/scriptc-node-fallback.d.ts +915 -215
- package/dist/lib/declarations/scriptc-overrides.d.ts +5 -1
- package/dist/lib/declarations/scriptc.d.ts +22 -2
- package/dist/lib/llvm/bin/scriptc-llvm-codegen +0 -0
- package/dist/lib/llvm/package.json +1 -1
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_util_compare.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_util_compare.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic-text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_util_compare.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_bigint.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_cycle.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_util_compare.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic-text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-x64/package.json +1 -1
- package/dist/lib/runtime-darwin-x64/runtime-pack.json +215 -215
- package/dist/lib/runtime-sources/package.json +1 -1
- package/dist/lib/runtime-sources/src/scr_array.c +317 -40
- package/dist/lib/runtime-sources/src/scr_async.test.ts +16 -6
- package/dist/lib/runtime-sources/src/scr_bigint.c +15 -0
- package/dist/lib/runtime-sources/src/scr_bytes.c +437 -216
- package/dist/lib/runtime-sources/src/scr_bytes.test.c +85 -0
- package/dist/lib/runtime-sources/src/scr_bytes_io.c +0 -34
- package/dist/lib/runtime-sources/src/scr_copying.c +12 -2
- package/dist/lib/runtime-sources/src/scr_cycle.c +84 -60
- package/dist/lib/runtime-sources/src/scr_json.c +494 -161
- package/dist/lib/runtime-sources/src/scr_json.test.c +323 -1
- package/dist/lib/runtime-sources/src/scr_json.test.ts +58 -25
- package/dist/lib/runtime-sources/src/scr_key.h +68 -0
- package/dist/lib/runtime-sources/src/scr_lib.c +177 -89
- package/dist/lib/runtime-sources/src/scr_map.c +268 -96
- package/dist/lib/runtime-sources/src/scr_number.c +2 -4
- package/dist/lib/runtime-sources/src/scr_numeric.h +25 -0
- package/dist/lib/runtime-sources/src/scr_numeric.test.c +37 -0
- package/dist/lib/runtime-sources/src/scr_numeric.test.ts +33 -0
- package/dist/lib/runtime-sources/src/scr_regex.c +255 -145
- package/dist/lib/runtime-sources/src/scr_runtime.h +76 -19
- package/dist/lib/runtime-sources/src/scr_string.c +184 -42
- package/package.json +1 -1
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* serializers (and the error messages here).
|
|
23
23
|
*/
|
|
24
24
|
#include "scr_runtime.h"
|
|
25
|
+
#include "scr_key.h"
|
|
25
26
|
|
|
26
27
|
static SCR_TL ScrDyn *scr_builtin_object_prototype;
|
|
27
28
|
|
|
@@ -159,7 +160,9 @@ void scr_jb_init(ScrJsonBuf *b) {
|
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
static void scr_jb_grow(ScrJsonBuf *b, size_t need) {
|
|
162
|
-
if (
|
|
163
|
+
if (need > SIZE_MAX - sizeof(ScrStr) - 1 - b->len) scr_json_oom();
|
|
164
|
+
size_t required = b->len + need;
|
|
165
|
+
if (required <= b->cap) return;
|
|
163
166
|
if (!b->data) { /* first allocation: len == 0 */
|
|
164
167
|
size_t cap = scr_jb_hint >= need ? scr_jb_hint : need;
|
|
165
168
|
ScrStr *s = scr_str_alloc_raw(0, cap);
|
|
@@ -168,7 +171,13 @@ static void scr_jb_grow(ScrJsonBuf *b, size_t need) {
|
|
|
168
171
|
return;
|
|
169
172
|
}
|
|
170
173
|
size_t cap = b->cap;
|
|
171
|
-
while (cap <
|
|
174
|
+
while (cap < required) {
|
|
175
|
+
if (cap > (SIZE_MAX - sizeof(ScrStr) - 1) / 2) {
|
|
176
|
+
cap = required;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
cap *= 2;
|
|
180
|
+
}
|
|
172
181
|
ScrStr *s = scr_str_regrow(SCR_JB_STR(b), cap);
|
|
173
182
|
b->data = s->data;
|
|
174
183
|
b->cap = cap;
|
|
@@ -186,7 +195,8 @@ void scr_jb_putc(ScrJsonBuf *b, char c) {
|
|
|
186
195
|
b->data[b->len++] = c;
|
|
187
196
|
}
|
|
188
197
|
|
|
189
|
-
|
|
198
|
+
void scr_jb_write(ScrJsonBuf *b, const char *s, size_t n) {
|
|
199
|
+
if (n == 0) return;
|
|
190
200
|
scr_jb_grow(b, n);
|
|
191
201
|
memcpy(b->data + b->len, s, n);
|
|
192
202
|
b->len += n;
|
|
@@ -215,18 +225,45 @@ void scr_jb_put_f64(ScrJsonBuf *b, double v) {
|
|
|
215
225
|
scr_jb_write(b, buf, n);
|
|
216
226
|
}
|
|
217
227
|
|
|
218
|
-
|
|
228
|
+
/* Find the next quote, backslash or control byte. memcpy keeps the word
|
|
229
|
+
* loads unaligned-safe, and the length guard never reads past the input.
|
|
230
|
+
* The masks only decide whether to inspect a word byte by byte: borrow
|
|
231
|
+
* propagation may mark extra lanes, so it must not determine a byte index.
|
|
232
|
+
* High UTF-8 bytes are ordinary data, independent of host byte order. */
|
|
233
|
+
static size_t scr_json_plain_bytes(const char *data, size_t len) {
|
|
234
|
+
const uint64_t ones = UINT64_C(0x0101010101010101);
|
|
235
|
+
const uint64_t high = UINT64_C(0x8080808080808080);
|
|
236
|
+
size_t i = 0;
|
|
237
|
+
while (len - i >= sizeof(uint64_t)) {
|
|
238
|
+
uint64_t word;
|
|
239
|
+
memcpy(&word, data + i, sizeof word);
|
|
240
|
+
uint64_t quotes = word ^ (ones * '"');
|
|
241
|
+
uint64_t slashes = word ^ (ones * '\\');
|
|
242
|
+
uint64_t special = ((quotes - ones) & ~quotes) |
|
|
243
|
+
((slashes - ones) & ~slashes) |
|
|
244
|
+
((word - ones * 0x20) & ~word);
|
|
245
|
+
if (special & high) break;
|
|
246
|
+
i += sizeof word;
|
|
247
|
+
}
|
|
248
|
+
while (i < len) {
|
|
249
|
+
unsigned char c = (unsigned char)data[i];
|
|
250
|
+
if (c < 0x20 || c == '"' || c == '\\') break;
|
|
251
|
+
i++;
|
|
252
|
+
}
|
|
253
|
+
return i;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
static void scr_jb_put_json_span(ScrJsonBuf *b, const char *data, size_t len) {
|
|
219
257
|
scr_jb_putc(b, '"');
|
|
220
258
|
/* Bulk-copy runs of unescaped bytes (UTF-8 passes through verbatim,
|
|
221
259
|
* like JS); escapes interrupt the run. */
|
|
222
|
-
size_t i = 0
|
|
223
|
-
while (i
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
scr_jb_write(b, s->data + i, run);
|
|
260
|
+
size_t i = 0;
|
|
261
|
+
while (i < len) {
|
|
262
|
+
size_t run = scr_json_plain_bytes(data + i, len - i);
|
|
263
|
+
scr_jb_write(b, data + i, run);
|
|
264
|
+
i += run;
|
|
265
|
+
if (i == len) break;
|
|
266
|
+
unsigned char c = (unsigned char)data[i++];
|
|
230
267
|
switch (c) {
|
|
231
268
|
case '"': scr_jb_puts(b, "\\\""); break;
|
|
232
269
|
case '\\': scr_jb_puts(b, "\\\\"); break;
|
|
@@ -241,13 +278,14 @@ void scr_jb_put_json_str(ScrJsonBuf *b, const ScrStr *s) {
|
|
|
241
278
|
scr_jb_puts(b, esc);
|
|
242
279
|
}
|
|
243
280
|
}
|
|
244
|
-
i += run + 1;
|
|
245
|
-
run = 0;
|
|
246
281
|
}
|
|
247
|
-
scr_jb_write(b, s->data + i, run);
|
|
248
282
|
scr_jb_putc(b, '"');
|
|
249
283
|
}
|
|
250
284
|
|
|
285
|
+
void scr_jb_put_json_str(ScrJsonBuf *b, const ScrStr *s) {
|
|
286
|
+
scr_jb_put_json_span(b, s->data, s->len);
|
|
287
|
+
}
|
|
288
|
+
|
|
251
289
|
ScrStr *scr_jb_finish(ScrJsonBuf *b) {
|
|
252
290
|
free(b->seen);
|
|
253
291
|
b->seen = NULL;
|
|
@@ -413,11 +451,110 @@ void scr_dyn_from_leave(void) {
|
|
|
413
451
|
* strict alloc/free balance.
|
|
414
452
|
*/
|
|
415
453
|
static SCR_TL ScrDyn *scr_builtin_array_prototype;
|
|
454
|
+
|
|
455
|
+
/* Property names are immutable and often repeated across simultaneously live
|
|
456
|
+
* objects. Entries own references to their bytes; this fixed weak table owns
|
|
457
|
+
* none. Collisions only reduce sharing, and the final release removes a weak
|
|
458
|
+
* entry before freeing storage. Long keys bypass lookup entirely. */
|
|
459
|
+
#define SCR_DYN_KEY_SHARE_SLOTS 256
|
|
460
|
+
#define SCR_DYN_KEY_SHARE_LENGTH 64
|
|
461
|
+
typedef struct {
|
|
462
|
+
size_t rc;
|
|
463
|
+
char bytes[];
|
|
464
|
+
} ScrDynKey;
|
|
465
|
+
typedef struct {
|
|
466
|
+
ScrDynKey *key;
|
|
467
|
+
size_t length;
|
|
468
|
+
} ScrDynKeySlot;
|
|
469
|
+
static SCR_TL ScrDynKeySlot scr_dyn_keys[SCR_DYN_KEY_SHARE_SLOTS];
|
|
470
|
+
#ifdef SCR_RC_AUDIT
|
|
471
|
+
static SCR_TL long scr_live_dyn_keys;
|
|
472
|
+
long scr_dyn_key_live_count(void) { return scr_live_dyn_keys; }
|
|
473
|
+
#endif
|
|
474
|
+
|
|
475
|
+
static size_t scr_dyn_key_slot(const char *key, size_t length) {
|
|
476
|
+
size_t hash = length;
|
|
477
|
+
for (size_t i = 0; i < length; i++) hash = hash * 31 + (unsigned char)key[i];
|
|
478
|
+
return hash % SCR_DYN_KEY_SHARE_SLOTS;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
static char *scr_dyn_key_copy(const char *key, size_t length) {
|
|
482
|
+
ScrDynKeySlot *slot = length <= SCR_DYN_KEY_SHARE_LENGTH
|
|
483
|
+
? &scr_dyn_keys[scr_dyn_key_slot(key, length)] : NULL;
|
|
484
|
+
if (slot && slot->key && slot->length == length &&
|
|
485
|
+
!memcmp(slot->key->bytes, key, length)) {
|
|
486
|
+
slot->key->rc++;
|
|
487
|
+
return slot->key->bytes;
|
|
488
|
+
}
|
|
489
|
+
if (length > SIZE_MAX - sizeof(ScrDynKey) - 1) scr_json_oom();
|
|
490
|
+
ScrDynKey *owned = malloc(sizeof *owned + length + 1);
|
|
491
|
+
if (!owned) scr_json_oom();
|
|
492
|
+
#ifdef SCR_RC_AUDIT
|
|
493
|
+
scr_live_dyn_keys++;
|
|
494
|
+
#endif
|
|
495
|
+
owned->rc = 1;
|
|
496
|
+
memcpy(owned->bytes, key, length);
|
|
497
|
+
owned->bytes[length] = '\0';
|
|
498
|
+
if (slot) *slot = (ScrDynKeySlot){owned, length};
|
|
499
|
+
return owned->bytes;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
static void scr_dyn_key_release(char *key, size_t length) {
|
|
503
|
+
if (!key) return;
|
|
504
|
+
ScrDynKey *owned = (ScrDynKey *)(key - offsetof(ScrDynKey, bytes));
|
|
505
|
+
if (--owned->rc) return;
|
|
506
|
+
if (length <= SCR_DYN_KEY_SHARE_LENGTH) {
|
|
507
|
+
ScrDynKeySlot *slot = &scr_dyn_keys[scr_dyn_key_slot(key, length)];
|
|
508
|
+
if (slot->key == owned) slot->key = NULL;
|
|
509
|
+
}
|
|
510
|
+
#ifdef SCR_RC_AUDIT
|
|
511
|
+
scr_live_dyn_keys--;
|
|
512
|
+
#endif
|
|
513
|
+
free(owned);
|
|
514
|
+
}
|
|
515
|
+
|
|
416
516
|
#ifndef SCR_RC_AUDIT
|
|
417
517
|
static SCR_TL ScrDyn *scr_dyn_free_arr, *scr_dyn_free_obj, *scr_dyn_free_misc;
|
|
418
518
|
static SCR_TL size_t scr_dyn_free_count;
|
|
419
519
|
#define SCR_DYN_FREE_MAX 8192
|
|
520
|
+
/* Recycled small objects may retain key bytes, but never values or an
|
|
521
|
+
* unbounded document's keys. This budget applies across the whole pool. */
|
|
522
|
+
static SCR_TL size_t scr_dyn_cached_key_bytes;
|
|
523
|
+
#define SCR_DYN_KEY_CACHE_MAX ((size_t)64 * 1024)
|
|
524
|
+
#define SCR_DYN_KEY_CACHE_SLOTS 16
|
|
525
|
+
#define SCR_DYN_KEY_CACHE_LENGTH 64
|
|
526
|
+
#endif
|
|
527
|
+
|
|
528
|
+
static void scr_dyn_obj_drop_keys(ScrDyn *d, bool cache) {
|
|
529
|
+
for (size_t i = 0; i < d->v.obj.cap; i++) {
|
|
530
|
+
ScrDynEntry *entry = &d->v.obj.entries[i];
|
|
531
|
+
if (!entry->key) continue;
|
|
532
|
+
#ifndef SCR_RC_AUDIT
|
|
533
|
+
if (cache && d->v.obj.cap <= SCR_DYN_KEY_CACHE_SLOTS &&
|
|
534
|
+
entry->key_len <= SCR_DYN_KEY_CACHE_LENGTH &&
|
|
535
|
+
sizeof(ScrDynKey) + entry->key_len + 1 <= SCR_DYN_KEY_CACHE_MAX - scr_dyn_cached_key_bytes) {
|
|
536
|
+
scr_dyn_cached_key_bytes += sizeof(ScrDynKey) + entry->key_len + 1;
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
#else
|
|
540
|
+
(void)cache;
|
|
420
541
|
#endif
|
|
542
|
+
scr_dyn_key_release(entry->key, entry->key_len);
|
|
543
|
+
entry->key = NULL;
|
|
544
|
+
entry->key_len = 0;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/* Primitive kinds cannot acquire properties or change kind while live, so
|
|
549
|
+
* an object property edge to one cannot participate in a cycle. Keep its RC edge
|
|
550
|
+
* out of trial deletion and release it in collected teardown instead. Other
|
|
551
|
+
* owners may still trace the same primitive through the uniform header ABI;
|
|
552
|
+
* our untraced reference keeps it alive until that teardown releases it.
|
|
553
|
+
* Entry metadata snapshots this decision during trace, before any white node
|
|
554
|
+
* can be freed. Teardown must never inspect the child's kind again. */
|
|
555
|
+
static bool scr_dyn_member_is_traced(const ScrDyn *value) {
|
|
556
|
+
return value && value->kind > SCR_DYN_STR && value->kind != SCR_DYN_UNDEF;
|
|
557
|
+
}
|
|
421
558
|
|
|
422
559
|
/* Checked objects and functions can close over one another. All heap dyn
|
|
423
560
|
* nodes carry a collector header so a captured unknown value has one uniform
|
|
@@ -425,19 +562,21 @@ static SCR_TL size_t scr_dyn_free_count;
|
|
|
425
562
|
* capsules keep their conservative ownership edges. */
|
|
426
563
|
void scr_dyn_trace_v(void *ptr, ScrTraceVisit visit, void *ctx) {
|
|
427
564
|
ScrDyn *d = ptr;
|
|
428
|
-
visit(d->prototype, ctx);
|
|
429
|
-
visit(d->symbol_properties, ctx);
|
|
430
|
-
visit(d->symbol_keys, ctx);
|
|
565
|
+
if (d->prototype) visit(d->prototype, ctx);
|
|
566
|
+
if (d->symbol_properties) visit(d->symbol_properties, ctx);
|
|
567
|
+
if (d->symbol_keys) visit(d->symbol_keys, ctx);
|
|
431
568
|
switch (d->kind) {
|
|
432
569
|
case SCR_DYN_ARR:
|
|
433
570
|
for (size_t i = 0; i < d->v.arr.len; i++) visit(d->v.arr.items[i], ctx);
|
|
434
|
-
visit(d->v.arr.properties, ctx);
|
|
571
|
+
if (d->v.arr.properties) visit(d->v.arr.properties, ctx);
|
|
435
572
|
break;
|
|
436
573
|
case SCR_DYN_OBJ:
|
|
437
574
|
for (size_t i = 0; i < d->v.obj.len; i++) {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
visit(
|
|
575
|
+
ScrDynEntry *entry = &d->v.obj.entries[i];
|
|
576
|
+
entry->value_traced = scr_dyn_member_is_traced(entry->value);
|
|
577
|
+
if (entry->value_traced) visit(entry->value, ctx);
|
|
578
|
+
if (entry->getter) visit(entry->getter, ctx);
|
|
579
|
+
if (entry->setter) visit(entry->setter, ctx);
|
|
441
580
|
}
|
|
442
581
|
break;
|
|
443
582
|
case SCR_DYN_FUNC: visit(d->v.fn.clo, ctx); break;
|
|
@@ -539,6 +678,12 @@ static ScrDyn *scr_dyn_alloc(ScrDynKind kind) {
|
|
|
539
678
|
d->v.arr.presence = NULL;
|
|
540
679
|
} else if (kind == SCR_DYN_OBJ) {
|
|
541
680
|
d->v.obj.len = 0; /* cap/entries preserved */
|
|
681
|
+
if (d->v.obj.cap <= SCR_DYN_KEY_CACHE_SLOTS) {
|
|
682
|
+
for (size_t i = 0; i < d->v.obj.cap; i++) {
|
|
683
|
+
ScrDynEntry *entry = &d->v.obj.entries[i];
|
|
684
|
+
if (entry->key) scr_dyn_cached_key_bytes -= sizeof(ScrDynKey) + entry->key_len + 1;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
542
687
|
d->v.obj.source_identity = NULL;
|
|
543
688
|
d->v.obj.source_access = NULL;
|
|
544
689
|
} else {
|
|
@@ -565,6 +710,10 @@ static void scr_dyn_handle_release(void *h, ScrDynHandleTag tag);
|
|
|
565
710
|
static bool scr_dyn_to_primitive_result_is_object(const ScrDyn *d);
|
|
566
711
|
|
|
567
712
|
static void scr_dyn_dispose(ScrDyn *d, bool collected) {
|
|
713
|
+
if (d->kind == SCR_DYN_OBJ) {
|
|
714
|
+
free(d->v.obj.index);
|
|
715
|
+
d->v.obj.index = NULL;
|
|
716
|
+
}
|
|
568
717
|
if (!collected) scr_dyn_release(d->prototype);
|
|
569
718
|
if (!collected) scr_dyn_release(d->symbol_properties);
|
|
570
719
|
if (!collected) scr_dyn_release(d->symbol_keys);
|
|
@@ -592,12 +741,15 @@ static void scr_dyn_dispose(ScrDyn *d, bool collected) {
|
|
|
592
741
|
break;
|
|
593
742
|
case SCR_DYN_OBJ:
|
|
594
743
|
for (size_t i = 0; i < d->v.obj.len; i++) {
|
|
595
|
-
|
|
744
|
+
ScrDyn *value = d->v.obj.entries[i].value;
|
|
745
|
+
if (!collected || !d->v.obj.entries[i].value_traced) scr_dyn_release(value);
|
|
596
746
|
if (!collected) {
|
|
597
|
-
scr_dyn_release(d->v.obj.entries[i].value);
|
|
598
747
|
scr_dyn_release(d->v.obj.entries[i].getter);
|
|
599
748
|
scr_dyn_release(d->v.obj.entries[i].setter);
|
|
600
749
|
}
|
|
750
|
+
d->v.obj.entries[i].value = NULL;
|
|
751
|
+
d->v.obj.entries[i].getter = NULL;
|
|
752
|
+
d->v.obj.entries[i].setter = NULL;
|
|
601
753
|
}
|
|
602
754
|
if (d->v.obj.source_identity) {
|
|
603
755
|
d->v.obj.source_access(d->v.obj.source_identity, false);
|
|
@@ -646,6 +798,7 @@ static void scr_dyn_dispose(ScrDyn *d, bool collected) {
|
|
|
646
798
|
#endif
|
|
647
799
|
#ifndef SCR_RC_AUDIT
|
|
648
800
|
if (!collected && scr_dyn_free_count < SCR_DYN_FREE_MAX) {
|
|
801
|
+
if (d->kind == SCR_DYN_OBJ) scr_dyn_obj_drop_keys(d, true);
|
|
649
802
|
/* Pooled blocks retain their headers and count toward the bounded
|
|
650
803
|
* allocated heap, but never remain in a candidate buffer. */
|
|
651
804
|
scr_weak_dispose(d);
|
|
@@ -659,7 +812,10 @@ static void scr_dyn_dispose(ScrDyn *d, bool collected) {
|
|
|
659
812
|
}
|
|
660
813
|
#endif
|
|
661
814
|
if (d->kind == SCR_DYN_ARR) free(d->v.arr.items);
|
|
662
|
-
else if (d->kind == SCR_DYN_OBJ)
|
|
815
|
+
else if (d->kind == SCR_DYN_OBJ) {
|
|
816
|
+
scr_dyn_obj_drop_keys(d, false);
|
|
817
|
+
free(d->v.obj.entries);
|
|
818
|
+
}
|
|
663
819
|
scr_cyc_free(d);
|
|
664
820
|
}
|
|
665
821
|
|
|
@@ -678,25 +834,95 @@ void scr_dyn_release(ScrDyn *d) {
|
|
|
678
834
|
scr_dyn_dispose(d, false);
|
|
679
835
|
}
|
|
680
836
|
|
|
837
|
+
/* Small objects retain their compact linear representation. A wide object
|
|
838
|
+
* owns a bucket index of entry positions; entries remain insertion ordered.
|
|
839
|
+
* No key/value ownership lives here, and callbacks never retain its slots. */
|
|
840
|
+
#define SCR_DYN_OBJECT_INDEX_MIN 32
|
|
841
|
+
typedef struct ScrDynObjectIndex {
|
|
842
|
+
size_t buckets;
|
|
843
|
+
size_t slots[]; /* zero is empty; occupied slots store entry + 1 */
|
|
844
|
+
} ScrDynObjectIndex;
|
|
845
|
+
|
|
846
|
+
static void scr_dyn_index_insert(ScrDynObjectIndex *index, const ScrDynEntry *entry, size_t position) {
|
|
847
|
+
size_t slot = scr_key_hash(entry->key, entry->key_len) & (index->buckets - 1);
|
|
848
|
+
while (index->slots[slot]) slot = (slot + 1) & (index->buckets - 1);
|
|
849
|
+
index->slots[slot] = position + 1;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/* Keep index allocation/probing outside compact object insertion and reads. */
|
|
853
|
+
static __attribute__((noinline)) void scr_dyn_index_append(ScrDyn *object) {
|
|
854
|
+
size_t count = object->v.obj.len;
|
|
855
|
+
ScrDynObjectIndex *index = object->v.obj.index;
|
|
856
|
+
if (!index || count > index->buckets / 2) {
|
|
857
|
+
size_t buckets = index ? index->buckets : 64;
|
|
858
|
+
while (count > buckets / 2) {
|
|
859
|
+
if (buckets > (SIZE_MAX - sizeof *index) / sizeof(size_t) / 2) scr_json_oom();
|
|
860
|
+
buckets *= 2;
|
|
861
|
+
}
|
|
862
|
+
free(index);
|
|
863
|
+
index = calloc(1, sizeof *index + buckets * sizeof(size_t));
|
|
864
|
+
if (!index) scr_json_oom();
|
|
865
|
+
index->buckets = buckets;
|
|
866
|
+
object->v.obj.index = index;
|
|
867
|
+
for (size_t i = 0; i < count; i++) scr_dyn_index_insert(index, &object->v.obj.entries[i], i);
|
|
868
|
+
} else {
|
|
869
|
+
scr_dyn_index_insert(index, &object->v.obj.entries[count - 1], count - 1);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/* A checked hint avoids repeating a linear walk for recurring property
|
|
874
|
+
* names. Slots store indices only, never object/key pointers. Every hit
|
|
875
|
+
* checks the current entry, so different shapes, deletion and reordering
|
|
876
|
+
* need no invalidation and collisions fall back to the ordinary search. */
|
|
877
|
+
#define SCR_DYN_PROPERTY_HINTS 64
|
|
878
|
+
static SCR_TL size_t scr_dyn_property_hints[SCR_DYN_PROPERTY_HINTS];
|
|
879
|
+
|
|
880
|
+
static ScrDynEntry *scr_dyn_find_linear_entry(const ScrDyn *object, const char *key, size_t length) {
|
|
881
|
+
for (size_t i = 0; i < object->v.obj.len; i++) {
|
|
882
|
+
ScrDynEntry *entry = &object->v.obj.entries[i];
|
|
883
|
+
if (entry->key_len == length && !memcmp(entry->key, key, length)) return entry;
|
|
884
|
+
}
|
|
885
|
+
return NULL;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
static __attribute__((noinline)) ScrDynEntry *scr_dyn_find_indexed_entry(
|
|
889
|
+
const ScrDyn *object, const char *key, size_t length) {
|
|
890
|
+
const ScrDynObjectIndex *index = object->v.obj.index;
|
|
891
|
+
size_t slot = scr_key_hash(key, length) & (index->buckets - 1);
|
|
892
|
+
while (index->slots[slot]) {
|
|
893
|
+
ScrDynEntry *entry = &object->v.obj.entries[index->slots[slot] - 1];
|
|
894
|
+
if (entry->key_len == length && !memcmp(entry->key, key, length)) return entry;
|
|
895
|
+
slot = (slot + 1) & (index->buckets - 1);
|
|
896
|
+
}
|
|
897
|
+
return NULL;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
static ScrDynEntry *scr_dyn_find_entry(const ScrDyn *object, const char *key, size_t length) {
|
|
901
|
+
if (object->kind != SCR_DYN_OBJ) return NULL;
|
|
902
|
+
if (object->v.obj.index) return scr_dyn_find_indexed_entry(object, key, length);
|
|
903
|
+
size_t slot = (length + (length ? (unsigned char)key[0] * 7u : 0)) % SCR_DYN_PROPERTY_HINTS;
|
|
904
|
+
size_t hint = scr_dyn_property_hints[slot];
|
|
905
|
+
if (hint < object->v.obj.len) {
|
|
906
|
+
ScrDynEntry *entry = &object->v.obj.entries[hint];
|
|
907
|
+
if (entry->key_len == length && !memcmp(entry->key, key, length)) return entry;
|
|
908
|
+
}
|
|
909
|
+
ScrDynEntry *entry = scr_dyn_find_linear_entry(object, key, length);
|
|
910
|
+
if (entry) scr_dyn_property_hints[slot] = (size_t)(entry - object->v.obj.entries);
|
|
911
|
+
return entry;
|
|
912
|
+
}
|
|
913
|
+
|
|
681
914
|
ScrDyn *scr_dyn_obj_get(const ScrDyn *d, const char *key, size_t key_len) {
|
|
682
915
|
if (d->kind == SCR_DYN_PROXY) {
|
|
683
916
|
scr_dyn_proxy_unsupported("raw object inspection");
|
|
684
917
|
return NULL;
|
|
685
918
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
const ScrDynEntry *e = &d->v.obj.entries[i];
|
|
689
|
-
if (e->key_len == key_len && memcmp(e->key, key, key_len) == 0) return e->value;
|
|
690
|
-
}
|
|
691
|
-
return NULL;
|
|
919
|
+
ScrDynEntry *entry = scr_dyn_find_entry(d, key, key_len);
|
|
920
|
+
return entry ? entry->value : NULL;
|
|
692
921
|
}
|
|
693
922
|
|
|
694
923
|
bool scr_dyn_obj_enumerable(const ScrDyn *d, const char *key, size_t key_len) {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
if (e->key_len == key_len && memcmp(e->key, key, key_len) == 0) return e->enumerable;
|
|
698
|
-
}
|
|
699
|
-
return false;
|
|
924
|
+
ScrDynEntry *entry = scr_dyn_find_entry(d, key, key_len);
|
|
925
|
+
return entry && entry->enumerable;
|
|
700
926
|
}
|
|
701
927
|
|
|
702
928
|
static const ScrDyn *scr_dyn_property_owner(const ScrDyn *object, const char *key, size_t length);
|
|
@@ -983,29 +1209,27 @@ ScrDyn *scr_dyn_arr_named_get(const ScrDyn *d, const ScrStr *key) {
|
|
|
983
1209
|
return value;
|
|
984
1210
|
}
|
|
985
1211
|
|
|
986
|
-
/*
|
|
987
|
-
*
|
|
988
|
-
*
|
|
989
|
-
static void scr_dyn_obj_put(ScrDyn *obj, char *key, size_t key_len, ScrDyn *value) {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
}
|
|
1000
|
-
ScrDyn *old = e->value;
|
|
1001
|
-
e->value = value;
|
|
1002
|
-
scr_dyn_release(old);
|
|
1003
|
-
free(key);
|
|
1212
|
+
/* Borrows key and takes ownership of value. Duplicate keys retain their
|
|
1213
|
+
* original spelling/storage and insertion position; the later value wins.
|
|
1214
|
+
* A recycled entry can reuse equal key bytes without a malloc/free pair. */
|
|
1215
|
+
static void scr_dyn_obj_put(ScrDyn *obj, const char *key, size_t key_len, ScrDyn *value) {
|
|
1216
|
+
/* Construction usually appends an absent name. Avoid a read hint that
|
|
1217
|
+
* repeats comparisons on compact objects; wide objects use their index. */
|
|
1218
|
+
ScrDynEntry *existing = obj->v.obj.index ? scr_dyn_find_entry(obj, key, key_len)
|
|
1219
|
+
: scr_dyn_find_linear_entry(obj, key, key_len);
|
|
1220
|
+
if (existing) {
|
|
1221
|
+
if (existing->accessor || !existing->writable) {
|
|
1222
|
+
scr_dyn_release(value);
|
|
1223
|
+
static const char message[] = "Cannot assign to read only property";
|
|
1224
|
+
scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1);
|
|
1004
1225
|
return;
|
|
1005
1226
|
}
|
|
1227
|
+
ScrDyn *old = existing->value;
|
|
1228
|
+
existing->value = value;
|
|
1229
|
+
scr_dyn_release(old);
|
|
1230
|
+
return;
|
|
1006
1231
|
}
|
|
1007
1232
|
if (obj->non_extensible) {
|
|
1008
|
-
free(key);
|
|
1009
1233
|
scr_dyn_release(value);
|
|
1010
1234
|
static const char message[] = "Cannot add property, object is not extensible";
|
|
1011
1235
|
scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1);
|
|
@@ -1015,11 +1239,16 @@ static void scr_dyn_obj_put(ScrDyn *obj, char *key, size_t key_len, ScrDyn *valu
|
|
|
1015
1239
|
size_t cap = obj->v.obj.cap ? obj->v.obj.cap * 2 : 4;
|
|
1016
1240
|
ScrDynEntry *entries = realloc(obj->v.obj.entries, cap * sizeof *entries);
|
|
1017
1241
|
if (!entries) scr_json_oom();
|
|
1242
|
+
memset(entries + obj->v.obj.cap, 0, (cap - obj->v.obj.cap) * sizeof *entries);
|
|
1018
1243
|
obj->v.obj.entries = entries;
|
|
1019
1244
|
obj->v.obj.cap = cap;
|
|
1020
1245
|
}
|
|
1021
1246
|
ScrDynEntry *e = &obj->v.obj.entries[obj->v.obj.len++];
|
|
1022
|
-
e->key
|
|
1247
|
+
if (!e->key || e->key_len != key_len || memcmp(e->key, key, key_len)) {
|
|
1248
|
+
char *owned = scr_dyn_key_copy(key, key_len);
|
|
1249
|
+
scr_dyn_key_release(e->key, e->key_len);
|
|
1250
|
+
e->key = owned;
|
|
1251
|
+
}
|
|
1023
1252
|
e->key_len = key_len;
|
|
1024
1253
|
e->value = value;
|
|
1025
1254
|
e->getter = NULL;
|
|
@@ -1028,6 +1257,7 @@ static void scr_dyn_obj_put(ScrDyn *obj, char *key, size_t key_len, ScrDyn *valu
|
|
|
1028
1257
|
e->writable = true;
|
|
1029
1258
|
e->enumerable = true;
|
|
1030
1259
|
e->configurable = true;
|
|
1260
|
+
if (obj->v.obj.len >= SCR_DYN_OBJECT_INDEX_MIN) scr_dyn_index_append(obj);
|
|
1031
1261
|
}
|
|
1032
1262
|
|
|
1033
1263
|
/* ── dyn construction (compiler-emitted converters & overflow reads) ───── */
|
|
@@ -1039,12 +1269,20 @@ ScrDyn *scr_dyn_undefined(void) {
|
|
|
1039
1269
|
return &undef;
|
|
1040
1270
|
}
|
|
1041
1271
|
|
|
1042
|
-
|
|
1272
|
+
/* Primitive nodes have no mutable properties or observable box identity.
|
|
1273
|
+
* As with undefined, SIZE_MAX keeps them out of RC and cycle bookkeeping;
|
|
1274
|
+
* callers retain the ordinary owned-result and ownership-transfer contracts. */
|
|
1275
|
+
ScrDyn *scr_dyn_new_null(void) {
|
|
1276
|
+
static ScrDyn value = { .rc = SIZE_MAX, .kind = SCR_DYN_NULL };
|
|
1277
|
+
return &value;
|
|
1278
|
+
}
|
|
1043
1279
|
|
|
1044
1280
|
ScrDyn *scr_dyn_new_bool(bool b) {
|
|
1045
|
-
ScrDyn
|
|
1046
|
-
|
|
1047
|
-
|
|
1281
|
+
static ScrDyn values[2] = {
|
|
1282
|
+
{ .rc = SIZE_MAX, .kind = SCR_DYN_BOOL, .v.b = false },
|
|
1283
|
+
{ .rc = SIZE_MAX, .kind = SCR_DYN_BOOL, .v.b = true },
|
|
1284
|
+
};
|
|
1285
|
+
return &values[b ? 1 : 0];
|
|
1048
1286
|
}
|
|
1049
1287
|
|
|
1050
1288
|
ScrDyn *scr_dyn_new_bigint(ScrBigInt *value) {
|
|
@@ -1080,6 +1318,22 @@ ScrDyn *scr_dyn_symbol_get(const ScrDyn *value, const ScrStr *key) {
|
|
|
1080
1318
|
}
|
|
1081
1319
|
|
|
1082
1320
|
ScrDyn *scr_dyn_new_num(double n) {
|
|
1321
|
+
/* Small integers are immutable primitives, just like the boolean and null
|
|
1322
|
+
* singletons. Initialize only touched slots; the table neither grows nor
|
|
1323
|
+
* owns heap references. Preserve negative zero as a distinct payload. */
|
|
1324
|
+
if (n >= -128 && n <= 255) {
|
|
1325
|
+
int value = (int)n;
|
|
1326
|
+
if ((double)value == n && (value != 0 || !signbit(n))) {
|
|
1327
|
+
static SCR_TL ScrDyn values[384];
|
|
1328
|
+
ScrDyn *cached = &values[value + 128];
|
|
1329
|
+
if (cached->rc == 0) {
|
|
1330
|
+
cached->rc = SIZE_MAX;
|
|
1331
|
+
cached->kind = SCR_DYN_NUM;
|
|
1332
|
+
cached->v.num = n;
|
|
1333
|
+
}
|
|
1334
|
+
return cached;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1083
1337
|
ScrDyn *d = scr_dyn_alloc(SCR_DYN_NUM);
|
|
1084
1338
|
d->v.num = n;
|
|
1085
1339
|
return d;
|
|
@@ -2819,14 +3073,10 @@ static void scr_dyn_handle_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) {
|
|
|
2819
3073
|
scr_throw_error(SCR_ERR_ERROR, scr_jb_finish(&b));
|
|
2820
3074
|
}
|
|
2821
3075
|
|
|
2822
|
-
/* Public obj insertion
|
|
2823
|
-
*
|
|
3076
|
+
/* Public obj insertion copies a new key only when needed, owns the value,
|
|
3077
|
+
* and preserves the existing entry for duplicate keys. */
|
|
2824
3078
|
void scr_dyn_obj_set(ScrDyn *obj, const char *key, size_t key_len, ScrDyn *value) {
|
|
2825
|
-
|
|
2826
|
-
if (!copy) scr_json_oom();
|
|
2827
|
-
memcpy(copy, key, key_len);
|
|
2828
|
-
copy[key_len] = '\0';
|
|
2829
|
-
scr_dyn_obj_put(obj, copy, key_len, value);
|
|
3079
|
+
scr_dyn_obj_put(obj, key, key_len, value);
|
|
2830
3080
|
}
|
|
2831
3081
|
|
|
2832
3082
|
static ScrStr *scr_dyn_property_key(const ScrDyn *key) {
|
|
@@ -2834,12 +3084,7 @@ static ScrStr *scr_dyn_property_key(const ScrDyn *key) {
|
|
|
2834
3084
|
}
|
|
2835
3085
|
|
|
2836
3086
|
static ScrDynEntry *scr_dyn_entry(ScrDyn *obj, const ScrStr *key) {
|
|
2837
|
-
|
|
2838
|
-
for (size_t i = 0; i < obj->v.obj.len; i++) {
|
|
2839
|
-
ScrDynEntry *entry = &obj->v.obj.entries[i];
|
|
2840
|
-
if (entry->key_len == key->len && memcmp(entry->key, key->data, key->len) == 0) return entry;
|
|
2841
|
-
}
|
|
2842
|
-
return NULL;
|
|
3087
|
+
return scr_dyn_find_entry(obj, key->data, key->len);
|
|
2843
3088
|
}
|
|
2844
3089
|
|
|
2845
3090
|
static bool scr_dyn_property_same_value(const ScrDyn *a, const ScrDyn *b) {
|
|
@@ -3022,6 +3267,7 @@ typedef struct {
|
|
|
3022
3267
|
unsigned kind; /* 0: array, 1: string, 2: Map, 3: Set */
|
|
3023
3268
|
unsigned selection; /* 0: values, 1: keys, 2: entries */
|
|
3024
3269
|
bool array_like;
|
|
3270
|
+
bool step_done; /* completion of the last compiler-consumed step */
|
|
3025
3271
|
double limit;
|
|
3026
3272
|
} ScrNativeIterator;
|
|
3027
3273
|
|
|
@@ -3063,21 +3309,18 @@ static void scr_native_iterator_release(void *ptr) {
|
|
|
3063
3309
|
free(iterator);
|
|
3064
3310
|
}
|
|
3065
3311
|
|
|
3066
|
-
|
|
3312
|
+
/* Produce an owned value without allocating the protocol result. The
|
|
3313
|
+
* compiler uses this only after checking the captured next function; public
|
|
3314
|
+
* next() calls still receive a fresh, independently mutable result object. */
|
|
3315
|
+
static ScrDyn *scr_native_iterator_value(ScrNativeIterator *iterator, bool *done_out) {
|
|
3067
3316
|
ScrDyn *source = iterator->source;
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
if (scr_dyn_truthy(scr_dyn_obj_get(result, "done", 4))) {
|
|
3072
|
-
iterator->source = NULL;
|
|
3073
|
-
scr_dyn_release(source);
|
|
3074
|
-
}
|
|
3075
|
-
return result;
|
|
3076
|
-
}
|
|
3317
|
+
/* An inherited indexed getter can advance this same iterator recursively.
|
|
3318
|
+
* Keep the source alive even if that nested call exhausts the iterator. */
|
|
3319
|
+
if (source) scr_dyn_retain(source);
|
|
3077
3320
|
/* Native array capsules retain the live array, so refresh its view at each
|
|
3078
3321
|
* step rather than iterating a snapshot taken when the iterator opened. */
|
|
3079
3322
|
ScrDyn *view = source && source->kind == SCR_DYN_TYPED_REF ? scr_dyn_typed_ref_materialize(source) : NULL;
|
|
3080
|
-
if (scr_exc_pending()) { scr_dyn_release(view); return NULL; }
|
|
3323
|
+
if (scr_exc_pending()) { scr_dyn_release(view); scr_dyn_release(source); return NULL; }
|
|
3081
3324
|
ScrDyn *items = view ? view : source;
|
|
3082
3325
|
ScrMap *map = source && (iterator->kind == 2 || iterator->kind == 3) ? source->v.handle.ptr : NULL;
|
|
3083
3326
|
if (map) while (iterator->index < scr_map_iter_count(map) && !scr_map_iter_live(map, iterator->index)) iterator->index++;
|
|
@@ -3123,6 +3366,24 @@ static ScrDyn *scr_native_iterator_next(ScrNativeIterator *iterator) {
|
|
|
3123
3366
|
scr_str_release(point);
|
|
3124
3367
|
}
|
|
3125
3368
|
scr_dyn_release(view);
|
|
3369
|
+
scr_dyn_release(source);
|
|
3370
|
+
*done_out = done;
|
|
3371
|
+
return value;
|
|
3372
|
+
}
|
|
3373
|
+
|
|
3374
|
+
static ScrDyn *scr_native_iterator_next(ScrNativeIterator *iterator) {
|
|
3375
|
+
ScrDyn *source = iterator->source;
|
|
3376
|
+
if (source && iterator->kind == 4) {
|
|
3377
|
+
ScrDyn *result = scr_dyn_handle_ops_of(source)->iter_step(source->v.handle.ptr, &iterator->index, iterator->selection);
|
|
3378
|
+
if (!result) return NULL;
|
|
3379
|
+
if (scr_dyn_truthy(scr_dyn_obj_get(result, "done", 4))) {
|
|
3380
|
+
iterator->source = NULL;
|
|
3381
|
+
scr_dyn_release(source);
|
|
3382
|
+
}
|
|
3383
|
+
return result;
|
|
3384
|
+
}
|
|
3385
|
+
bool done;
|
|
3386
|
+
ScrDyn *value = scr_native_iterator_value(iterator, &done);
|
|
3126
3387
|
if (!value) return NULL;
|
|
3127
3388
|
ScrDyn *result = scr_dyn_new_obj();
|
|
3128
3389
|
scr_dyn_obj_set(result, "value", 5, value);
|
|
@@ -3172,6 +3433,28 @@ static ScrDyn *scr_native_handle_iterator_next_call(ScrClosure *closure, ScrDyn
|
|
|
3172
3433
|
return scr_native_iterator_next_checked(4);
|
|
3173
3434
|
}
|
|
3174
3435
|
|
|
3436
|
+
bool scr_dyn_iterator_can_step(const ScrDyn *iterator, const ScrDyn *next) {
|
|
3437
|
+
if (iterator->kind != SCR_DYN_HANDLE || iterator->v.handle.tag != SCR_DYNH_ITERATOR) return false;
|
|
3438
|
+
unsigned kind = ((ScrNativeIterator *)iterator->v.handle.ptr)->kind;
|
|
3439
|
+
if (kind > 3) return false;
|
|
3440
|
+
size_t index = kind == 3 ? 8 : kind == 2 ? 7 : kind == 1 ? 5 : 0;
|
|
3441
|
+
return next == scr_iterator_functions[index];
|
|
3442
|
+
}
|
|
3443
|
+
|
|
3444
|
+
ScrDyn *scr_dyn_iterator_step(const ScrDyn *value) {
|
|
3445
|
+
ScrNativeIterator *iterator = value->v.handle.ptr;
|
|
3446
|
+
bool done = false;
|
|
3447
|
+
ScrDyn *item = scr_native_iterator_value(iterator, &done);
|
|
3448
|
+
/* Publish after getters finish, so recursive steps cannot overwrite this
|
|
3449
|
+
* step's completion. No user code runs between step and step_done. */
|
|
3450
|
+
iterator->step_done = done;
|
|
3451
|
+
return item;
|
|
3452
|
+
}
|
|
3453
|
+
|
|
3454
|
+
bool scr_dyn_iterator_step_done(const ScrDyn *value) {
|
|
3455
|
+
return ((ScrNativeIterator *)value->v.handle.ptr)->step_done;
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3175
3458
|
static ScrDyn *scr_native_iterator_get(void *ptr, const char *key, size_t len) {
|
|
3176
3459
|
if (len != 4 || memcmp(key, "next", 4)) return NULL;
|
|
3177
3460
|
unsigned kind = ((ScrNativeIterator *)ptr)->kind;
|
|
@@ -5475,10 +5758,7 @@ void scr_jb_put_dyn(ScrJsonBuf *b, const ScrDyn *d) {
|
|
|
5475
5758
|
if (e->value->kind == SCR_DYN_JSVAL && scr_dyn_isl_typeof_is(e->value, "function")) continue; /* engine functions drop too */
|
|
5476
5759
|
if (!first) scr_jb_putc(b, ',');
|
|
5477
5760
|
first = false;
|
|
5478
|
-
|
|
5479
|
-
ScrStr *k = scr_str_new(e->key, e->key_len);
|
|
5480
|
-
scr_jb_put_json_str(b, k);
|
|
5481
|
-
scr_str_release(k);
|
|
5761
|
+
scr_jb_put_json_span(b, e->key, e->key_len);
|
|
5482
5762
|
scr_jb_putc(b, ':');
|
|
5483
5763
|
scr_jb_put_dyn(b, e->value);
|
|
5484
5764
|
}
|
|
@@ -5639,8 +5919,10 @@ static int32_t scr_json_hex4(ScrJsonP *p) {
|
|
|
5639
5919
|
* slow path would produce) and returns -1. */
|
|
5640
5920
|
static int scr_json_string_span(ScrJsonP *p, const char **span,
|
|
5641
5921
|
size_t *span_len) {
|
|
5642
|
-
|
|
5643
|
-
|
|
5922
|
+
*span = p->s + p->pos + 1;
|
|
5923
|
+
*span_len = scr_json_plain_bytes(*span, p->len - p->pos - 1);
|
|
5924
|
+
size_t i = p->pos + 1 + *span_len;
|
|
5925
|
+
if (i < p->len) {
|
|
5644
5926
|
unsigned char c = (unsigned char)p->s[i];
|
|
5645
5927
|
if (c == '"') {
|
|
5646
5928
|
*span = p->s + p->pos + 1;
|
|
@@ -5653,7 +5935,6 @@ static int scr_json_string_span(ScrJsonP *p, const char **span,
|
|
|
5653
5935
|
scr_json_throw_pos("Bad control character in string literal", i);
|
|
5654
5936
|
return -1;
|
|
5655
5937
|
}
|
|
5656
|
-
i++;
|
|
5657
5938
|
}
|
|
5658
5939
|
scr_json_throw_pos("Unterminated string", p->pos);
|
|
5659
5940
|
return -1;
|
|
@@ -5661,11 +5942,13 @@ static int scr_json_string_span(ScrJsonP *p, const char **span,
|
|
|
5661
5942
|
|
|
5662
5943
|
/* Slow path: parses the string literal at p->pos (the opening quote),
|
|
5663
5944
|
* decoding escapes, into a +1 ScrStr. NULL on error (thrown). */
|
|
5664
|
-
static ScrStr *scr_json_string_slow(ScrJsonP *p) {
|
|
5945
|
+
static ScrStr *scr_json_string_slow(ScrJsonP *p, size_t prefix) {
|
|
5665
5946
|
size_t open = p->pos;
|
|
5666
5947
|
p->pos++; /* opening quote */
|
|
5667
5948
|
ScrJsonBuf b;
|
|
5668
5949
|
scr_jb_init(&b);
|
|
5950
|
+
scr_jb_write(&b, p->s + p->pos, prefix);
|
|
5951
|
+
p->pos += prefix;
|
|
5669
5952
|
for (;;) {
|
|
5670
5953
|
if (p->pos >= p->len) {
|
|
5671
5954
|
scr_jb_dispose(&b);
|
|
@@ -5683,8 +5966,9 @@ static ScrStr *scr_json_string_slow(ScrJsonP *p) {
|
|
|
5683
5966
|
return NULL;
|
|
5684
5967
|
}
|
|
5685
5968
|
if (c != '\\') {
|
|
5686
|
-
|
|
5687
|
-
p->pos
|
|
5969
|
+
size_t run = scr_json_plain_bytes(p->s + p->pos, p->len - p->pos);
|
|
5970
|
+
scr_jb_write(&b, p->s + p->pos, run);
|
|
5971
|
+
p->pos += run;
|
|
5688
5972
|
continue;
|
|
5689
5973
|
}
|
|
5690
5974
|
p->pos++; /* backslash */
|
|
@@ -5759,7 +6043,7 @@ static ScrStr *scr_json_string_scr(ScrJsonP *p) {
|
|
|
5759
6043
|
int r = scr_json_string_span(p, &span, &span_len);
|
|
5760
6044
|
if (r < 0) return NULL;
|
|
5761
6045
|
if (r > 0) return scr_str_new(span, span_len);
|
|
5762
|
-
return scr_json_string_slow(p);
|
|
6046
|
+
return scr_json_string_slow(p, span_len);
|
|
5763
6047
|
}
|
|
5764
6048
|
|
|
5765
6049
|
static ScrDyn *scr_json_value(ScrJsonP *p);
|
|
@@ -5838,20 +6122,17 @@ static ScrDyn *scr_json_number(ScrJsonP *p) {
|
|
|
5838
6122
|
}
|
|
5839
6123
|
exp10 += eneg ? -ev : ev;
|
|
5840
6124
|
}
|
|
5841
|
-
ScrDyn *d = scr_dyn_alloc(SCR_DYN_NUM);
|
|
5842
6125
|
if (precise && exp10 >= -22 && exp10 <= 22) {
|
|
5843
6126
|
double v = (double)mant; /* exact: mant < 10^15 < 2^53 */
|
|
5844
6127
|
if (exp10 > 0) v *= scr_json_pow10[exp10];
|
|
5845
6128
|
else if (exp10 < 0) v /= scr_json_pow10[-exp10];
|
|
5846
|
-
|
|
5847
|
-
return d;
|
|
6129
|
+
return scr_dyn_new_num(neg ? -v : v);
|
|
5848
6130
|
}
|
|
5849
6131
|
/* The validated span re-parses with strtod (correctly rounded, and the
|
|
5850
6132
|
* grammar above is a strict subset of what strtod accepts). The ScrStr
|
|
5851
6133
|
* data is NUL-terminated, and strtod stops at the first non-number char,
|
|
5852
6134
|
* so parsing from `start` reads exactly the validated token. */
|
|
5853
|
-
|
|
5854
|
-
return d;
|
|
6135
|
+
return scr_dyn_new_num(strtod(p->s + start, NULL));
|
|
5855
6136
|
}
|
|
5856
6137
|
|
|
5857
6138
|
static bool scr_json_lit(ScrJsonP *p, const char *word, size_t n) {
|
|
@@ -5918,10 +6199,12 @@ static ScrDyn *scr_json_object(ScrJsonP *p) {
|
|
|
5918
6199
|
scr_json_throw_pos("Expected property name or '}'", p->pos);
|
|
5919
6200
|
return NULL;
|
|
5920
6201
|
}
|
|
5921
|
-
/*
|
|
5922
|
-
*
|
|
6202
|
+
/* Keep the key borrowed until insertion determines whether it already
|
|
6203
|
+
* exists or can reuse a recycled slot. Escaped keys own their decoded
|
|
6204
|
+
* string until the value has been parsed and inserted. */
|
|
5923
6205
|
size_t key_len = 0;
|
|
5924
|
-
char *key;
|
|
6206
|
+
const char *key;
|
|
6207
|
+
ScrStr *decoded_key = NULL;
|
|
5925
6208
|
{
|
|
5926
6209
|
const char *span;
|
|
5927
6210
|
size_t span_len;
|
|
@@ -5931,27 +6214,21 @@ static ScrDyn *scr_json_object(ScrJsonP *p) {
|
|
|
5931
6214
|
return NULL;
|
|
5932
6215
|
}
|
|
5933
6216
|
if (r > 0) {
|
|
5934
|
-
key =
|
|
5935
|
-
if (!key) scr_json_oom();
|
|
5936
|
-
memcpy(key, span, span_len);
|
|
5937
|
-
key[span_len] = '\0';
|
|
6217
|
+
key = span;
|
|
5938
6218
|
key_len = span_len;
|
|
5939
6219
|
} else {
|
|
5940
|
-
|
|
5941
|
-
if (!
|
|
6220
|
+
decoded_key = scr_json_string_slow(p, span_len);
|
|
6221
|
+
if (!decoded_key) {
|
|
5942
6222
|
scr_dyn_release(obj);
|
|
5943
6223
|
return NULL;
|
|
5944
6224
|
}
|
|
5945
|
-
key =
|
|
5946
|
-
|
|
5947
|
-
memcpy(key, ks->data, ks->len + 1);
|
|
5948
|
-
key_len = ks->len;
|
|
5949
|
-
scr_str_release(ks);
|
|
6225
|
+
key = decoded_key->data;
|
|
6226
|
+
key_len = decoded_key->len;
|
|
5950
6227
|
}
|
|
5951
6228
|
}
|
|
5952
6229
|
scr_json_ws(p);
|
|
5953
6230
|
if (p->pos >= p->len || p->s[p->pos] != ':') {
|
|
5954
|
-
|
|
6231
|
+
scr_str_release(decoded_key);
|
|
5955
6232
|
scr_dyn_release(obj);
|
|
5956
6233
|
if (p->pos >= p->len) scr_json_throw("Unexpected end of JSON input");
|
|
5957
6234
|
else scr_json_throw_pos("Expected ':' after property name", p->pos);
|
|
@@ -5960,11 +6237,12 @@ static ScrDyn *scr_json_object(ScrJsonP *p) {
|
|
|
5960
6237
|
p->pos++; /* ':' */
|
|
5961
6238
|
ScrDyn *value = scr_json_value(p);
|
|
5962
6239
|
if (!value) {
|
|
5963
|
-
|
|
6240
|
+
scr_str_release(decoded_key);
|
|
5964
6241
|
scr_dyn_release(obj);
|
|
5965
6242
|
return NULL;
|
|
5966
6243
|
}
|
|
5967
6244
|
scr_dyn_obj_put(obj, key, key_len, value); /* later duplicate keys win */
|
|
6245
|
+
scr_str_release(decoded_key);
|
|
5968
6246
|
scr_json_ws(p);
|
|
5969
6247
|
if (p->pos >= p->len) {
|
|
5970
6248
|
scr_dyn_release(obj);
|
|
@@ -6013,19 +6291,15 @@ static ScrDyn *scr_json_value(ScrJsonP *p) {
|
|
|
6013
6291
|
}
|
|
6014
6292
|
if (c == 't') {
|
|
6015
6293
|
if (!scr_json_lit(p, "true", 4)) return NULL;
|
|
6016
|
-
|
|
6017
|
-
d->v.b = true;
|
|
6018
|
-
return d;
|
|
6294
|
+
return scr_dyn_new_bool(true);
|
|
6019
6295
|
}
|
|
6020
6296
|
if (c == 'f') {
|
|
6021
6297
|
if (!scr_json_lit(p, "false", 5)) return NULL;
|
|
6022
|
-
|
|
6023
|
-
d->v.b = false;
|
|
6024
|
-
return d;
|
|
6298
|
+
return scr_dyn_new_bool(false);
|
|
6025
6299
|
}
|
|
6026
6300
|
if (c == 'n') {
|
|
6027
6301
|
if (!scr_json_lit(p, "null", 4)) return NULL;
|
|
6028
|
-
return
|
|
6302
|
+
return scr_dyn_new_null();
|
|
6029
6303
|
}
|
|
6030
6304
|
if (c == '-' || (c >= '0' && c <= '9')) return scr_json_number(p);
|
|
6031
6305
|
scr_json_throw_token(p);
|
|
@@ -6117,7 +6391,12 @@ static void scr_json_delete_member(ScrDyn *object, const ScrStr *key) {
|
|
|
6117
6391
|
ScrDynEntry removed = *entry;
|
|
6118
6392
|
memmove(entry, entry + 1, (object->v.obj.len - i - 1) * sizeof *entry);
|
|
6119
6393
|
object->v.obj.len--;
|
|
6120
|
-
free(
|
|
6394
|
+
free(object->v.obj.index);
|
|
6395
|
+
object->v.obj.index = NULL;
|
|
6396
|
+
/* The moved tail aliases a live entry; spare slots own only their own
|
|
6397
|
+
* retained key bytes, never a second pointer to a surviving property. */
|
|
6398
|
+
memset(&object->v.obj.entries[object->v.obj.len], 0, sizeof *entry);
|
|
6399
|
+
scr_dyn_key_release(removed.key, removed.key_len);
|
|
6121
6400
|
scr_dyn_release(removed.value);
|
|
6122
6401
|
scr_dyn_release(removed.getter);
|
|
6123
6402
|
scr_dyn_release(removed.setter);
|
|
@@ -6324,12 +6603,76 @@ static void scr_json_gap(ScrJsonBuf *buffer, const ScrStr *gap, size_t depth) {
|
|
|
6324
6603
|
for (size_t i = 0; i < depth; i++) scr_jb_write(buffer, gap->data, gap->len);
|
|
6325
6604
|
}
|
|
6326
6605
|
|
|
6606
|
+
/* These primitive values cannot invoke toJSON or mutate a containing frame.
|
|
6607
|
+
* BigInts, functions and every reference kind keep the protocol walk. */
|
|
6608
|
+
static bool scr_json_plain_value(const ScrDyn *value) {
|
|
6609
|
+
switch (value->kind) {
|
|
6610
|
+
case SCR_DYN_NULL: case SCR_DYN_UNDEF: case SCR_DYN_SYMBOL:
|
|
6611
|
+
case SCR_DYN_BOOL: case SCR_DYN_NUM: case SCR_DYN_STR: return true;
|
|
6612
|
+
default: return false;
|
|
6613
|
+
}
|
|
6614
|
+
}
|
|
6615
|
+
|
|
6616
|
+
/* Prove the whole immediate container before writing any of it. Without a
|
|
6617
|
+
* replacer or executable children, entries cannot change and borrowed names
|
|
6618
|
+
* need neither a key snapshot nor repeated lookups. Numeric property names
|
|
6619
|
+
* keep the ordinary sorted-key walk; sparse/accessor arrays keep Get. This
|
|
6620
|
+
* proof is repeated per call, so later mutations never leave a stale fact. */
|
|
6621
|
+
static bool scr_json_plain_container(const ScrDyn *value) {
|
|
6622
|
+
if (value->kind == SCR_DYN_ARR) {
|
|
6623
|
+
if (value->v.arr.presence || value->v.arr.properties) return false;
|
|
6624
|
+
for (size_t i = 0; i < value->v.arr.len; i++)
|
|
6625
|
+
if (!scr_json_plain_value(value->v.arr.items[i])) return false;
|
|
6626
|
+
return true;
|
|
6627
|
+
}
|
|
6628
|
+
if (value->kind != SCR_DYN_OBJ) return false;
|
|
6629
|
+
for (size_t i = 0; i < value->v.obj.len; i++) {
|
|
6630
|
+
const ScrDynEntry *entry = &value->v.obj.entries[i];
|
|
6631
|
+
if (!entry->enumerable) continue;
|
|
6632
|
+
double index;
|
|
6633
|
+
if (entry->accessor || !scr_json_plain_value(entry->value) ||
|
|
6634
|
+
scr_dyn_key_is_index(entry->key, entry->key_len, &index)) return false;
|
|
6635
|
+
}
|
|
6636
|
+
return true;
|
|
6637
|
+
}
|
|
6638
|
+
|
|
6639
|
+
static bool scr_json_plain_write(ScrJsonBuf *buffer, const ScrDyn *value,
|
|
6640
|
+
const ScrStr *gap, size_t depth) {
|
|
6641
|
+
bool array = value->kind == SCR_DYN_ARR;
|
|
6642
|
+
size_t length = array ? value->v.arr.len : value->v.obj.len;
|
|
6643
|
+
scr_jb_putc(buffer, array ? '[' : '{');
|
|
6644
|
+
bool first = true;
|
|
6645
|
+
for (size_t i = 0; i < length; i++) {
|
|
6646
|
+
const ScrDynEntry *entry = array ? NULL : &value->v.obj.entries[i];
|
|
6647
|
+
if (entry && !entry->enumerable) continue;
|
|
6648
|
+
const ScrDyn *child = array ? value->v.arr.items[i] : entry->value;
|
|
6649
|
+
bool omitted = scr_json_omitted(child);
|
|
6650
|
+
if (!array && omitted) continue;
|
|
6651
|
+
if (!omitted && !scr_json_callback_depth(depth + 1)) return false;
|
|
6652
|
+
if (!first) scr_jb_putc(buffer, ',');
|
|
6653
|
+
first = false;
|
|
6654
|
+
scr_json_gap(buffer, gap, depth + 1);
|
|
6655
|
+
if (entry) {
|
|
6656
|
+
scr_jb_put_json_span(buffer, entry->key, entry->key_len);
|
|
6657
|
+
scr_jb_putc(buffer, ':');
|
|
6658
|
+
if (gap->len) scr_jb_putc(buffer, ' ');
|
|
6659
|
+
}
|
|
6660
|
+
if (omitted) scr_jb_puts(buffer, "null");
|
|
6661
|
+
else scr_dyn_json_write(buffer, child);
|
|
6662
|
+
}
|
|
6663
|
+
if (!first) scr_json_gap(buffer, gap, depth);
|
|
6664
|
+
scr_jb_putc(buffer, array ? ']' : '}');
|
|
6665
|
+
return true;
|
|
6666
|
+
}
|
|
6667
|
+
|
|
6327
6668
|
static bool scr_json_replaced_write(ScrJsonBuf *buffer, const ScrDyn *value,
|
|
6328
6669
|
const ScrDyn *replacer, const ScrStr *gap, size_t depth) {
|
|
6329
6670
|
if (!scr_json_callback_depth(depth)) return false;
|
|
6330
6671
|
ScrDyn *view = value->kind == SCR_DYN_TYPED_REF ? scr_dyn_typed_ref_materialize(value) : NULL;
|
|
6331
6672
|
const ScrDyn *observed = view ? view : value;
|
|
6332
6673
|
if (scr_exc_pending()) { scr_dyn_release(view); return false; }
|
|
6674
|
+
if (!view && replacer->kind == SCR_DYN_UNDEF && scr_json_plain_container(value))
|
|
6675
|
+
return scr_json_plain_write(buffer, value, gap, depth);
|
|
6333
6676
|
if (observed->kind != SCR_DYN_OBJ && observed->kind != SCR_DYN_ARR && observed->kind != SCR_DYN_BYTES) {
|
|
6334
6677
|
/* Engine objects need an engine callback bridge, not an opaque JSON
|
|
6335
6678
|
* splice: that would silently skip all their children. */
|
|
@@ -6820,9 +7163,8 @@ static ScrDyn *scr_dyn_obj_read_receiver(const ScrDyn *d, const char *key, size_
|
|
|
6820
7163
|
continue;
|
|
6821
7164
|
}
|
|
6822
7165
|
if (current->kind != SCR_DYN_OBJ) continue;
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
if (entry->key_len != key_len || memcmp(entry->key, key, key_len) != 0) continue;
|
|
7166
|
+
ScrDynEntry *entry = scr_dyn_find_entry(current, key, key_len);
|
|
7167
|
+
if (entry) {
|
|
6826
7168
|
if (!entry->accessor) return scr_dyn_retain(entry->value);
|
|
6827
7169
|
if (!entry->getter) return scr_dyn_retain(scr_dyn_undefined());
|
|
6828
7170
|
ScrDyn *getter = scr_dyn_retain(entry->getter);
|
|
@@ -7090,15 +7432,9 @@ bool scr_dyn_reflect_set(ScrDyn *target, ScrDyn *raw_key, ScrDyn *value, ScrDyn
|
|
|
7090
7432
|
|
|
7091
7433
|
/* The array-index test (ECMA: a canonical numeric string < 2^32-1). */
|
|
7092
7434
|
static bool scr_dyn_key_is_index(const char *key, size_t len, double *out) {
|
|
7093
|
-
|
|
7094
|
-
if (key
|
|
7095
|
-
|
|
7096
|
-
for (size_t i = 0; i < len; i++) {
|
|
7097
|
-
if (key[i] < '0' || key[i] > '9') return false;
|
|
7098
|
-
v = v * 10 + (key[i] - '0');
|
|
7099
|
-
}
|
|
7100
|
-
if (v > 4294967294.0) return false;
|
|
7101
|
-
*out = v;
|
|
7435
|
+
uint32_t index;
|
|
7436
|
+
if (!scr_key_array_index(key, len, &index)) return false;
|
|
7437
|
+
*out = index;
|
|
7102
7438
|
return true;
|
|
7103
7439
|
}
|
|
7104
7440
|
|
|
@@ -7116,34 +7452,31 @@ static ScrDyn *scr_dyn_objwalk_key(const char *key, size_t key_len) {
|
|
|
7116
7452
|
ScrDyn *scr_dyn_obj_own_keys(const ScrDyn *v) {
|
|
7117
7453
|
size_t n = v->v.obj.len;
|
|
7118
7454
|
ScrDyn *keys = scr_dyn_new_arr();
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
if (!is_index || !idx) scr_trap("scriptc: out of memory\n");
|
|
7122
|
-
size_t index_count = 0;
|
|
7455
|
+
ScrKeyIndex *indices = NULL;
|
|
7456
|
+
size_t count = 0;
|
|
7123
7457
|
for (size_t i = 0; i < n; i++) {
|
|
7124
|
-
const ScrDynEntry *
|
|
7125
|
-
|
|
7126
|
-
if (
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
scr_dyn_arr_push(keys, scr_dyn_objwalk_key(
|
|
7138
|
-
|
|
7139
|
-
|
|
7458
|
+
const ScrDynEntry *entry = &v->v.obj.entries[i];
|
|
7459
|
+
uint32_t index;
|
|
7460
|
+
if (!scr_key_array_index(entry->key, entry->key_len, &index)) continue;
|
|
7461
|
+
if (!indices) {
|
|
7462
|
+
if (n > SIZE_MAX / sizeof *indices) scr_json_oom();
|
|
7463
|
+
indices = malloc(n * sizeof *indices);
|
|
7464
|
+
if (!indices) scr_json_oom();
|
|
7465
|
+
}
|
|
7466
|
+
indices[count++] = (ScrKeyIndex){index, i};
|
|
7467
|
+
}
|
|
7468
|
+
scr_key_index_sort(indices, count);
|
|
7469
|
+
for (size_t i = 0; i < count; i++) {
|
|
7470
|
+
const ScrDynEntry *entry = &v->v.obj.entries[indices[i].entry];
|
|
7471
|
+
scr_dyn_arr_push(keys, scr_dyn_objwalk_key(entry->key, entry->key_len));
|
|
7472
|
+
}
|
|
7473
|
+
free(indices);
|
|
7140
7474
|
for (size_t i = 0; i < n; i++) {
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7475
|
+
const ScrDynEntry *entry = &v->v.obj.entries[i];
|
|
7476
|
+
uint32_t index;
|
|
7477
|
+
if (!scr_key_array_index(entry->key, entry->key_len, &index))
|
|
7478
|
+
scr_dyn_arr_push(keys, scr_dyn_objwalk_key(entry->key, entry->key_len));
|
|
7144
7479
|
}
|
|
7145
|
-
free(is_index);
|
|
7146
|
-
free(idx);
|
|
7147
7480
|
return keys;
|
|
7148
7481
|
}
|
|
7149
7482
|
|