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.
Files changed (54) hide show
  1. package/README.md +6 -3
  2. package/THIRD_PARTY_LICENSES +1 -1
  3. package/cli.js +2782 -770
  4. package/node/include/js_node.h +1 -0
  5. package/node/prelude/_http_agent.js +5 -0
  6. package/node/prelude/_http_server.js +7 -0
  7. package/node/prelude/dns.js +7 -1
  8. package/node/prelude/events.js +7 -3
  9. package/node/prelude/http.js +693 -285
  10. package/node/prelude/https.js +14 -3
  11. package/node/prelude/internal/events_extras.js +2 -5
  12. package/node/prelude/internal/features.js +17 -0
  13. package/node/prelude/internal/http_agent.js +88 -2
  14. package/node/prelude/internal/http_chars.ts +22 -0
  15. package/node/prelude/internal/http_symbols.js +3 -0
  16. package/node/prelude/internal/net_bound.js +123 -0
  17. package/node/prelude/internal/net_onread.js +73 -0
  18. package/node/prelude/internal/net_tos.js +54 -0
  19. package/node/prelude/internal/perf_observe.js +7 -1
  20. package/node/prelude/internal/tls/wrap.js +2 -1
  21. package/node/prelude/module.js +42 -18
  22. package/node/prelude/net.js +212 -61
  23. package/node/prelude/perf_hooks.js +4 -0
  24. package/node/src/host_http.c +151 -21
  25. package/node/src/host_net.c +75 -9
  26. package/node/src/host_sys.c +9 -0
  27. package/package.json +1 -1
  28. package/prebuilt/win32-x64/boringssl-ce573858472b7fc9.a +0 -0
  29. package/prebuilt/win32-x64/brotli-20d86a61f30baeb3.a +0 -0
  30. package/prebuilt/win32-x64/uv-b11bae65d7fced82.a +0 -0
  31. package/prebuilt/win32-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
  32. package/prebuilt/win32-x64/zstd-015315ac7c784c73.a +0 -0
  33. package/runtime/include/js_atoms.h +4 -0
  34. package/runtime/include/js_buffer.h +185 -0
  35. package/runtime/include/js_builtins.h +53 -0
  36. package/runtime/include/js_exception.h +3 -0
  37. package/runtime/include/js_function.h +1 -1
  38. package/runtime/include/js_object.h +21 -3
  39. package/runtime/include/js_ops.h +27 -0
  40. package/runtime/include/js_runtime.h +1 -0
  41. package/runtime/include/js_web.h +3 -3
  42. package/runtime/include/jscontext.h +15 -0
  43. package/runtime/src/atoms.c +37 -3
  44. package/runtime/src/builtins.c +17 -4
  45. package/runtime/src/events.c +19 -0
  46. package/runtime/src/function.c +37 -10
  47. package/runtime/src/gc.c +7 -5
  48. package/runtime/src/internal.h +4 -4
  49. package/runtime/src/iterator.c +5 -1
  50. package/runtime/src/object.c +42 -6
  51. package/runtime/src/regexp.c +569 -12
  52. package/runtime/src/shape.c +68 -1
  53. package/runtime/src/typedarray.c +23 -0
  54. package/runtime/src/weak.c +95 -24
@@ -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
- void js_shapes_sweep(JSRuntime *rt) {
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
 
@@ -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 };
@@ -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
- /* The ephemeron pass: marks the value of every live WeakMap entry whose key is marked.
396
- * true if anything new was marked (the caller drains and calls again). */
397
- bool js_weak_ephemerons(JSRuntime *rt, JSTracer *trc) {
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 (size_t i = 0; i < rt->weak.count; i++) {
400
- JSObject *o = rt->weak.items[i];
401
- if (o->shape->cls != &weak_map_class || !js_gc_is_marked(&o->cell)) continue;
402
- WeakTable *t = (WeakTable *)o;
403
- for (uint32_t k = 0; k < t->cap; k++) {
404
- WeakEntry *e = &t->entries[k];
405
- if (!js_is_cell(e->key) || !js_gc_is_marked(js_as_cell(e->key)) || is_live(e->value)) continue;
406
- js_trace_value(trc, e->value);
407
- marked = true;
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, clear dead WeakRef targets, and
414
- * queue the registrations whose target died. */
415
- void js_weak_sweep(JSRuntime *rt) {
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
- WeakTable *t = (WeakTable *)o;
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