visaljs 0.0.8 → 0.0.10
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/README.md +6 -3
- package/THIRD_PARTY_LICENSES +1 -1
- package/cli.js +2782 -770
- package/node/include/js_node.h +1 -0
- package/node/prelude/_http_agent.js +5 -0
- package/node/prelude/_http_server.js +7 -0
- package/node/prelude/dns.js +7 -1
- package/node/prelude/events.js +7 -3
- package/node/prelude/http.js +693 -285
- package/node/prelude/https.js +14 -3
- package/node/prelude/internal/events_extras.js +2 -5
- package/node/prelude/internal/features.js +17 -0
- package/node/prelude/internal/http_agent.js +88 -2
- package/node/prelude/internal/http_chars.ts +22 -0
- package/node/prelude/internal/http_symbols.js +3 -0
- package/node/prelude/internal/net_bound.js +123 -0
- package/node/prelude/internal/net_onread.js +73 -0
- package/node/prelude/internal/net_tos.js +54 -0
- package/node/prelude/internal/perf_observe.js +7 -1
- package/node/prelude/internal/tls/wrap.js +2 -1
- package/node/prelude/module.js +42 -18
- package/node/prelude/net.js +212 -61
- package/node/prelude/perf_hooks.js +4 -0
- package/node/src/host_http.c +151 -21
- package/node/src/host_net.c +75 -9
- package/node/src/host_sys.c +9 -0
- package/package.json +1 -1
- package/prebuilt/win32-x64/boringssl-ce573858472b7fc9.a +0 -0
- package/prebuilt/win32-x64/brotli-20d86a61f30baeb3.a +0 -0
- package/prebuilt/win32-x64/uv-b11bae65d7fced82.a +0 -0
- package/prebuilt/win32-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/win32-x64/zstd-015315ac7c784c73.a +0 -0
- package/runtime/include/js_atoms.h +4 -0
- package/runtime/include/js_buffer.h +185 -0
- package/runtime/include/js_builtins.h +53 -0
- package/runtime/include/js_exception.h +3 -0
- package/runtime/include/js_function.h +1 -1
- package/runtime/include/js_object.h +21 -3
- package/runtime/include/js_ops.h +27 -0
- package/runtime/include/js_runtime.h +1 -0
- package/runtime/include/js_web.h +3 -3
- package/runtime/include/jscontext.h +15 -0
- package/runtime/src/atoms.c +37 -3
- package/runtime/src/builtins.c +17 -4
- package/runtime/src/events.c +19 -0
- package/runtime/src/function.c +37 -10
- package/runtime/src/gc.c +7 -5
- package/runtime/src/internal.h +4 -4
- package/runtime/src/iterator.c +5 -1
- package/runtime/src/object.c +42 -6
- package/runtime/src/regexp.c +569 -12
- package/runtime/src/shape.c +68 -1
- package/runtime/src/typedarray.c +23 -0
- package/runtime/src/weak.c +95 -24
package/runtime/src/shape.c
CHANGED
|
@@ -119,6 +119,35 @@ static void roots_put(JSShape **table, size_t cap, JSShape *s) {
|
|
|
119
119
|
table[i] = s;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
/* Removes root `s`, which is in the table. Linear probing: later entries of its probe run move back
|
|
123
|
+
* into the hole, so that every lookup still finds them. */
|
|
124
|
+
static void roots_remove(JSShapeRegistry *r, const JSShape *s) {
|
|
125
|
+
size_t mask = r->root_cap - 1, i = root_hash(s->cls, s->proto, s->inline_cap) & mask;
|
|
126
|
+
while (r->roots[i] != s) i = (i + 1) & mask;
|
|
127
|
+
r->roots[i] = NULL;
|
|
128
|
+
r->root_count--;
|
|
129
|
+
for (size_t j = (i + 1) & mask; r->roots[j]; j = (j + 1) & mask) {
|
|
130
|
+
JSShape *x = r->roots[j];
|
|
131
|
+
size_t home = root_hash(x->cls, x->proto, x->inline_cap) & mask;
|
|
132
|
+
bool stays = i <= j ? (i < home && home <= j) : (i < home || home <= j);
|
|
133
|
+
if (!stays) {
|
|
134
|
+
r->roots[i] = x;
|
|
135
|
+
r->roots[j] = NULL;
|
|
136
|
+
i = j;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* A shape made since the last collection: a young collection checks it. */
|
|
142
|
+
static void note_young(JSShapeRegistry *r, JSShape *s) {
|
|
143
|
+
if (r->young_count == r->young_cap) {
|
|
144
|
+
r->young_cap = r->young_cap ? r->young_cap * 2 : 256;
|
|
145
|
+
r->young = realloc(r->young, r->young_cap * sizeof *r->young);
|
|
146
|
+
if (!r->young) js_fatal("out of memory (shape registry)");
|
|
147
|
+
}
|
|
148
|
+
r->young[r->young_count++] = s;
|
|
149
|
+
}
|
|
150
|
+
|
|
122
151
|
static void roots_rebuild(JSShapeRegistry *r, size_t cap, bool (*keep)(JSShape *)) {
|
|
123
152
|
JSShape **table = calloc(cap, sizeof *table);
|
|
124
153
|
if (!table) js_fatal("out of memory (shape roots)");
|
|
@@ -152,6 +181,7 @@ JSShape *js_shape_root(JSContext *ctx, const JSClass *cls, JSValue proto, uint16
|
|
|
152
181
|
if (2 * (r->root_count + 1) > r->root_cap) roots_rebuild(r, r->root_cap ? r->root_cap * 2 : 64, NULL);
|
|
153
182
|
roots_put(r->roots, r->root_cap, s);
|
|
154
183
|
r->root_count++;
|
|
184
|
+
note_young(r, s);
|
|
155
185
|
return s;
|
|
156
186
|
}
|
|
157
187
|
|
|
@@ -196,6 +226,7 @@ JSShape *js_shape_add(JSContext *ctx, JSShape *shape, JSValue key, uint8_t attrs
|
|
|
196
226
|
if (!t->items) js_fatal("out of memory (transitions)");
|
|
197
227
|
}
|
|
198
228
|
t->items[t->count++] = (JSTransition){ key, attrs, child };
|
|
229
|
+
note_young(&ctx->rt->shapes, child);
|
|
199
230
|
return child;
|
|
200
231
|
}
|
|
201
232
|
|
|
@@ -239,8 +270,42 @@ JSPropTable *js_shape_entries(JSShape *s) {
|
|
|
239
270
|
|
|
240
271
|
static bool shape_alive(JSShape *s) { return js_gc_is_marked(&s->cell); }
|
|
241
272
|
|
|
242
|
-
|
|
273
|
+
/* A young collection: an old shape stays marked until a full collection, so only the shapes made
|
|
274
|
+
* since the last collection can be dead. A dead root leaves the roots table; a dead child leaves
|
|
275
|
+
* its parent's transitions (an emptied table stays, for the next full collection to free); a dead
|
|
276
|
+
* parent, made since the last collection too, leaves the tail of `parents`. */
|
|
277
|
+
static void sweep_young(JSShapeRegistry *r) {
|
|
278
|
+
for (size_t i = 0; i < r->young_count; i++) {
|
|
279
|
+
JSShape *s = r->young[i];
|
|
280
|
+
if (shape_alive(s)) continue;
|
|
281
|
+
if (!s->parent) {
|
|
282
|
+
roots_remove(r, s);
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
JSShape *p = s->parent;
|
|
286
|
+
if (!shape_alive(p)) continue; /* its transitions are freed with it */
|
|
287
|
+
JSTransitions *t = p->transitions;
|
|
288
|
+
for (uint32_t k = 0; k < t->count; k++)
|
|
289
|
+
if (t->items[k].child == s) {
|
|
290
|
+
t->items[k] = t->items[--t->count];
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
size_t kept = r->old_parents;
|
|
295
|
+
for (size_t i = r->old_parents; i < r->parent_count; i++)
|
|
296
|
+
if (shape_alive(r->parents[i])) r->parents[kept++] = r->parents[i];
|
|
297
|
+
r->parent_count = kept;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
void js_shapes_sweep(JSRuntime *rt, bool full) {
|
|
243
301
|
JSShapeRegistry *r = &rt->shapes;
|
|
302
|
+
if (!full) {
|
|
303
|
+
sweep_young(r);
|
|
304
|
+
r->young_count = 0;
|
|
305
|
+
r->old_parents = r->parent_count;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
r->young_count = 0;
|
|
244
309
|
/* a dead parent's children are dead too: they point to it */
|
|
245
310
|
size_t kept = 0;
|
|
246
311
|
for (size_t i = 0; i < r->parent_count; i++) {
|
|
@@ -259,6 +324,7 @@ void js_shapes_sweep(JSRuntime *rt) {
|
|
|
259
324
|
}
|
|
260
325
|
}
|
|
261
326
|
r->parent_count = kept;
|
|
327
|
+
r->old_parents = kept;
|
|
262
328
|
if (r->root_cap) {
|
|
263
329
|
size_t live = 0;
|
|
264
330
|
for (size_t i = 0; i < r->root_cap; i++) live += r->roots[i] && shape_alive(r->roots[i]);
|
|
@@ -271,6 +337,7 @@ void js_shapes_sweep(JSRuntime *rt) {
|
|
|
271
337
|
void js_shapes_free(JSRuntime *rt) {
|
|
272
338
|
free(rt->shapes.roots);
|
|
273
339
|
free(rt->shapes.parents);
|
|
340
|
+
free(rt->shapes.young);
|
|
274
341
|
memset(&rt->shapes, 0, sizeof rt->shapes);
|
|
275
342
|
}
|
|
276
343
|
|
package/runtime/src/typedarray.c
CHANGED
|
@@ -1234,6 +1234,26 @@ static double hook_typed_array_length(JSValue v) {
|
|
|
1234
1234
|
return out_of_bounds(as_ta(v)) ? -1 : (double)ta_length(as_ta(v));
|
|
1235
1235
|
}
|
|
1236
1236
|
|
|
1237
|
+
/* An element the core may read or write inline: of a typed array, a valid index (§10.4.5.15
|
|
1238
|
+
* IsValidIntegerIndex), a Number element. Its own data property: no prototype, no code runs. */
|
|
1239
|
+
static bool inline_elem(JSValue v, int32_t i) {
|
|
1240
|
+
if (!is_ta(v) || i < 0) return false;
|
|
1241
|
+
const JSTypedArray *ta = as_ta(v);
|
|
1242
|
+
return !is_bigint_type(ta->type) && (size_t)i < ta_length(ta);
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
static bool hook_typed_array_get(JSValue v, int32_t i, JSValue *out) {
|
|
1246
|
+
if (!inline_elem(v, i)) return false;
|
|
1247
|
+
*out = js_number(load_number(as_ta(v), (size_t)i));
|
|
1248
|
+
return true;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
static bool hook_typed_array_set(JSValue v, int32_t i, double d) {
|
|
1252
|
+
if (!inline_elem(v, i) || immutable(as_ta(v))) return false;
|
|
1253
|
+
store(as_ta(v), (size_t)i, d);
|
|
1254
|
+
return true;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1237
1257
|
static bool hook_view_bytes(JSValue v, uint8_t **data, size_t *length) {
|
|
1238
1258
|
return !js_is_array_buffer(v) && js_buffer_source_bytes(v, data, length);
|
|
1239
1259
|
}
|
|
@@ -1330,6 +1350,9 @@ static void install_typedarray(JSContext *ctx, JSObject *global) {
|
|
|
1330
1350
|
ctx->rt->is_typed_array = hook_is_typed_array;
|
|
1331
1351
|
ctx->rt->typed_array_length = hook_typed_array_length;
|
|
1332
1352
|
ctx->rt->view_bytes = hook_view_bytes;
|
|
1353
|
+
ctx->rt->typed_array_get = hook_typed_array_get;
|
|
1354
|
+
ctx->rt->typed_array_class = &ta_class;
|
|
1355
|
+
ctx->rt->typed_array_set = hook_typed_array_set;
|
|
1333
1356
|
}
|
|
1334
1357
|
|
|
1335
1358
|
const JSComponent js_component_typedarray = { "typedarray", install_typedarray };
|
package/runtime/src/weak.c
CHANGED
|
@@ -61,6 +61,8 @@ typedef struct WeakTable {
|
|
|
61
61
|
JSObject obj;
|
|
62
62
|
WeakEntry *entries; /* malloc'd, the heap's external memory; NULL until the first key */
|
|
63
63
|
uint32_t cap, count, used; /* used: count + tombstones */
|
|
64
|
+
uint32_t writes; /* entries set since the last collection, listed in rt->weak_writes */
|
|
65
|
+
bool all_written; /* too many to list: a young collection looks at every entry */
|
|
64
66
|
} WeakTable;
|
|
65
67
|
|
|
66
68
|
static uint32_t hash_key(JSValue k) {
|
|
@@ -83,7 +85,27 @@ static void insert_fresh(WeakEntry *entries, uint32_t cap, JSValue key, JSValue
|
|
|
83
85
|
entries[i] = (WeakEntry){ key, value };
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
/* Lists an entry set since the last collection. An entry set before has an old key and an old
|
|
89
|
+
* value (the last collection marked both, or dropped the entry), so a young collection can't free
|
|
90
|
+
* either: it looks only at the listed ones (js_weak_ephemerons, js_weak_sweep). Past a quarter of
|
|
91
|
+
* the table, the table is listed whole instead, which bounds the list. */
|
|
92
|
+
static void note_write(JSRuntime *rt, WeakTable *t, JSValue key) {
|
|
93
|
+
if (t->all_written) return;
|
|
94
|
+
if (t->writes >= 64 && t->writes >= t->count / 4) {
|
|
95
|
+
t->all_written = true;
|
|
96
|
+
key = JS_EMPTY;
|
|
97
|
+
}
|
|
98
|
+
t->writes++;
|
|
99
|
+
struct JSWeakWrites *l = &rt->weak_writes;
|
|
100
|
+
if (l->count == l->cap) {
|
|
101
|
+
l->cap = l->cap ? l->cap * 2 : 64;
|
|
102
|
+
l->items = xrealloc(l->items, l->cap * sizeof *l->items);
|
|
103
|
+
}
|
|
104
|
+
l->items[l->count++] = (struct JSWeakWrite){ &t->obj, key };
|
|
105
|
+
}
|
|
106
|
+
|
|
86
107
|
static void table_set(JSContext *ctx, WeakTable *t, JSValue key, JSValue value) {
|
|
108
|
+
note_write(ctx->rt, t, key);
|
|
87
109
|
WeakEntry *e = find(t, key);
|
|
88
110
|
if (e) {
|
|
89
111
|
e->value = value; /* malloc'd memory: no write barrier */
|
|
@@ -392,27 +414,83 @@ int js_run_finalization_cleanups(JSContext *ctx) {
|
|
|
392
414
|
|
|
393
415
|
/* ---- The collector's side (gc.c) ------------------------------------------------------- */
|
|
394
416
|
|
|
395
|
-
/*
|
|
396
|
-
|
|
397
|
-
|
|
417
|
+
/* An entry's ephemeron step: its value marked when its key is. Returns whether it marked it. */
|
|
418
|
+
static bool mark_value(JSTracer *trc, const WeakEntry *e) {
|
|
419
|
+
if (!js_is_cell(e->key) || !js_gc_is_marked(js_as_cell(e->key)) || is_live(e->value)) return false;
|
|
420
|
+
js_trace_value(trc, e->value);
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
static bool mark_values(JSTracer *trc, const WeakTable *t) {
|
|
398
425
|
bool marked = false;
|
|
399
|
-
for (
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
426
|
+
for (uint32_t k = 0; k < t->cap; k++) marked |= mark_value(trc, &t->entries[k]);
|
|
427
|
+
return marked;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* Drops the entry if its key died. */
|
|
431
|
+
static void sweep_entry(WeakTable *t, WeakEntry *e) {
|
|
432
|
+
if (js_is_cell(e->key) && !js_gc_is_marked(js_as_cell(e->key))) {
|
|
433
|
+
e->key = TOMBSTONE;
|
|
434
|
+
e->value = JS_UNDEFINED;
|
|
435
|
+
t->count--;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
static void sweep_entries(WeakTable *t) {
|
|
440
|
+
for (uint32_t k = 0; k < t->cap; k++) sweep_entry(t, &t->entries[k]);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/* The ephemeron pass: marks the value of every live WeakMap entry whose key is marked; a young
|
|
444
|
+
* collection, of the entries set since the last collection (note_write). true if anything new was
|
|
445
|
+
* marked (the caller drains and calls again). */
|
|
446
|
+
bool js_weak_ephemerons(JSRuntime *rt, JSTracer *trc, bool full) {
|
|
447
|
+
bool marked = false;
|
|
448
|
+
if (full) {
|
|
449
|
+
for (size_t i = 0; i < rt->weak.count; i++) {
|
|
450
|
+
JSObject *o = rt->weak.items[i];
|
|
451
|
+
if (o->shape->cls == &weak_map_class && js_gc_is_marked(&o->cell)) marked |= mark_values(trc, (WeakTable *)o);
|
|
452
|
+
}
|
|
453
|
+
return marked;
|
|
454
|
+
}
|
|
455
|
+
for (size_t i = 0; i < rt->weak_writes.count; i++) {
|
|
456
|
+
struct JSWeakWrite w = rt->weak_writes.items[i];
|
|
457
|
+
if (w.table->shape->cls != &weak_map_class || !js_gc_is_marked(&w.table->cell)) continue;
|
|
458
|
+
WeakTable *t = (WeakTable *)w.table;
|
|
459
|
+
if (js_is_empty(w.key)) {
|
|
460
|
+
marked |= mark_values(trc, t);
|
|
461
|
+
} else {
|
|
462
|
+
WeakEntry *e = find(t, w.key);
|
|
463
|
+
if (e) marked |= mark_value(trc, e);
|
|
408
464
|
}
|
|
409
465
|
}
|
|
410
466
|
return marked;
|
|
411
467
|
}
|
|
412
468
|
|
|
413
|
-
/* After marking: forget dead containers, drop dead keys
|
|
414
|
-
* queue the registrations whose
|
|
415
|
-
|
|
469
|
+
/* After marking: forget dead containers, drop dead keys (a young collection: of the entries set
|
|
470
|
+
* since the last collection), clear dead WeakRef targets, and queue the registrations whose
|
|
471
|
+
* target died. Then the list of entries set starts anew. */
|
|
472
|
+
void js_weak_sweep(JSRuntime *rt, bool full) {
|
|
473
|
+
struct JSWeakWrites *writes = &rt->weak_writes;
|
|
474
|
+
if (!full) {
|
|
475
|
+
for (size_t i = 0; i < writes->count; i++) {
|
|
476
|
+
struct JSWeakWrite w = writes->items[i];
|
|
477
|
+
if (!js_gc_is_marked(&w.table->cell)) continue; /* dead: its entries go with it */
|
|
478
|
+
WeakTable *t = (WeakTable *)w.table;
|
|
479
|
+
if (js_is_empty(w.key)) {
|
|
480
|
+
sweep_entries(t);
|
|
481
|
+
} else {
|
|
482
|
+
WeakEntry *e = find(t, w.key);
|
|
483
|
+
if (e) sweep_entry(t, e);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
for (size_t i = 0; i < writes->count; i++) { /* dead tables too: they're finalized later */
|
|
488
|
+
WeakTable *t = (WeakTable *)writes->items[i].table;
|
|
489
|
+
t->writes = 0;
|
|
490
|
+
t->all_written = false;
|
|
491
|
+
}
|
|
492
|
+
writes->count = 0;
|
|
493
|
+
|
|
416
494
|
struct JSWeakList *l = &rt->weak;
|
|
417
495
|
size_t n = 0;
|
|
418
496
|
for (size_t i = 0; i < l->count; i++) {
|
|
@@ -421,15 +499,7 @@ void js_weak_sweep(JSRuntime *rt) {
|
|
|
421
499
|
l->items[n++] = o;
|
|
422
500
|
const JSClass *cls = o->shape->cls;
|
|
423
501
|
if (cls == &weak_map_class || cls == &weak_set_class) {
|
|
424
|
-
|
|
425
|
-
for (uint32_t k = 0; k < t->cap; k++) {
|
|
426
|
-
WeakEntry *e = &t->entries[k];
|
|
427
|
-
if (js_is_cell(e->key) && !js_gc_is_marked(js_as_cell(e->key))) {
|
|
428
|
-
e->key = TOMBSTONE;
|
|
429
|
-
e->value = JS_UNDEFINED;
|
|
430
|
-
t->count--;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
502
|
+
if (full) sweep_entries((WeakTable *)o);
|
|
433
503
|
} else if (cls == &weak_ref_class) {
|
|
434
504
|
WeakRef *w = (WeakRef *)o;
|
|
435
505
|
if (!is_live(w->target)) w->target = JS_UNDEFINED;
|
|
@@ -454,6 +524,7 @@ void js_weak_trace_roots(JSRuntime *rt, JSTracer *trc) {
|
|
|
454
524
|
|
|
455
525
|
void js_weak_free(JSRuntime *rt) {
|
|
456
526
|
free(rt->weak.items);
|
|
527
|
+
free(rt->weak_writes.items);
|
|
457
528
|
free(rt->kept_alive.items);
|
|
458
529
|
}
|
|
459
530
|
|