visaljs 0.0.9 → 0.0.11

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 (232) hide show
  1. package/README.md +12 -4
  2. package/THIRD_PARTY_LICENSES +31 -1
  3. package/cli.js +3745 -890
  4. package/node/include/js_node.h +12 -1
  5. package/node/prelude/_http_common.js +29 -0
  6. package/node/prelude/async_hooks.js +50 -11
  7. package/node/prelude/child_process.js +31 -4
  8. package/node/prelude/console.js +311 -143
  9. package/node/prelude/crypto.js +6 -4
  10. package/node/prelude/dgram.js +1169 -0
  11. package/node/prelude/diagnostics_channel.js +3 -1
  12. package/node/prelude/dns.js +250 -45
  13. package/node/prelude/dns_promises.js +8 -2
  14. package/node/prelude/fs_promises.js +9 -1
  15. package/node/prelude/http.js +405 -94
  16. package/node/prelude/https.js +13 -4
  17. package/node/prelude/internal/console_class.js +7 -0
  18. package/node/prelude/internal/console_global.js +25 -0
  19. package/node/prelude/internal/crypto/key_encryption.js +217 -0
  20. package/node/prelude/internal/crypto/keys.js +161 -26
  21. package/node/prelude/internal/crypto/random.js +24 -0
  22. package/node/prelude/internal/crypto/util.js +2 -1
  23. package/node/prelude/internal/crypto_x509.js +161 -0
  24. package/node/prelude/internal/dns_classes.js +6 -0
  25. package/node/prelude/internal/dns_resolver.js +371 -0
  26. package/node/prelude/internal/errors.js +9 -0
  27. package/node/prelude/internal/events_on.js +82 -37
  28. package/node/prelude/internal/features.js +8 -4
  29. package/node/prelude/internal/fs_filehandle.js +10 -4
  30. package/node/prelude/internal/http_chars.ts +22 -0
  31. package/node/prelude/internal/perf_histogram.js +1 -1
  32. package/node/prelude/internal/process_more.js +32 -17
  33. package/node/prelude/internal/process_signals.js +8 -2
  34. package/node/prelude/internal/punycode.ts +163 -0
  35. package/node/prelude/internal/readline.js +648 -274
  36. package/node/prelude/internal/readline_editor.js +1240 -0
  37. package/node/prelude/internal/string_width.js +43 -0
  38. package/node/prelude/internal/tls/common.js +145 -33
  39. package/node/prelude/internal/tls/wrap.js +182 -49
  40. package/node/prelude/internal/tls/x509.js +4 -1
  41. package/node/prelude/internal/udp.js +177 -0
  42. package/node/prelude/net.js +30 -14
  43. package/node/prelude/os.js +24 -6
  44. package/node/prelude/perf_hooks.js +37 -2
  45. package/node/prelude/punycode.js +2 -130
  46. package/node/prelude/readline.js +113 -4
  47. package/node/prelude/readline_promises.js +56 -32
  48. package/node/prelude/tls.js +21 -4
  49. package/node/prelude/util.js +3 -16
  50. package/node/src/host_dns.c +623 -0
  51. package/node/src/host_http.c +12 -0
  52. package/node/src/host_net.c +46 -0
  53. package/node/src/host_os.c +1 -6
  54. package/node/src/host_sys.c +79 -0
  55. package/node/src/host_udp.c +418 -0
  56. package/node/third_party/c-ares/LICENSE.md +24 -0
  57. package/node/third_party/c-ares/config/darwin/ares_config.h +573 -0
  58. package/node/third_party/c-ares/config/freebsd/ares_config.h +573 -0
  59. package/node/third_party/c-ares/config/linux/ares_config.h +573 -0
  60. package/node/third_party/c-ares/config/openbsd/ares_config.h +583 -0
  61. package/node/third_party/c-ares/include/ares.h +1237 -0
  62. package/node/third_party/c-ares/include/ares_build.h +202 -0
  63. package/node/third_party/c-ares/include/ares_dns.h +127 -0
  64. package/node/third_party/c-ares/include/ares_dns_record.h +1119 -0
  65. package/node/third_party/c-ares/include/ares_nameser.h +510 -0
  66. package/node/third_party/c-ares/include/ares_version.h +47 -0
  67. package/node/third_party/c-ares/src/lib/ares_addrinfo2hostent.c +305 -0
  68. package/node/third_party/c-ares/src/lib/ares_addrinfo_localhost.c +245 -0
  69. package/node/third_party/c-ares/src/lib/ares_android.c +496 -0
  70. package/node/third_party/c-ares/src/lib/ares_android.h +38 -0
  71. package/node/third_party/c-ares/src/lib/ares_cancel.c +87 -0
  72. package/node/third_party/c-ares/src/lib/ares_close_sockets.c +137 -0
  73. package/node/third_party/c-ares/src/lib/ares_conn.c +511 -0
  74. package/node/third_party/c-ares/src/lib/ares_conn.h +202 -0
  75. package/node/third_party/c-ares/src/lib/ares_cookie.c +461 -0
  76. package/node/third_party/c-ares/src/lib/ares_data.c +168 -0
  77. package/node/third_party/c-ares/src/lib/ares_data.h +92 -0
  78. package/node/third_party/c-ares/src/lib/ares_destroy.c +159 -0
  79. package/node/third_party/c-ares/src/lib/ares_free_hostent.c +54 -0
  80. package/node/third_party/c-ares/src/lib/ares_free_string.c +33 -0
  81. package/node/third_party/c-ares/src/lib/ares_freeaddrinfo.c +67 -0
  82. package/node/third_party/c-ares/src/lib/ares_getaddrinfo.c +806 -0
  83. package/node/third_party/c-ares/src/lib/ares_getenv.c +38 -0
  84. package/node/third_party/c-ares/src/lib/ares_getenv.h +34 -0
  85. package/node/third_party/c-ares/src/lib/ares_gethostbyaddr.c +229 -0
  86. package/node/third_party/c-ares/src/lib/ares_gethostbyname.c +341 -0
  87. package/node/third_party/c-ares/src/lib/ares_getnameinfo.c +437 -0
  88. package/node/third_party/c-ares/src/lib/ares_hosts_file.c +960 -0
  89. package/node/third_party/c-ares/src/lib/ares_inet_net_pton.h +31 -0
  90. package/node/third_party/c-ares/src/lib/ares_init.c +631 -0
  91. package/node/third_party/c-ares/src/lib/ares_ipv6.h +115 -0
  92. package/node/third_party/c-ares/src/lib/ares_library_init.c +190 -0
  93. package/node/third_party/c-ares/src/lib/ares_metrics.c +261 -0
  94. package/node/third_party/c-ares/src/lib/ares_options.c +492 -0
  95. package/node/third_party/c-ares/src/lib/ares_parse_into_addrinfo.c +179 -0
  96. package/node/third_party/c-ares/src/lib/ares_private.h +648 -0
  97. package/node/third_party/c-ares/src/lib/ares_process.c +1649 -0
  98. package/node/third_party/c-ares/src/lib/ares_qcache.c +442 -0
  99. package/node/third_party/c-ares/src/lib/ares_query.c +151 -0
  100. package/node/third_party/c-ares/src/lib/ares_search.c +623 -0
  101. package/node/third_party/c-ares/src/lib/ares_send.c +300 -0
  102. package/node/third_party/c-ares/src/lib/ares_set_socket_functions.c +590 -0
  103. package/node/third_party/c-ares/src/lib/ares_setup.h +224 -0
  104. package/node/third_party/c-ares/src/lib/ares_socket.c +427 -0
  105. package/node/third_party/c-ares/src/lib/ares_socket.h +163 -0
  106. package/node/third_party/c-ares/src/lib/ares_sortaddrinfo.c +447 -0
  107. package/node/third_party/c-ares/src/lib/ares_strerror.c +91 -0
  108. package/node/third_party/c-ares/src/lib/ares_sysconfig.c +642 -0
  109. package/node/third_party/c-ares/src/lib/ares_sysconfig_files.c +851 -0
  110. package/node/third_party/c-ares/src/lib/ares_sysconfig_mac.c +373 -0
  111. package/node/third_party/c-ares/src/lib/ares_sysconfig_win.c +673 -0
  112. package/node/third_party/c-ares/src/lib/ares_timeout.c +162 -0
  113. package/node/third_party/c-ares/src/lib/ares_update_servers.c +1376 -0
  114. package/node/third_party/c-ares/src/lib/ares_version.c +36 -0
  115. package/node/third_party/c-ares/src/lib/config-dos.h +114 -0
  116. package/node/third_party/c-ares/src/lib/config-win32.h +396 -0
  117. package/node/third_party/c-ares/src/lib/dsa/ares_array.c +409 -0
  118. package/node/third_party/c-ares/src/lib/dsa/ares_htable.c +451 -0
  119. package/node/third_party/c-ares/src/lib/dsa/ares_htable.h +174 -0
  120. package/node/third_party/c-ares/src/lib/dsa/ares_htable_asvp.c +224 -0
  121. package/node/third_party/c-ares/src/lib/dsa/ares_htable_dict.c +230 -0
  122. package/node/third_party/c-ares/src/lib/dsa/ares_htable_strvp.c +210 -0
  123. package/node/third_party/c-ares/src/lib/dsa/ares_htable_szvp.c +188 -0
  124. package/node/third_party/c-ares/src/lib/dsa/ares_htable_vpstr.c +186 -0
  125. package/node/third_party/c-ares/src/lib/dsa/ares_htable_vpvp.c +194 -0
  126. package/node/third_party/c-ares/src/lib/dsa/ares_llist.c +385 -0
  127. package/node/third_party/c-ares/src/lib/dsa/ares_slist.c +479 -0
  128. package/node/third_party/c-ares/src/lib/dsa/ares_slist.h +207 -0
  129. package/node/third_party/c-ares/src/lib/event/ares_event.h +191 -0
  130. package/node/third_party/c-ares/src/lib/event/ares_event_configchg.c +746 -0
  131. package/node/third_party/c-ares/src/lib/event/ares_event_epoll.c +192 -0
  132. package/node/third_party/c-ares/src/lib/event/ares_event_kqueue.c +248 -0
  133. package/node/third_party/c-ares/src/lib/event/ares_event_poll.c +140 -0
  134. package/node/third_party/c-ares/src/lib/event/ares_event_select.c +159 -0
  135. package/node/third_party/c-ares/src/lib/event/ares_event_thread.c +575 -0
  136. package/node/third_party/c-ares/src/lib/event/ares_event_wake_pipe.c +172 -0
  137. package/node/third_party/c-ares/src/lib/event/ares_event_win32.c +981 -0
  138. package/node/third_party/c-ares/src/lib/event/ares_event_win32.h +161 -0
  139. package/node/third_party/c-ares/src/lib/include/ares_array.h +276 -0
  140. package/node/third_party/c-ares/src/lib/include/ares_buf.h +732 -0
  141. package/node/third_party/c-ares/src/lib/include/ares_htable_asvp.h +130 -0
  142. package/node/third_party/c-ares/src/lib/include/ares_htable_dict.h +123 -0
  143. package/node/third_party/c-ares/src/lib/include/ares_htable_strvp.h +130 -0
  144. package/node/third_party/c-ares/src/lib/include/ares_htable_szvp.h +118 -0
  145. package/node/third_party/c-ares/src/lib/include/ares_htable_vpstr.h +111 -0
  146. package/node/third_party/c-ares/src/lib/include/ares_htable_vpvp.h +128 -0
  147. package/node/third_party/c-ares/src/lib/include/ares_llist.h +239 -0
  148. package/node/third_party/c-ares/src/lib/include/ares_mem.h +41 -0
  149. package/node/third_party/c-ares/src/lib/include/ares_str.h +261 -0
  150. package/node/third_party/c-ares/src/lib/inet_net_pton.c +451 -0
  151. package/node/third_party/c-ares/src/lib/inet_ntop.c +219 -0
  152. package/node/third_party/c-ares/src/lib/legacy/ares_create_query.c +78 -0
  153. package/node/third_party/c-ares/src/lib/legacy/ares_expand_name.c +99 -0
  154. package/node/third_party/c-ares/src/lib/legacy/ares_expand_string.c +107 -0
  155. package/node/third_party/c-ares/src/lib/legacy/ares_fds.c +80 -0
  156. package/node/third_party/c-ares/src/lib/legacy/ares_getsock.c +85 -0
  157. package/node/third_party/c-ares/src/lib/legacy/ares_parse_a_reply.c +107 -0
  158. package/node/third_party/c-ares/src/lib/legacy/ares_parse_aaaa_reply.c +109 -0
  159. package/node/third_party/c-ares/src/lib/legacy/ares_parse_caa_reply.c +137 -0
  160. package/node/third_party/c-ares/src/lib/legacy/ares_parse_mx_reply.c +110 -0
  161. package/node/third_party/c-ares/src/lib/legacy/ares_parse_naptr_reply.c +132 -0
  162. package/node/third_party/c-ares/src/lib/legacy/ares_parse_ns_reply.c +154 -0
  163. package/node/third_party/c-ares/src/lib/legacy/ares_parse_ptr_reply.c +213 -0
  164. package/node/third_party/c-ares/src/lib/legacy/ares_parse_soa_reply.c +115 -0
  165. package/node/third_party/c-ares/src/lib/legacy/ares_parse_srv_reply.c +114 -0
  166. package/node/third_party/c-ares/src/lib/legacy/ares_parse_txt_reply.c +144 -0
  167. package/node/third_party/c-ares/src/lib/legacy/ares_parse_uri_reply.c +113 -0
  168. package/node/third_party/c-ares/src/lib/record/ares_dns_mapping.c +982 -0
  169. package/node/third_party/c-ares/src/lib/record/ares_dns_multistring.c +309 -0
  170. package/node/third_party/c-ares/src/lib/record/ares_dns_multistring.h +72 -0
  171. package/node/third_party/c-ares/src/lib/record/ares_dns_name.c +736 -0
  172. package/node/third_party/c-ares/src/lib/record/ares_dns_parse.c +1360 -0
  173. package/node/third_party/c-ares/src/lib/record/ares_dns_private.h +273 -0
  174. package/node/third_party/c-ares/src/lib/record/ares_dns_record.c +1701 -0
  175. package/node/third_party/c-ares/src/lib/record/ares_dns_write.c +1229 -0
  176. package/node/third_party/c-ares/src/lib/str/ares_buf.c +1510 -0
  177. package/node/third_party/c-ares/src/lib/str/ares_str.c +562 -0
  178. package/node/third_party/c-ares/src/lib/str/ares_strsplit.c +90 -0
  179. package/node/third_party/c-ares/src/lib/str/ares_strsplit.h +51 -0
  180. package/node/third_party/c-ares/src/lib/thirdparty/apple/dnsinfo.h +122 -0
  181. package/node/third_party/c-ares/src/lib/util/ares_iface_ips.c +640 -0
  182. package/node/third_party/c-ares/src/lib/util/ares_iface_ips.h +140 -0
  183. package/node/third_party/c-ares/src/lib/util/ares_math.c +167 -0
  184. package/node/third_party/c-ares/src/lib/util/ares_math.h +58 -0
  185. package/node/third_party/c-ares/src/lib/util/ares_rand.c +389 -0
  186. package/node/third_party/c-ares/src/lib/util/ares_rand.h +36 -0
  187. package/node/third_party/c-ares/src/lib/util/ares_threads.c +613 -0
  188. package/node/third_party/c-ares/src/lib/util/ares_threads.h +60 -0
  189. package/node/third_party/c-ares/src/lib/util/ares_time.h +49 -0
  190. package/node/third_party/c-ares/src/lib/util/ares_timeval.c +95 -0
  191. package/node/third_party/c-ares/src/lib/util/ares_uri.c +1627 -0
  192. package/node/third_party/c-ares/src/lib/util/ares_uri.h +252 -0
  193. package/node/third_party/c-ares/src/lib/windows_port.c +29 -0
  194. package/package.json +1 -1
  195. package/prebuilt/darwin-arm64/cares-a6286c65ca9374a3.a +0 -0
  196. package/prebuilt/darwin-x64/cares-a6286c65ca9374a3.a +0 -0
  197. package/prebuilt/linux-arm64/cares-4ba05ac8e948af8e.a +0 -0
  198. package/prebuilt/linux-arm64-musl/cares-4ba05ac8e948af8e.a +0 -0
  199. package/prebuilt/linux-x64/cares-4ba05ac8e948af8e.a +0 -0
  200. package/prebuilt/linux-x64-musl/cares-4ba05ac8e948af8e.a +0 -0
  201. package/prebuilt/win32-x64/boringssl-ce573858472b7fc9.a +0 -0
  202. package/prebuilt/win32-x64/brotli-20d86a61f30baeb3.a +0 -0
  203. package/prebuilt/win32-x64/cares-49c9ec95162b912d.a +0 -0
  204. package/prebuilt/win32-x64/uv-b11bae65d7fced82.a +0 -0
  205. package/prebuilt/win32-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
  206. package/prebuilt/win32-x64/zstd-015315ac7c784c73.a +0 -0
  207. package/runtime/include/js_buffer.h +185 -0
  208. package/runtime/include/js_builtins.h +58 -0
  209. package/runtime/include/js_crypto.h +11 -0
  210. package/runtime/include/js_exception.h +3 -0
  211. package/runtime/include/js_function.h +1 -1
  212. package/runtime/include/js_gc.h +13 -10
  213. package/runtime/include/js_jobs.h +2 -0
  214. package/runtime/include/js_object.h +79 -3
  215. package/runtime/include/js_ops.h +27 -0
  216. package/runtime/include/js_runtime.h +1 -0
  217. package/runtime/include/js_web.h +0 -3
  218. package/runtime/include/jscontext.h +8 -0
  219. package/runtime/src/builtins.c +17 -4
  220. package/runtime/src/crypto_internal.h +1 -0
  221. package/runtime/src/crypto_subtle.c +66 -7
  222. package/runtime/src/crypto_tls.c +265 -19
  223. package/runtime/src/function.c +37 -10
  224. package/runtime/src/gc.c +76 -93
  225. package/runtime/src/inspect.c +18 -1
  226. package/runtime/src/iterator.c +5 -1
  227. package/runtime/src/jobs.c +37 -22
  228. package/runtime/src/object.c +91 -8
  229. package/runtime/src/os.c +16 -6
  230. package/runtime/src/os.h +2 -0
  231. package/runtime/src/process.c +97 -10
  232. package/runtime/src/typedarray.c +23 -0
