@scriptc/cli-darwin-arm64 0.2.4 → 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-arm64/artifacts/dev/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic-text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_util_compare.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_array.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_bytes_io.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_copying.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_json.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_lib.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_map.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_number.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_regex.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_string.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_util_compare.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic-text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
- package/dist/lib/runtime-darwin-arm64/package.json +1 -1
- package/dist/lib/runtime-darwin-arm64/runtime-pack.json +181 -181
- package/dist/lib/runtime-sources/package.json +1 -1
- package/dist/lib/runtime-sources/src/scr_array.c +267 -6
- package/dist/lib/runtime-sources/src/scr_async.test.ts +16 -6
- 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_json.c +479 -150
- 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 +132 -73
- package/dist/lib/runtime-sources/src/scr_map.c +50 -66
- 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 +238 -167
- package/dist/lib/runtime-sources/src/scr_runtime.h +37 -6
- package/dist/lib/runtime-sources/src/scr_string.c +155 -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) ───── */
|
|
@@ -1088,6 +1318,22 @@ ScrDyn *scr_dyn_symbol_get(const ScrDyn *value, const ScrStr *key) {
|
|
|
1088
1318
|
}
|
|
1089
1319
|
|
|
1090
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
|
+
}
|
|
1091
1337
|
ScrDyn *d = scr_dyn_alloc(SCR_DYN_NUM);
|
|
1092
1338
|
d->v.num = n;
|
|
1093
1339
|
return d;
|
|
@@ -2827,14 +3073,10 @@ static void scr_dyn_handle_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) {
|
|
|
2827
3073
|
scr_throw_error(SCR_ERR_ERROR, scr_jb_finish(&b));
|
|
2828
3074
|
}
|
|
2829
3075
|
|
|
2830
|
-
/* Public obj insertion
|
|
2831
|
-
*
|
|
3076
|
+
/* Public obj insertion copies a new key only when needed, owns the value,
|
|
3077
|
+
* and preserves the existing entry for duplicate keys. */
|
|
2832
3078
|
void scr_dyn_obj_set(ScrDyn *obj, const char *key, size_t key_len, ScrDyn *value) {
|
|
2833
|
-
|
|
2834
|
-
if (!copy) scr_json_oom();
|
|
2835
|
-
memcpy(copy, key, key_len);
|
|
2836
|
-
copy[key_len] = '\0';
|
|
2837
|
-
scr_dyn_obj_put(obj, copy, key_len, value);
|
|
3079
|
+
scr_dyn_obj_put(obj, key, key_len, value);
|
|
2838
3080
|
}
|
|
2839
3081
|
|
|
2840
3082
|
static ScrStr *scr_dyn_property_key(const ScrDyn *key) {
|
|
@@ -2842,12 +3084,7 @@ static ScrStr *scr_dyn_property_key(const ScrDyn *key) {
|
|
|
2842
3084
|
}
|
|
2843
3085
|
|
|
2844
3086
|
static ScrDynEntry *scr_dyn_entry(ScrDyn *obj, const ScrStr *key) {
|
|
2845
|
-
|
|
2846
|
-
for (size_t i = 0; i < obj->v.obj.len; i++) {
|
|
2847
|
-
ScrDynEntry *entry = &obj->v.obj.entries[i];
|
|
2848
|
-
if (entry->key_len == key->len && memcmp(entry->key, key->data, key->len) == 0) return entry;
|
|
2849
|
-
}
|
|
2850
|
-
return NULL;
|
|
3087
|
+
return scr_dyn_find_entry(obj, key->data, key->len);
|
|
2851
3088
|
}
|
|
2852
3089
|
|
|
2853
3090
|
static bool scr_dyn_property_same_value(const ScrDyn *a, const ScrDyn *b) {
|
|
@@ -3030,6 +3267,7 @@ typedef struct {
|
|
|
3030
3267
|
unsigned kind; /* 0: array, 1: string, 2: Map, 3: Set */
|
|
3031
3268
|
unsigned selection; /* 0: values, 1: keys, 2: entries */
|
|
3032
3269
|
bool array_like;
|
|
3270
|
+
bool step_done; /* completion of the last compiler-consumed step */
|
|
3033
3271
|
double limit;
|
|
3034
3272
|
} ScrNativeIterator;
|
|
3035
3273
|
|
|
@@ -3071,21 +3309,18 @@ static void scr_native_iterator_release(void *ptr) {
|
|
|
3071
3309
|
free(iterator);
|
|
3072
3310
|
}
|
|
3073
3311
|
|
|
3074
|
-
|
|
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) {
|
|
3075
3316
|
ScrDyn *source = iterator->source;
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
if (scr_dyn_truthy(scr_dyn_obj_get(result, "done", 4))) {
|
|
3080
|
-
iterator->source = NULL;
|
|
3081
|
-
scr_dyn_release(source);
|
|
3082
|
-
}
|
|
3083
|
-
return result;
|
|
3084
|
-
}
|
|
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);
|
|
3085
3320
|
/* Native array capsules retain the live array, so refresh its view at each
|
|
3086
3321
|
* step rather than iterating a snapshot taken when the iterator opened. */
|
|
3087
3322
|
ScrDyn *view = source && source->kind == SCR_DYN_TYPED_REF ? scr_dyn_typed_ref_materialize(source) : NULL;
|
|
3088
|
-
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; }
|
|
3089
3324
|
ScrDyn *items = view ? view : source;
|
|
3090
3325
|
ScrMap *map = source && (iterator->kind == 2 || iterator->kind == 3) ? source->v.handle.ptr : NULL;
|
|
3091
3326
|
if (map) while (iterator->index < scr_map_iter_count(map) && !scr_map_iter_live(map, iterator->index)) iterator->index++;
|
|
@@ -3131,6 +3366,24 @@ static ScrDyn *scr_native_iterator_next(ScrNativeIterator *iterator) {
|
|
|
3131
3366
|
scr_str_release(point);
|
|
3132
3367
|
}
|
|
3133
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);
|
|
3134
3387
|
if (!value) return NULL;
|
|
3135
3388
|
ScrDyn *result = scr_dyn_new_obj();
|
|
3136
3389
|
scr_dyn_obj_set(result, "value", 5, value);
|
|
@@ -3180,6 +3433,28 @@ static ScrDyn *scr_native_handle_iterator_next_call(ScrClosure *closure, ScrDyn
|
|
|
3180
3433
|
return scr_native_iterator_next_checked(4);
|
|
3181
3434
|
}
|
|
3182
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
|
+
|
|
3183
3458
|
static ScrDyn *scr_native_iterator_get(void *ptr, const char *key, size_t len) {
|
|
3184
3459
|
if (len != 4 || memcmp(key, "next", 4)) return NULL;
|
|
3185
3460
|
unsigned kind = ((ScrNativeIterator *)ptr)->kind;
|
|
@@ -5483,10 +5758,7 @@ void scr_jb_put_dyn(ScrJsonBuf *b, const ScrDyn *d) {
|
|
|
5483
5758
|
if (e->value->kind == SCR_DYN_JSVAL && scr_dyn_isl_typeof_is(e->value, "function")) continue; /* engine functions drop too */
|
|
5484
5759
|
if (!first) scr_jb_putc(b, ',');
|
|
5485
5760
|
first = false;
|
|
5486
|
-
|
|
5487
|
-
ScrStr *k = scr_str_new(e->key, e->key_len);
|
|
5488
|
-
scr_jb_put_json_str(b, k);
|
|
5489
|
-
scr_str_release(k);
|
|
5761
|
+
scr_jb_put_json_span(b, e->key, e->key_len);
|
|
5490
5762
|
scr_jb_putc(b, ':');
|
|
5491
5763
|
scr_jb_put_dyn(b, e->value);
|
|
5492
5764
|
}
|
|
@@ -5647,8 +5919,10 @@ static int32_t scr_json_hex4(ScrJsonP *p) {
|
|
|
5647
5919
|
* slow path would produce) and returns -1. */
|
|
5648
5920
|
static int scr_json_string_span(ScrJsonP *p, const char **span,
|
|
5649
5921
|
size_t *span_len) {
|
|
5650
|
-
|
|
5651
|
-
|
|
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) {
|
|
5652
5926
|
unsigned char c = (unsigned char)p->s[i];
|
|
5653
5927
|
if (c == '"') {
|
|
5654
5928
|
*span = p->s + p->pos + 1;
|
|
@@ -5661,7 +5935,6 @@ static int scr_json_string_span(ScrJsonP *p, const char **span,
|
|
|
5661
5935
|
scr_json_throw_pos("Bad control character in string literal", i);
|
|
5662
5936
|
return -1;
|
|
5663
5937
|
}
|
|
5664
|
-
i++;
|
|
5665
5938
|
}
|
|
5666
5939
|
scr_json_throw_pos("Unterminated string", p->pos);
|
|
5667
5940
|
return -1;
|
|
@@ -5669,11 +5942,13 @@ static int scr_json_string_span(ScrJsonP *p, const char **span,
|
|
|
5669
5942
|
|
|
5670
5943
|
/* Slow path: parses the string literal at p->pos (the opening quote),
|
|
5671
5944
|
* decoding escapes, into a +1 ScrStr. NULL on error (thrown). */
|
|
5672
|
-
static ScrStr *scr_json_string_slow(ScrJsonP *p) {
|
|
5945
|
+
static ScrStr *scr_json_string_slow(ScrJsonP *p, size_t prefix) {
|
|
5673
5946
|
size_t open = p->pos;
|
|
5674
5947
|
p->pos++; /* opening quote */
|
|
5675
5948
|
ScrJsonBuf b;
|
|
5676
5949
|
scr_jb_init(&b);
|
|
5950
|
+
scr_jb_write(&b, p->s + p->pos, prefix);
|
|
5951
|
+
p->pos += prefix;
|
|
5677
5952
|
for (;;) {
|
|
5678
5953
|
if (p->pos >= p->len) {
|
|
5679
5954
|
scr_jb_dispose(&b);
|
|
@@ -5691,8 +5966,9 @@ static ScrStr *scr_json_string_slow(ScrJsonP *p) {
|
|
|
5691
5966
|
return NULL;
|
|
5692
5967
|
}
|
|
5693
5968
|
if (c != '\\') {
|
|
5694
|
-
|
|
5695
|
-
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;
|
|
5696
5972
|
continue;
|
|
5697
5973
|
}
|
|
5698
5974
|
p->pos++; /* backslash */
|
|
@@ -5767,7 +6043,7 @@ static ScrStr *scr_json_string_scr(ScrJsonP *p) {
|
|
|
5767
6043
|
int r = scr_json_string_span(p, &span, &span_len);
|
|
5768
6044
|
if (r < 0) return NULL;
|
|
5769
6045
|
if (r > 0) return scr_str_new(span, span_len);
|
|
5770
|
-
return scr_json_string_slow(p);
|
|
6046
|
+
return scr_json_string_slow(p, span_len);
|
|
5771
6047
|
}
|
|
5772
6048
|
|
|
5773
6049
|
static ScrDyn *scr_json_value(ScrJsonP *p);
|
|
@@ -5846,20 +6122,17 @@ static ScrDyn *scr_json_number(ScrJsonP *p) {
|
|
|
5846
6122
|
}
|
|
5847
6123
|
exp10 += eneg ? -ev : ev;
|
|
5848
6124
|
}
|
|
5849
|
-
ScrDyn *d = scr_dyn_alloc(SCR_DYN_NUM);
|
|
5850
6125
|
if (precise && exp10 >= -22 && exp10 <= 22) {
|
|
5851
6126
|
double v = (double)mant; /* exact: mant < 10^15 < 2^53 */
|
|
5852
6127
|
if (exp10 > 0) v *= scr_json_pow10[exp10];
|
|
5853
6128
|
else if (exp10 < 0) v /= scr_json_pow10[-exp10];
|
|
5854
|
-
|
|
5855
|
-
return d;
|
|
6129
|
+
return scr_dyn_new_num(neg ? -v : v);
|
|
5856
6130
|
}
|
|
5857
6131
|
/* The validated span re-parses with strtod (correctly rounded, and the
|
|
5858
6132
|
* grammar above is a strict subset of what strtod accepts). The ScrStr
|
|
5859
6133
|
* data is NUL-terminated, and strtod stops at the first non-number char,
|
|
5860
6134
|
* so parsing from `start` reads exactly the validated token. */
|
|
5861
|
-
|
|
5862
|
-
return d;
|
|
6135
|
+
return scr_dyn_new_num(strtod(p->s + start, NULL));
|
|
5863
6136
|
}
|
|
5864
6137
|
|
|
5865
6138
|
static bool scr_json_lit(ScrJsonP *p, const char *word, size_t n) {
|
|
@@ -5926,10 +6199,12 @@ static ScrDyn *scr_json_object(ScrJsonP *p) {
|
|
|
5926
6199
|
scr_json_throw_pos("Expected property name or '}'", p->pos);
|
|
5927
6200
|
return NULL;
|
|
5928
6201
|
}
|
|
5929
|
-
/*
|
|
5930
|
-
*
|
|
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. */
|
|
5931
6205
|
size_t key_len = 0;
|
|
5932
|
-
char *key;
|
|
6206
|
+
const char *key;
|
|
6207
|
+
ScrStr *decoded_key = NULL;
|
|
5933
6208
|
{
|
|
5934
6209
|
const char *span;
|
|
5935
6210
|
size_t span_len;
|
|
@@ -5939,27 +6214,21 @@ static ScrDyn *scr_json_object(ScrJsonP *p) {
|
|
|
5939
6214
|
return NULL;
|
|
5940
6215
|
}
|
|
5941
6216
|
if (r > 0) {
|
|
5942
|
-
key =
|
|
5943
|
-
if (!key) scr_json_oom();
|
|
5944
|
-
memcpy(key, span, span_len);
|
|
5945
|
-
key[span_len] = '\0';
|
|
6217
|
+
key = span;
|
|
5946
6218
|
key_len = span_len;
|
|
5947
6219
|
} else {
|
|
5948
|
-
|
|
5949
|
-
if (!
|
|
6220
|
+
decoded_key = scr_json_string_slow(p, span_len);
|
|
6221
|
+
if (!decoded_key) {
|
|
5950
6222
|
scr_dyn_release(obj);
|
|
5951
6223
|
return NULL;
|
|
5952
6224
|
}
|
|
5953
|
-
key =
|
|
5954
|
-
|
|
5955
|
-
memcpy(key, ks->data, ks->len + 1);
|
|
5956
|
-
key_len = ks->len;
|
|
5957
|
-
scr_str_release(ks);
|
|
6225
|
+
key = decoded_key->data;
|
|
6226
|
+
key_len = decoded_key->len;
|
|
5958
6227
|
}
|
|
5959
6228
|
}
|
|
5960
6229
|
scr_json_ws(p);
|
|
5961
6230
|
if (p->pos >= p->len || p->s[p->pos] != ':') {
|
|
5962
|
-
|
|
6231
|
+
scr_str_release(decoded_key);
|
|
5963
6232
|
scr_dyn_release(obj);
|
|
5964
6233
|
if (p->pos >= p->len) scr_json_throw("Unexpected end of JSON input");
|
|
5965
6234
|
else scr_json_throw_pos("Expected ':' after property name", p->pos);
|
|
@@ -5968,11 +6237,12 @@ static ScrDyn *scr_json_object(ScrJsonP *p) {
|
|
|
5968
6237
|
p->pos++; /* ':' */
|
|
5969
6238
|
ScrDyn *value = scr_json_value(p);
|
|
5970
6239
|
if (!value) {
|
|
5971
|
-
|
|
6240
|
+
scr_str_release(decoded_key);
|
|
5972
6241
|
scr_dyn_release(obj);
|
|
5973
6242
|
return NULL;
|
|
5974
6243
|
}
|
|
5975
6244
|
scr_dyn_obj_put(obj, key, key_len, value); /* later duplicate keys win */
|
|
6245
|
+
scr_str_release(decoded_key);
|
|
5976
6246
|
scr_json_ws(p);
|
|
5977
6247
|
if (p->pos >= p->len) {
|
|
5978
6248
|
scr_dyn_release(obj);
|
|
@@ -6121,7 +6391,12 @@ static void scr_json_delete_member(ScrDyn *object, const ScrStr *key) {
|
|
|
6121
6391
|
ScrDynEntry removed = *entry;
|
|
6122
6392
|
memmove(entry, entry + 1, (object->v.obj.len - i - 1) * sizeof *entry);
|
|
6123
6393
|
object->v.obj.len--;
|
|
6124
|
-
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);
|
|
6125
6400
|
scr_dyn_release(removed.value);
|
|
6126
6401
|
scr_dyn_release(removed.getter);
|
|
6127
6402
|
scr_dyn_release(removed.setter);
|
|
@@ -6328,12 +6603,76 @@ static void scr_json_gap(ScrJsonBuf *buffer, const ScrStr *gap, size_t depth) {
|
|
|
6328
6603
|
for (size_t i = 0; i < depth; i++) scr_jb_write(buffer, gap->data, gap->len);
|
|
6329
6604
|
}
|
|
6330
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
|
+
|
|
6331
6668
|
static bool scr_json_replaced_write(ScrJsonBuf *buffer, const ScrDyn *value,
|
|
6332
6669
|
const ScrDyn *replacer, const ScrStr *gap, size_t depth) {
|
|
6333
6670
|
if (!scr_json_callback_depth(depth)) return false;
|
|
6334
6671
|
ScrDyn *view = value->kind == SCR_DYN_TYPED_REF ? scr_dyn_typed_ref_materialize(value) : NULL;
|
|
6335
6672
|
const ScrDyn *observed = view ? view : value;
|
|
6336
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);
|
|
6337
6676
|
if (observed->kind != SCR_DYN_OBJ && observed->kind != SCR_DYN_ARR && observed->kind != SCR_DYN_BYTES) {
|
|
6338
6677
|
/* Engine objects need an engine callback bridge, not an opaque JSON
|
|
6339
6678
|
* splice: that would silently skip all their children. */
|
|
@@ -6824,9 +7163,8 @@ static ScrDyn *scr_dyn_obj_read_receiver(const ScrDyn *d, const char *key, size_
|
|
|
6824
7163
|
continue;
|
|
6825
7164
|
}
|
|
6826
7165
|
if (current->kind != SCR_DYN_OBJ) continue;
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
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) {
|
|
6830
7168
|
if (!entry->accessor) return scr_dyn_retain(entry->value);
|
|
6831
7169
|
if (!entry->getter) return scr_dyn_retain(scr_dyn_undefined());
|
|
6832
7170
|
ScrDyn *getter = scr_dyn_retain(entry->getter);
|
|
@@ -7094,15 +7432,9 @@ bool scr_dyn_reflect_set(ScrDyn *target, ScrDyn *raw_key, ScrDyn *value, ScrDyn
|
|
|
7094
7432
|
|
|
7095
7433
|
/* The array-index test (ECMA: a canonical numeric string < 2^32-1). */
|
|
7096
7434
|
static bool scr_dyn_key_is_index(const char *key, size_t len, double *out) {
|
|
7097
|
-
|
|
7098
|
-
if (key
|
|
7099
|
-
|
|
7100
|
-
for (size_t i = 0; i < len; i++) {
|
|
7101
|
-
if (key[i] < '0' || key[i] > '9') return false;
|
|
7102
|
-
v = v * 10 + (key[i] - '0');
|
|
7103
|
-
}
|
|
7104
|
-
if (v > 4294967294.0) return false;
|
|
7105
|
-
*out = v;
|
|
7435
|
+
uint32_t index;
|
|
7436
|
+
if (!scr_key_array_index(key, len, &index)) return false;
|
|
7437
|
+
*out = index;
|
|
7106
7438
|
return true;
|
|
7107
7439
|
}
|
|
7108
7440
|
|
|
@@ -7120,34 +7452,31 @@ static ScrDyn *scr_dyn_objwalk_key(const char *key, size_t key_len) {
|
|
|
7120
7452
|
ScrDyn *scr_dyn_obj_own_keys(const ScrDyn *v) {
|
|
7121
7453
|
size_t n = v->v.obj.len;
|
|
7122
7454
|
ScrDyn *keys = scr_dyn_new_arr();
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
if (!is_index || !idx) scr_trap("scriptc: out of memory\n");
|
|
7126
|
-
size_t index_count = 0;
|
|
7455
|
+
ScrKeyIndex *indices = NULL;
|
|
7456
|
+
size_t count = 0;
|
|
7127
7457
|
for (size_t i = 0; i < n; i++) {
|
|
7128
|
-
const ScrDynEntry *
|
|
7129
|
-
|
|
7130
|
-
if (
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
scr_dyn_arr_push(keys, scr_dyn_objwalk_key(
|
|
7142
|
-
|
|
7143
|
-
|
|
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);
|
|
7144
7474
|
for (size_t i = 0; i < n; i++) {
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
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));
|
|
7148
7479
|
}
|
|
7149
|
-
free(is_index);
|
|
7150
|
-
free(idx);
|
|
7151
7480
|
return keys;
|
|
7152
7481
|
}
|
|
7153
7482
|
|