package/runtime/src/gc.c CHANGED
@@ -658,8 +658,8 @@ static JSCell *alloc_large(JSContext *ctx, size_t size) {
658
658
  b->is_large = 1;
659
659
  uint8_t shift = 9; /* cards of 512 bytes, or more for a very large object */
660
660
  while ((size + ((size_t)1 << shift) - 1) >> shift > JS_LARGE_CARDS) shift++;
661
- b->size_class = shift;
662
- b->alloc_bits[0] = 1;
661
+ b->card_shift = shift;
662
+ b->large_alloc = 1;
663
663
 
664
664
  if (h->large_count == h->large_cap) {
665
665
  h->large_cap = h->large_cap ? h->large_cap * 2 : 16;
@@ -811,13 +811,12 @@ static void push_remembered(JSHeap *h, JSCell *c) {
811
811
  * next stores of young cells mark their own cards. */
812
812
  static void mark_cards(JSHeap *h, JSCell *c, const void *from, size_t bytes) {
813
813
  JSBlock *b = js_block_of(c);
814
- unsigned shift = b->size_class;
814
+ unsigned shift = b->card_shift;
815
815
  size_t size = b->cell_size, off = (size_t)((const char *)from - (const char *)c);
816
816
  if (!from || (const char *)from < (const char *)c || off >= size) off = 0, bytes = size;
817
817
  if (!bytes) bytes = 1;
818
818
  size_t last = (off + bytes - 1 < size ? off + bytes - 1 : size - 1) >> shift;
819
- uint64_t *cards = js_large_cards(b);
820
- for (size_t k = off >> shift; k <= last; k++) cards[k / 64] |= UINT64_C(1) << (k % 64);
819
+ for (size_t k = off >> shift; k <= last; k++) b->cards[k / 64] |= UINT64_C(1) << (k % 64);
821
820
  if (!b->remembered) {
822
821
  b->remembered = 1;
823
822
  push_remembered(h, c);
@@ -918,6 +917,10 @@ static bool verify_edge(const JSTracer *trc, const JSCell *c) {
918
917
  /* Kinds that hold no cells: marked, never pushed or visited. */
919
918
  static const bool leaf_kind[JS_CELL_KIND_COUNT] = { [JS_CELL_STR_FLAT] = true, [JS_CELL_BIGINT] = true };
920
919
 
920
+ static bool has_children(const JSCell *c) {
921
+ return !leaf_kind[c->kind] && !(c->kind == JS_CELL_HOST && !((const JSHostCell *)c)->cls->visit);
922
+ }
923
+
921
924
  static JS_NOINLINE void grow_stack(JSTracer *trc) {
922
925
  trc->cap = trc->cap ? trc->cap * 2 : 4096;
923
926
  trc->stack = xrealloc(trc->stack, trc->cap * sizeof *trc->stack);
@@ -939,6 +942,18 @@ static inline void push(JSTracer *trc, JSCell *c) {
939
942
  trc->stack[trc->sp++] = c;
940
943
  }
941
944
 
945
+ /* Sets heap cell `c`'s mark bit. Returns whether it was clear: this marker marked it, and it's old
946
+ * from now on. In a young collection every old cell's already is. */
947
+ static inline bool mark_bit(JSTracer *trc, JSCell *c) {
948
+ JSBlock *b = js_block_of(c);
949
+ uint32_t i = cell_index(b, c);
950
+ if (!set_mark(trc, &b->mark_bits[i / 64], UINT64_C(1) << (i % 64))) return false;
951
+ c->gc = b->is_large ? JS_GC_OLD_LARGE : JS_GC_OLD;
952
+ trc->marked++;
953
+ trc->marked_bytes += b->is_large ? b->map_size : b->cell_size;
954
+ return true;
955
+ }
956
+
942
957
  /* Marks `c`, and pushes it if it holds cells. Its header is read here, the first touch of the
943
958
  * cell: trace_cell prefetched it. */
944
959
  static inline void mark(JSTracer *trc, JSCell *c) {
@@ -947,14 +962,7 @@ static inline void mark(JSTracer *trc, JSCell *c) {
947
962
  #else
948
963
  if (js_cell_is_static(c)) return;
949
964
  #endif
950
- JSBlock *b = js_block_of(c);
951
- uint32_t i = cell_index(b, c);
952
- if (!set_mark(trc, &b->mark_bits[i / 64], UINT64_C(1) << (i % 64))) return; /* in a young collection, every old cell is */
953
- c->gc = b->is_large ? JS_GC_OLD_LARGE : JS_GC_OLD; /* it survives: old from now on */
954
- trc->marked++;
955
- trc->marked_bytes += b->is_large ? b->map_size : b->cell_size;
956
- if (leaf_kind[c->kind] || (c->kind == JS_CELL_HOST && !((JSHostCell *)c)->cls->visit)) return;
957
- push(trc, c);
965
+ if (mark_bit(trc, c) && has_children(c)) push(trc, c);
958
966
  }
959
967
 
960
968
  /* The marking barrier's: marked now, and on the marker's stack to be visited. */
@@ -1011,13 +1019,7 @@ static inline void mark_ool(JSTracer *trc, JSValueArray *a) {
1011
1019
  }
1012
1020
  if (verify_edge(trc, c)) return;
1013
1021
  #endif
1014
- JSBlock *b = js_block_of(c);
1015
- uint32_t i = cell_index(b, c);
1016
- if (set_mark(trc, &b->mark_bits[i / 64], UINT64_C(1) << (i % 64))) {
1017
- c->gc = b->is_large ? JS_GC_OLD_LARGE : JS_GC_OLD;
1018
- trc->marked++;
1019
- trc->marked_bytes += b->is_large ? b->map_size : b->cell_size;
1020
- }
1022
+ mark_bit(trc, c);
1021
1023
  for (uint32_t k = 0; k < c->aux; k++) trace_value(trc, a->values[k]);
1022
1024
  }
1023
1025
 
@@ -1115,6 +1117,11 @@ static void add_task(JSMarkPool *p, JSCell *c, size_t from, size_t to) {
1115
1117
  atomic_store_int(&p->news, p->news + 1);
1116
1118
  }
1117
1119
 
1120
+ /* Bytes [from, to) of `c` as tasks of MARK_CHUNK bytes at most. */
1121
+ static void add_chunk_tasks(JSMarkPool *p, JSCell *c, size_t from, size_t to) {
1122
+ for (; from < to; from += MARK_CHUNK) add_task(p, c, from, to - from > MARK_CHUNK ? from + MARK_CHUNK : to);
1123
+ }
1124
+
1118
1125
  static void give_packet(JSTracer *trc, JSMarkPool *p) {
1119
1126
  size_t n = (trc->sp - trc->base) / 2;
1120
1127
  if (n > PACKET) n = PACKET;
@@ -1199,10 +1206,8 @@ static size_t splittable_bytes(const JSCell *c) {
1199
1206
  /* A large cell met while marking with others: its chunks but the first become tasks, for
1200
1207
  * whoever is free; this marker visits the first. */
1201
1208
  static void split_cell(JSTracer *trc, JSMarkPool *p, JSCell *c) {
1202
- size_t size = js_block_of(c)->cell_size;
1203
1209
  js_os_lock(p->lock);
1204
- for (size_t from = MARK_CHUNK; from < size; from += MARK_CHUNK)
1205
- add_task(p, c, from, size - from > MARK_CHUNK ? from + MARK_CHUNK : size);
1210
+ add_chunk_tasks(p, c, MARK_CHUNK, js_block_of(c)->cell_size);
1206
1211
  js_os_cond_broadcast(p->work);
1207
1212
  js_os_unlock(p->lock);
1208
1213
  #ifdef JS_GC_VERIFY
@@ -1373,10 +1378,6 @@ static void drain(JSTracer *trc) {
1373
1378
  #endif
1374
1379
  }
1375
1380
 
1376
- static bool has_children(const JSCell *c) {
1377
- return !leaf_kind[c->kind] && !(c->kind == JS_CELL_HOST && !((const JSHostCell *)c)->cls->visit);
1378
- }
1379
-
1380
1381
  /* The slots of `slots[0..n)` (at byte `base` of their cell) in bytes [from, to) of it. */
1381
1382
  static void trace_slots_in(JSTracer *trc, const JSValue *slots, size_t n, size_t base, size_t from, size_t to) {
1382
1383
  size_t first = from <= base ? 0 : (from - base) / sizeof(JSValue);
@@ -1416,43 +1417,50 @@ static bool visit_range(JSTracer *trc, JSCell *c, size_t from, size_t to) {
1416
1417
  return false;
1417
1418
  }
1418
1419
 
1420
+ /* Whether visit_range can visit part of `c`, not the whole cell whatever the range. */
1421
+ static bool visits_part(const JSCell *c) {
1422
+ if (c->kind == JS_CELL_HOST) return ((const JSHostCell *)c)->cls->visit_range != NULL;
1423
+ return splittable_bytes(c) != 0;
1424
+ }
1425
+
1426
+ static bool card_marked(const JSBlock *b, size_t k) { return (b->cards[k / 64] >> (k % 64)) & 1; }
1427
+
1428
+ /* The next run of large `b`'s cards from card *k that are marked (`marked`) or not: bytes
1429
+ * [*from, *to) of its cell. Returns false when there's none left. */
1430
+ static bool next_card_run(const JSBlock *b, bool marked, size_t *k, size_t *from, size_t *to) {
1431
+ unsigned shift = b->card_shift;
1432
+ size_t n = (b->cell_size + ((size_t)1 << shift) - 1) >> shift;
1433
+ while (*k < n && card_marked(b, *k) != marked) (*k)++;
1434
+ if (*k == n) return false;
1435
+ size_t end = *k + 1;
1436
+ while (end < n && card_marked(b, end) == marked) end++;
1437
+ *from = *k << shift;
1438
+ *to = end << shift < b->cell_size ? end << shift : b->cell_size;
1439
+ *k = end;
1440
+ return true;
1441
+ }
1442
+
1443
+ static void forget_cards(JSBlock *b) {
1444
+ memset(b->cards, 0, sizeof b->cards);
1445
+ b->remembered = 0;
1446
+ }
1447
+
1419
1448
  /* A remembered large cell: visits its marked cards, a run of them at a time (`marked`), or, for
1420
1449
  * the barrier verifier, the others; then, when visiting the marked ones, forgets them. */
1421
1450
  static void visit_cards(JSTracer *trc, JSCell *c, bool marked) {
1422
1451
  JSBlock *b = js_block_of(c);
1423
- uint64_t *cards = js_large_cards(b);
1424
- unsigned shift = b->size_class;
1425
- size_t n = (b->cell_size + ((size_t)1 << shift) - 1) >> shift;
1426
- for (size_t k = 0; k < n;) {
1427
- if ((bool)((cards[k / 64] >> (k % 64)) & 1) != marked) {
1428
- k++;
1429
- continue;
1430
- }
1431
- size_t end = k + 1;
1432
- while (end < n && (bool)((cards[end / 64] >> (end % 64)) & 1) == marked) end++;
1452
+ size_t from, to;
1453
+ for (size_t k = 0; next_card_run(b, marked, &k, &from, &to);) {
1433
1454
  #ifdef JS_GC_VERIFY
1434
1455
  trc->visiting = c;
1435
1456
  #endif
1436
- size_t to = end << shift < b->cell_size ? end << shift : b->cell_size;
1437
- if (!visit_range(trc, c, k << shift, to) && !marked) return; /* inexact: can't check part */
1438
- k = end;
1439
- }
1440
- if (marked) {
1441
- memset(cards, 0, JS_LARGE_CARDS / 8);
1442
- b->remembered = 0;
1457
+ if (!visit_range(trc, c, from, to) && !marked) return; /* inexact: can't check part */
1443
1458
  }
1459
+ if (marked) forget_cards(b);
1444
1460
  }
1445
1461
 
1446
1462
  /* ---- Incremental marking: slices ---------------------------------------- */
1447
1463
 
1448
- /* A large cell is visited this much at a time in a slice, when its kind's range visit is exact. */
1449
-
1450
- static bool visits_in_chunks(const JSCell *c) {
1451
- if (!js_block_of(c)->is_large || js_block_of(c)->cell_size <= MARK_CHUNK) return false;
1452
- return c->kind == JS_CELL_VALUE_ARRAY || c->kind == JS_CELL_ENV ||
1453
- (c->kind == JS_CELL_ELEMENTS && ((const JSElements *)c)->kind == JS_ELEMENTS_DENSE);
1454
- }
1455
-
1456
1464
  /* The next chunk of the partly visited cell. Returns the bytes it covered. */
1457
1465
  static size_t visit_chunk(JSTracer *trc) {
1458
1466
  JSCell *c = trc->partial;
@@ -1489,7 +1497,7 @@ static bool drain_some(JSTracer *trc, size_t budget) {
1489
1497
  }
1490
1498
  if (!trc->sp) break;
1491
1499
  JSCell *c = trc->stack[--trc->sp];
1492
- if (visits_in_chunks(c)) {
1500
+ if (splittable_bytes(c) > MARK_CHUNK) {
1493
1501
  trc->partial = c;
1494
1502
  trc->partial_from = 0;
1495
1503
  continue;
@@ -1508,56 +1516,32 @@ static void drain_all(JSTracer *trc) {
1508
1516
  drain(trc);
1509
1517
  }
1510
1518
 
1511
- /* A young collection's other roots: the old cells a store gave a young cell. They're marked
1512
- * already, so they're pushed to be visited, dead or alive: a remembered cell that died since keeps
1513
- * its young cells until the next collection, so nothing it points to is freed under it (a stale
1514
- * stack word could still find it). Each becomes plain old again. */
1515
1519
  /* Below this much in marked cards, a young collection visits them itself. */
1516
1520
  #define PARALLEL_CARDS ((size_t)256 << 10)
1517
1521
 
1518
- static bool exact_range(const JSCell *c) {
1519
- switch (c->kind) {
1520
- case JS_CELL_VALUE_ARRAY: case JS_CELL_ENV: return true;
1521
- case JS_CELL_ELEMENTS: return ((const JSElements *)c)->kind == JS_ELEMENTS_DENSE;
1522
- case JS_CELL_HOST: return ((const JSHostCell *)c)->cls->visit_range != NULL;
1523
- default: return false;
1524
- }
1525
- }
1526
-
1527
-
1528
1522
  /* Large `c`'s marked card runs, as tasks of MARK_CHUNK bytes at most (the whole cell, once, for a
1529
1523
  * kind that can't visit part of it); then its cards are forgotten. Returns the tasks' bytes. */
1530
1524
  static size_t card_tasks(JSMarkPool *p, JSCell *c) {
1531
1525
  JSBlock *b = js_block_of(c);
1532
- uint64_t *cards = js_large_cards(b);
1533
- unsigned shift = b->size_class;
1534
- size_t n = (b->cell_size + ((size_t)1 << shift) - 1) >> shift, bytes = 0;
1535
- if (!exact_range(c)) {
1526
+ size_t bytes = 0, from, to;
1527
+ if (!visits_part(c)) {
1536
1528
  add_task(p, c, 0, b->cell_size);
1537
1529
  bytes = b->cell_size;
1538
1530
  } else {
1539
- for (size_t k = 0; k < n;) {
1540
- if (!((cards[k / 64] >> (k % 64)) & 1)) {
1541
- k++;
1542
- continue;
1543
- }
1544
- size_t end = k + 1;
1545
- while (end < n && ((cards[end / 64] >> (end % 64)) & 1)) end++;
1546
- size_t to = end << shift < b->cell_size ? end << shift : b->cell_size;
1547
- for (size_t from = k << shift; from < to; from += MARK_CHUNK) {
1548
- add_task(p, c, from, to - from > MARK_CHUNK ? from + MARK_CHUNK : to);
1549
- bytes += to - from > MARK_CHUNK ? MARK_CHUNK : to - from;
1550
- }
1551
- k = end;
1531
+ for (size_t k = 0; next_card_run(b, true, &k, &from, &to);) {
1532
+ add_chunk_tasks(p, c, from, to);
1533
+ bytes += to - from;
1552
1534
  }
1553
1535
  }
1554
- memset(cards, 0, JS_LARGE_CARDS / 8);
1555
- b->remembered = 0;
1536
+ forget_cards(b);
1556
1537
  return bytes;
1557
1538
  }
1558
1539
 
1559
- /* A young collection's other roots: the remembered cells. With helpers, large cells' marked cards
1560
- * become tasks that every marker takes a share of (drain), when there are enough of them. */
1540
+ /* A young collection's other roots: the old cells a store gave a young cell. They're marked
1541
+ * already, so they're pushed to be visited, dead or alive: a remembered cell that died since keeps
1542
+ * its young cells until the next collection, so nothing it points to is freed under it (a stale
1543
+ * stack word could still find it). Each becomes plain old again. With helpers, large cells' marked
1544
+ * cards become tasks that every marker takes a share of (drain), when there are enough of them. */
1561
1545
  static void mark_remembered(JSHeap *h, JSTracer *trc) {
1562
1546
  JSMarkPool *p = h->gc_helpers ? mark_pool(h) : NULL;
1563
1547
  size_t task_bytes = 0;
@@ -1588,10 +1572,7 @@ static void mark_remembered(JSHeap *h, JSTracer *trc) {
1588
1572
  static void forget_remembered(JSHeap *h) {
1589
1573
  for (size_t i = 0; i < h->remembered_count; i++) {
1590
1574
  JSCell *c = h->remembered[i];
1591
- if (c->gc != JS_GC_OLD_LARGE) continue; /* JS_GC_OLD again when it's marked */
1592
- JSBlock *b = js_block_of(c);
1593
- memset(js_large_cards(b), 0, JS_LARGE_CARDS / 8);
1594
- b->remembered = 0;
1575
+ if (c->gc == JS_GC_OLD_LARGE) forget_cards(js_block_of(c)); /* the others: JS_GC_OLD again when they're marked */
1595
1576
  }
1596
1577
  h->remembered_count = 0;
1597
1578
  }
@@ -1646,8 +1627,10 @@ static void mark_roots(JSRuntime *rt, JSTracer *trc) {
1646
1627
  js_trace_value(trc, rt->async_context);
1647
1628
  js_jobs_trace(rt, trc); /* queued jobs, unhandled rejections */
1648
1629
  js_weak_trace_roots(rt, trc); /* the WeakRef kept-alive set */
1649
- for (JSContext *realm = rt->realms; realm; realm = realm->next_realm)
1630
+ for (JSContext *realm = rt->realms; realm; realm = realm->next_realm) {
1650
1631
  for (unsigned i = 0; i < JS_INTRINSIC_COUNT; i++) js_trace_value(trc, realm->intrinsics[i]);
1632
+ if (realm->array_shape) js_trace_cell(trc, &realm->array_shape->cell);
1633
+ }
1651
1634
  for (size_t t = 0; t < h->root_table_count; t++)
1652
1635
  for (size_t i = 0; i < h->root_tables[t].n; i++) js_trace_value(trc, h->root_tables[t].slots[i]);
1653
1636
  for (size_t i = 0; i < h->root_count; i++) js_trace_value(trc, *h->roots[i]);
@@ -438,6 +438,23 @@ static JSValue get_constructor_name(Insp *ip, JSValue obj, double recurse) {
438
438
  }
439
439
 
440
440
  /* getPrefix: "Foo ", "Foo [Tag] ", "[Object: null prototype] " */
441
+ /* Node's getPrefix: the constructor's name already shows the tag when it contains it, at its end
442
+ * or before a character that isn't lowercase (SecretKeyObject shows KeyObject; Foox doesn't show Foo) */
443
+ static bool names_tag(JSContext *ctx, JSValue constructor, JSValue tag) {
444
+ JSStrView c = js_string_view(ctx, constructor), t = js_string_view(ctx, tag);
445
+ if (t.length == 0 || t.length > c.length) return false;
446
+ for (uint32_t pos = 0; pos + t.length <= c.length; pos++) { /* the first occurrence decides, as indexOf */
447
+ uint32_t i = 0;
448
+ while (i < t.length && js_view_at(c, pos + i) == js_view_at(t, i)) i++;
449
+ if (i < t.length) continue;
450
+ uint32_t end = pos + t.length;
451
+ if (end == c.length) return true;
452
+ uint32_t next = js_view_at(c, end);
453
+ return next >= 'A' && next <= 'Z'; /* Node: not equal to its own lowercase (ASCII here) */
454
+ }
455
+ return false;
456
+ }
457
+
441
458
  static void put_prefix(Builder *b, JSContext *ctx, JSValue constructor, JSValue tag, const char *fallback, const char *size) {
442
459
  bool has_tag = js_string_length(tag) != 0;
443
460
  if (js_is_null(constructor)) {
@@ -455,7 +472,7 @@ static void put_prefix(Builder *b, JSContext *ctx, JSValue constructor, JSValue
455
472
  }
456
473
  put_str(b, ctx, constructor);
457
474
  put_ascii(b, size);
458
- if (has_tag && !js_string_equals(ctx, constructor, tag)) {
475
+ if (has_tag && !names_tag(ctx, constructor, tag)) {
459
476
  put_ascii(b, " [");
460
477
  put_str(b, ctx, tag);
461
478
  put_ascii(b, "]");
@@ -236,7 +236,11 @@ JSValue js_iter_next(JSContext *ctx, JSValue rec) {
236
236
  if (r->done) return JS_EMPTY;
237
237
  JSValue value;
238
238
  if (r->mode == MODE_ARRAY) {
239
- value = array_step(ctx, r->iterator, r->index, KIND_VALUES);
239
+ /* a dense array's present element: what array_step's [[Get]] would read */
240
+ const JSElements *d = js_as_object(r->iterator)->elements;
241
+ uint32_t i = (uint32_t)r->index;
242
+ if (JS_LIKELY(d->kind == JS_ELEMENTS_DENSE && r->index < d->length && !js_is_empty(d->data[i]))) value = d->data[i];
243
+ else value = array_step(ctx, r->iterator, r->index, KIND_VALUES);
240
244
  r->index++;
241
245
  } else if (r->mode == MODE_STRING) {
242
246
  uint32_t i = (uint32_t)r->index;
@@ -17,8 +17,7 @@
17
17
 
18
18
  /* ---- The queue -------------------------------------------------------------------- */
19
19
 
20
- void js_enqueue_microtask(JSContext *ctx, const JSJob *job) {
21
- JSJobQueue *q = &ctx->rt->jobs;
20
+ static void enqueue(JSContext *ctx, JSJobQueue *q, const JSJob *job) {
22
21
  if (q->count == q->cap) { /* grow, unwrapping the ring: no allocation of cells, so no GC */
23
22
  size_t cap = q->cap ? q->cap * 2 : 64;
24
23
  JSJob *items = malloc(cap * sizeof *items);
@@ -36,6 +35,14 @@ void js_enqueue_microtask(JSContext *ctx, const JSJob *job) {
36
35
  q->count++;
37
36
  }
38
37
 
38
+ void js_enqueue_microtask(JSContext *ctx, const JSJob *job) {
39
+ enqueue(ctx, &ctx->rt->jobs, job);
40
+ }
41
+
42
+ void js_enqueue_tick(JSContext *ctx, const JSJob *job) {
43
+ enqueue(ctx, &ctx->rt->ticks, job);
44
+ }
45
+
39
46
  /* ---- The async context --------------------------------------------------------------- */
40
47
 
41
48
  JSValue js_async_context(JSContext *ctx) { return ctx->rt->async_context; }
@@ -144,25 +151,31 @@ static int process_rejections(JSContext *ctx) {
144
151
 
145
152
  /* ---- The checkpoint -------------------------------------------------------------- */
146
153
 
154
+ /* Runs every job in `q`, the ones queued meanwhile too: 0, or -1 after an uncaught error (reported) */
155
+ static int drain(JSJobQueue *q) {
156
+ while (q->count) {
157
+ JSJob job = q->items[q->head]; /* on the C stack: the conservative scan keeps it alive */
158
+ q->head = (q->head + 1) % q->cap;
159
+ q->count--;
160
+ if (run_job(&job) < 0 && js_report_uncaught(job.realm)) return -1; /* handled: the next job runs */
161
+ }
162
+ return 0;
163
+ }
164
+
165
+ /* Node's processTicksAndRejections: the ticks, then the microtasks (a tick a microtask queues
166
+ * waits for them all), again while there are ticks; the rejections only then, so a handler a
167
+ * tick adds is in time */
147
168
  int js_run_microtasks(JSContext *ctx) {
148
169
  JSRuntime *rt = ctx->rt;
149
170
  if (rt->in_checkpoint) return 0;
150
171
  rt->in_checkpoint = 1;
151
- JSJobQueue *q = &rt->jobs;
152
172
  int status = 0;
153
- do { /* again while the host's handling of a rejection queued jobs */
154
- while (q->count) {
155
- JSJob job = q->items[q->head]; /* on the C stack: the conservative scan keeps it alive */
156
- q->head = (q->head + 1) % q->cap;
157
- q->count--;
158
- if (run_job(&job) < 0 && js_report_uncaught(job.realm)) { /* handled: the next job runs */
159
- status = -1;
160
- break;
161
- }
162
- }
173
+ do {
174
+ status = drain(&rt->ticks);
175
+ if (status == 0) status = drain(&rt->jobs);
163
176
  js_clear_kept_objects(rt); /* the end of the job: WeakRef targets may go now */
164
- if (status == 0) status = process_rejections(ctx);
165
- } while (status == 0 && (q->count || rt->handled_late.count));
177
+ if (status == 0 && rt->ticks.count == 0) status = process_rejections(ctx);
178
+ } while (status == 0 && (rt->ticks.count || rt->jobs.count || rt->handled_late.count));
166
179
  rt->in_checkpoint = 0;
167
180
  return status;
168
181
  }
@@ -170,13 +183,14 @@ int js_run_microtasks(JSContext *ctx) {
170
183
  /* ---- GC and teardown (gc.c) ----------------------------------------------------- */
171
184
 
172
185
  void js_jobs_trace(JSRuntime *rt, JSTracer *trc) {
173
- JSJobQueue *q = &rt->jobs;
174
- for (size_t i = 0; i < q->count; i++) {
175
- JSJob *j = &q->items[(q->head + i) % q->cap];
176
- js_trace_value(trc, j->a);
177
- js_trace_value(trc, j->b);
178
- js_trace_value(trc, j->c);
179
- js_trace_value(trc, j->context);
186
+ for (JSJobQueue *q = &rt->jobs; q; q = q == &rt->jobs ? &rt->ticks : NULL) {
187
+ for (size_t i = 0; i < q->count; i++) {
188
+ JSJob *j = &q->items[(q->head + i) % q->cap];
189
+ js_trace_value(trc, j->a);
190
+ js_trace_value(trc, j->b);
191
+ js_trace_value(trc, j->c);
192
+ js_trace_value(trc, j->context);
193
+ }
180
194
  }
181
195
  for (size_t i = 0; i < rt->rejections.count; i++) js_trace_value(trc, rt->rejections.items[i]);
182
196
  for (size_t i = 0; i < rt->handled_late.count; i++) js_trace_value(trc, rt->handled_late.items[i]);
@@ -189,6 +203,7 @@ void js_jobs_trace(JSRuntime *rt, JSTracer *trc) {
189
203
 
190
204
  void js_jobs_free(JSRuntime *rt) {
191
205
  free(rt->jobs.items);
206
+ free(rt->ticks.items);
192
207
  free(rt->rejections.items);
193
208
  free(rt->handled_late.items);
194
209
  free(rt->timers.heap);
@@ -544,7 +544,8 @@ int js_ordinary_set(JSContext *ctx, JSObject *o, JSValue key, JSValue v, JSValue
544
544
  /* not for classes that intercept indices (mapped arguments); arrays only guard length */
545
545
  bool plain = !o->shape->cls->define_own_property || o->shape->cls == &js_class_array;
546
546
  if (plain && js_elements_try_store(ctx, o, js_key_index(key), v)) return 1;
547
- } else if (js_is_object(receiver) && js_as_object(receiver) == o) {
547
+ } else if (js_is_object(receiver) && js_as_object(receiver) == o &&
548
+ (!o->shape->cls->define_own_property || o->shape->cls == &js_class_array)) { /* a class that intercepts writes (process.env) sees each */
548
549
  uint8_t attrs;
549
550
  int32_t slot = find_own(o, key, &attrs);
550
551
  if (slot >= 0 && (attrs & (JS_PROP_WRITABLE | JS_PROP_ACCESSOR)) == JS_PROP_WRITABLE) {
@@ -697,8 +698,7 @@ const JSClass js_class_array = {
697
698
  "Array", sizeof(JSObject), array_get_own, array_define, array_delete, array_extra_keys, NULL, NULL, NULL,
698
699
  };
699
700
 
700
- static JSValue array_with_proto(JSContext *ctx, JSValue proto, uint32_t capacity) {
701
- JSShape *shape = js_shape_root(ctx, &js_class_array, proto, 0); /* named properties on arrays are rare */
701
+ static JSValue array_of_shape(JSContext *ctx, JSShape *shape, uint32_t capacity) {
702
702
  JSObject *a = js_alloc_object(ctx, JS_CELL_ARRAY, shape);
703
703
  if (capacity) {
704
704
  JSElements *e = (JSElements *)js_alloc_cell(ctx, JS_CELL_ELEMENTS, sizeof(JSElements) + (size_t)capacity * sizeof(JSValue));
@@ -709,8 +709,13 @@ static JSValue array_with_proto(JSContext *ctx, JSValue proto, uint32_t capacity
709
709
  return js_object_value(a);
710
710
  }
711
711
 
712
+ static JSValue array_with_proto(JSContext *ctx, JSValue proto, uint32_t capacity) {
713
+ return array_of_shape(ctx, js_shape_root(ctx, &js_class_array, proto, 0), capacity); /* named properties on arrays are rare */
714
+ }
715
+
712
716
  JSValue js_new_array(JSContext *ctx, uint32_t capacity) {
713
- return array_with_proto(ctx, ctx->intrinsics[JS_INTRINSIC_ARRAY_PROTOTYPE], capacity);
717
+ if (JS_UNLIKELY(!ctx->array_shape)) ctx->array_shape = js_shape_root(ctx, &js_class_array, ctx->intrinsics[JS_INTRINSIC_ARRAY_PROTOTYPE], 0);
718
+ return array_of_shape(ctx, ctx->array_shape, capacity);
714
719
  }
715
720
 
716
721
  JSValue js_new_array_from(JSContext *ctx, const JSValue *values, uint32_t n) {
@@ -740,6 +745,51 @@ int js_array_push(JSContext *ctx, JSObject *a, JSValue v) {
740
745
  return js_define_own_property(ctx, a, js_index_key(len), &d, true);
741
746
  }
742
747
 
748
+ static const char *const SARRAY_KINDS[] = { "number", "string", "boolean" };
749
+
750
+ static JSValue sarray_not_array(JSContext *ctx, int kind) {
751
+ return js_throw_type_error(ctx, "A %s[] of static code isn't an array", SARRAY_KINDS[kind]);
752
+ }
753
+
754
+ static JSValue sarray_out_of_range(JSContext *ctx, int64_t i, uint32_t len) {
755
+ if (i < 0) return js_throw_range_error(ctx, "Invalid array index for length %u", len);
756
+ return js_throw_range_error(ctx, "Index %lld is out of range for length %u", (long long)i, len);
757
+ }
758
+
759
+ JSValue js_sarray_get_slow(JSContext *ctx, JSValue arr, int64_t i, int kind) {
760
+ if (!js_is_array(arr)) return sarray_not_array(ctx, kind);
761
+ uint32_t len = js_array_length(js_as_object(arr));
762
+ if (i < 0 || i >= len) return sarray_out_of_range(ctx, i, len);
763
+ /* a hole, a sparse store: the element as JavaScript reads it, which may be of another kind */
764
+ JSValue v = js_get_elem(ctx, arr, js_number((double)i));
765
+ if (js_is_exception(v) || js_sarray_is(v, kind)) return v;
766
+ return js_throw_type_error(ctx, "Element %lld of a %s[] isn't a %s", (long long)i, SARRAY_KINDS[kind], SARRAY_KINDS[kind]);
767
+ }
768
+
769
+ int js_sarray_set_slow(JSContext *ctx, JSValue arr, int64_t i, JSValue v, int kind) {
770
+ JSValue error = !js_is_array(arr) ? sarray_not_array(ctx, kind)
771
+ : !js_sarray_is(v, kind) ? js_throw_type_error(ctx, "A %s[]'s element can't be another type", SARRAY_KINDS[kind])
772
+ : JS_UNDEFINED;
773
+ if (js_is_exception(error)) return -1;
774
+ uint32_t len = js_array_length(js_as_object(arr));
775
+ if (i == len) return js_array_push(ctx, js_as_object(arr), v);
776
+ if (i < 0 || i > len) {
777
+ js_discard(sarray_out_of_range(ctx, i, len));
778
+ return -1;
779
+ }
780
+ return js_set_elem(ctx, arr, js_number((double)i), v, true);
781
+ }
782
+
783
+ JSValue js_sarray_length_slow(JSContext *ctx, JSValue arr) {
784
+ if (!js_is_array(arr)) return sarray_not_array(ctx, JS_SARRAY_NUMBER);
785
+ return js_throw_range_error(ctx, "A static array's length is below 2^31");
786
+ }
787
+
788
+ JSValue js_sarray_push_slow(JSContext *ctx, JSValue arr, JSValue v, int kind) {
789
+ if (js_sarray_set_slow(ctx, arr, js_is_array(arr) ? js_array_length(js_as_object(arr)) : 0, v, kind) < 0) return JS_EXCEPTION;
790
+ return js_sarray_length(ctx, arr);
791
+ }
792
+
743
793
  int js_array_append(JSContext *ctx, JSValue arr, JSValue v) {
744
794
  JSObject *a = js_as_object(arr);
745
795
  if (!js_is_empty(v)) return js_array_push(ctx, a, v);
@@ -821,7 +871,7 @@ JSValue js_get_value(JSContext *ctx, JSValue obj, JSValue key) {
821
871
  }
822
872
 
823
873
  int js_set_value(JSContext *ctx, JSValue obj, JSValue key, JSValue v, bool strict) {
824
- if (js_is_object(obj)) return js_set(ctx, js_as_object(obj), key, v, obj, strict);
874
+ if (js_is_object(obj)) return js_key_is_index(key) ? js_set(ctx, js_as_object(obj), key, v, obj, strict) : js_set_prop_slow(ctx, obj, key, v, strict, NULL);
825
875
  if (js_is_nullish(obj)) {
826
876
  js_discard(nullish_error(ctx, obj, key, "set"));
827
877
  return -1;
@@ -1045,6 +1095,11 @@ int js_set_prop_slow(JSContext *ctx, JSValue obj, JSValue key, JSValue v, bool s
1045
1095
  JS_ASSERT_NOT_EXC(obj);
1046
1096
  JS_ASSERT_NOT_EXC(v);
1047
1097
  if (!js_is_object(obj) || js_key_is_index(key)) return js_set_value(ctx, obj, key, v, strict);
1098
+ JSPropCache none; /* no site (a computed key): only the shared cache */
1099
+ if (!ic) {
1100
+ none.kind = JS_IC_EMPTY;
1101
+ ic = &none;
1102
+ }
1048
1103
  JSObject *o = js_as_object(obj);
1049
1104
  if (ic->kind == JS_IC_ADD && o->shape == ic->shape && chain_holds(ctx, ic)) {
1050
1105
  JSShape *next = ic->next; /* on the stack: kept alive if ensure_slot collects */
@@ -1074,7 +1129,8 @@ int js_set_prop_slow(JSContext *ctx, JSValue obj, JSValue key, JSValue v, bool s
1074
1129
  lookup(ctx, o, key, &l);
1075
1130
  JSShape *before = o->shape;
1076
1131
  bool cacheable = l.cacheable && !l.exotic;
1077
- if (cacheable && l.holder == o && (l.attrs & (JS_PROP_WRITABLE | JS_PROP_ACCESSOR)) == JS_PROP_WRITABLE) {
1132
+ bool intercepts = before->cls->define_own_property && before->cls != &js_class_array;
1133
+ if (cacheable && !intercepts && l.holder == o && (l.attrs & (JS_PROP_WRITABLE | JS_PROP_ACCESSOR)) == JS_PROP_WRITABLE) {
1078
1134
  JSPropCache c = with_fast_path((JSPropCache){ .kind = JS_IC_OWN, .slot = (uint32_t)l.slot, .shape = before });
1079
1135
  *ic = c;
1080
1136
  m->key = key;
@@ -1083,8 +1139,10 @@ int js_set_prop_slow(JSContext *ctx, JSValue obj, JSValue key, JSValue v, bool s
1083
1139
  return 1;
1084
1140
  }
1085
1141
  int r = js_set(ctx, o, key, v, obj, strict);
1086
- /* a plain add (no setter or read-only property on the chain): remember the transition */
1087
- if (r == 1 && cacheable && !l.holder && !before->cls->define_own_property && !is_dictionary(o) &&
1142
+ /* a plain add (no setter or read-only property on the chain, or a prototype's writable data
1143
+ * property it shadows, as EventEmitter.prototype._events): remember the transition */
1144
+ bool shadows = l.holder && l.holder != o && (l.attrs & (JS_PROP_WRITABLE | JS_PROP_ACCESSOR)) == JS_PROP_WRITABLE;
1145
+ if (r == 1 && cacheable && (!l.holder || shadows) && !before->cls->define_own_property && !is_dictionary(o) &&
1088
1146
  o->shape->parent == before && js_same_bits(o->shape->key, key)) {
1089
1147
  JSPropCache c = { .kind = JS_IC_ADD, .depth = l.depth, .shape = before, .next = o->shape, .epoch = epoch };
1090
1148
  memcpy(c.chain, l.chain, sizeof c.chain);
@@ -1098,9 +1156,32 @@ int js_set_prop_slow(JSContext *ctx, JSValue obj, JSValue key, JSValue v, bool s
1098
1156
 
1099
1157
  /* ---- Generated code: computed keys, delete, literals --------------------------------- */
1100
1158
 
1159
+ /* Whether `key` can be a keyed site's: a property key as it is (a symbol, an interned string
1160
+ * that isn't an index), so that the same bits mean the same key. Makes the site its. */
1161
+ static bool take_site_key(JSContext *ctx, JSValue key, JSValue *site, JSPropCache *ic) {
1162
+ if (!js_is_symbol(key) && !(js_is_string(key) && js_is_atom(key))) return false;
1163
+ JSValue k = js_to_property_key(ctx, key); /* a primitive: no user code */
1164
+ if (!js_same_bits(k, key) || js_key_is_index(k)) return false;
1165
+ memset(ic, 0, sizeof *ic); /* its entries were for the old key */
1166
+ *site = key;
1167
+ return true;
1168
+ }
1169
+
1170
+ JSValue js_get_keyed_slow(JSContext *ctx, JSValue obj, JSValue key, JSValue *site, JSPropCache *ic) {
1171
+ if (take_site_key(ctx, key, site, ic)) return js_get_prop(ctx, obj, key, ic);
1172
+ return js_get_elem(ctx, obj, key);
1173
+ }
1174
+
1175
+ int js_set_keyed_slow(JSContext *ctx, JSValue obj, JSValue key, JSValue v, bool strict, JSValue *site, JSPropCache *ic) {
1176
+ if (take_site_key(ctx, key, site, ic)) return js_set_prop(ctx, obj, key, v, strict, ic);
1177
+ return js_set_elem(ctx, obj, key, v, strict);
1178
+ }
1179
+
1101
1180
  JSValue js_get_elem(JSContext *ctx, JSValue obj, JSValue key) {
1102
1181
  JS_ASSERT_NOT_EXC(obj);
1103
1182
  JS_ASSERT_NOT_EXC(key);
1183
+ JSValue elem; /* a typed array's element: the component reads it inline */
1184
+ if (js_is_int32(key) && ctx->rt->typed_array_get && ctx->rt->typed_array_get(obj, js_as_int32(key), &elem)) return elem;
1104
1185
  if (js_is_nullish(obj)) {
1105
1186
  JSValue k = js_is_object(key) ? key : js_to_property_key(ctx, key); /* primitives only: no user code */
1106
1187
  if (js_is_exception(k)) return JS_EXCEPTION;
@@ -1113,6 +1194,8 @@ JSValue js_get_elem(JSContext *ctx, JSValue obj, JSValue key) {
1113
1194
 
1114
1195
  int js_set_elem(JSContext *ctx, JSValue obj, JSValue key, JSValue v, bool strict) {
1115
1196
  JS_ASSERT_NOT_EXC(v);
1197
+ /* a Number into a typed array's element: no ToNumber code to run, the component stores it inline */
1198
+ if (js_is_int32(key) && js_is_number(v) && ctx->rt->typed_array_set && ctx->rt->typed_array_set(obj, js_as_int32(key), js_as_number(v))) return 1;
1116
1199
  if (js_is_nullish(obj)) {
1117
1200
  JSValue k = js_is_object(key) ? js_empty_string() : js_to_property_key(ctx, key);
1118
1201
  if (js_is_exception(k)) return -1;
package/runtime/src/os.c CHANGED
@@ -151,6 +151,10 @@ const char *js_os_timezone_id(void) {
151
151
  return tz && *tz ? tz : ""; /* Windows zone names aren't IANA ids: the offset fallback */
152
152
  }
153
153
 
154
+ void js_os_timezone_changed(void) {
155
+ _tzset();
156
+ }
157
+
154
158
 
155
159
  double js_os_thread_cpu_ms(void) {
156
160
  FILETIME created, exited, kernel, user;
@@ -331,11 +335,18 @@ double js_os_time_ms(void) {
331
335
  return (double)ts.tv_sec * 1000.0 + (double)(ts.tv_nsec / 1000000);
332
336
  }
333
337
 
338
+ static bool tz_initialized, tz_id_done;
339
+
340
+ void js_os_timezone_changed(void) {
341
+ tzset();
342
+ tz_initialized = true;
343
+ tz_id_done = false;
344
+ }
345
+
334
346
  int32_t js_os_local_offset(int64_t seconds, bool *dst) {
335
- static bool initialized;
336
- if (!initialized) {
347
+ if (!tz_initialized) {
337
348
  tzset();
338
- initialized = true;
349
+ tz_initialized = true;
339
350
  }
340
351
  time_t t = (time_t)seconds;
341
352
  struct tm tm;
@@ -351,9 +362,8 @@ int32_t js_os_local_offset(int64_t seconds, bool *dst) {
351
362
  * points. */
352
363
  const char *js_os_timezone_id(void) {
353
364
  static char id[512];
354
- static bool done;
355
- if (done) return id;
356
- done = true;
365
+ if (tz_id_done) return id;
366
+ tz_id_done = true;
357
367
  const char *tz = getenv("TZ");
358
368
  char path[512] = "";
359
369
  if (tz && *tz) {