@scriptc/runtime 0.0.0 → 0.0.2

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 (416) hide show
  1. package/LICENSE +202 -0
  2. package/package.json +16 -6
  3. package/src/scr_array.c +488 -0
  4. package/src/scr_assert.c +1393 -0
  5. package/src/scr_async.c +2593 -0
  6. package/src/scr_async_dyn.c +607 -0
  7. package/src/scr_bytes.c +1425 -0
  8. package/src/scr_bytes_io.c +161 -0
  9. package/src/scr_child.c +3587 -0
  10. package/src/scr_closure.c +214 -0
  11. package/src/scr_console.c +143 -0
  12. package/src/scr_cycle.c +218 -0
  13. package/src/scr_dc.c +805 -0
  14. package/src/scr_dgram.c +1007 -0
  15. package/src/scr_dyn_handle.c +154 -0
  16. package/src/scr_dyn_invoke.c +644 -0
  17. package/src/scr_error.c +332 -0
  18. package/src/scr_events.c +714 -0
  19. package/src/scr_events_emitter.c +699 -0
  20. package/src/scr_exception.c +242 -0
  21. package/src/scr_fetch.c +1130 -0
  22. package/src/scr_fetch_curl.c +654 -0
  23. package/src/scr_http.c +3482 -0
  24. package/src/scr_http2.c +3030 -0
  25. package/src/scr_inspect.c +803 -0
  26. package/src/scr_inspect_island.c +51 -0
  27. package/src/scr_island.c +9363 -0
  28. package/src/scr_json.c +2151 -0
  29. package/src/scr_lib.c +3612 -0
  30. package/src/scr_loop_epoll.c +241 -0
  31. package/src/scr_loop_kqueue.c +121 -0
  32. package/src/scr_loop_wsapoll.c +208 -0
  33. package/src/scr_map.c +591 -0
  34. package/src/scr_net.c +3017 -0
  35. package/src/scr_net_island.c +458 -0
  36. package/src/scr_number.c +115 -0
  37. package/src/scr_object.c +25 -0
  38. package/src/scr_path.c +1266 -0
  39. package/src/scr_platform.h +119 -0
  40. package/src/scr_readline.c +287 -0
  41. package/src/scr_regex.c +1080 -0
  42. package/src/scr_runtime.h +5046 -0
  43. package/src/scr_stream.c +2877 -0
  44. package/src/scr_string.c +1264 -0
  45. package/src/scr_symbol.c +132 -0
  46. package/src/scr_test.c +662 -0
  47. package/src/scr_tls.c +1520 -0
  48. package/src/scr_union.c +105 -0
  49. package/src/scr_url.c +911 -0
  50. package/src/scr_url_internal.h +29 -0
  51. package/src/scr_url_params.c +561 -0
  52. package/src/scr_watch.c +568 -0
  53. package/src/scr_web.c +1778 -0
  54. package/src/scr_win.c +76 -0
  55. package/src/scr_zlib.c +197 -0
  56. package/src/scr_zlib_island.c +15 -0
  57. package/vendor/README.md +52 -0
  58. package/vendor/curl/COPYRIGHT +534 -0
  59. package/vendor/curl/include/curl/curl.h +3214 -0
  60. package/vendor/curl/include/curl/curlver.h +79 -0
  61. package/vendor/curl/include/curl/easy.h +125 -0
  62. package/vendor/curl/include/curl/header.h +74 -0
  63. package/vendor/curl/include/curl/mprintf.h +52 -0
  64. package/vendor/curl/include/curl/multi.h +460 -0
  65. package/vendor/curl/include/curl/options.h +70 -0
  66. package/vendor/curl/include/curl/stdcheaders.h +35 -0
  67. package/vendor/curl/include/curl/system.h +508 -0
  68. package/vendor/curl/include/curl/typecheck-gcc.h +716 -0
  69. package/vendor/curl/include/curl/urlapi.h +149 -0
  70. package/vendor/curl/include/curl/websockets.h +84 -0
  71. package/vendor/mbedtls/LICENSE +553 -0
  72. package/vendor/mbedtls/include/CMakeLists.txt +22 -0
  73. package/vendor/mbedtls/include/mbedtls/aes.h +631 -0
  74. package/vendor/mbedtls/include/mbedtls/aria.h +343 -0
  75. package/vendor/mbedtls/include/mbedtls/asn1.h +642 -0
  76. package/vendor/mbedtls/include/mbedtls/asn1write.h +390 -0
  77. package/vendor/mbedtls/include/mbedtls/base64.h +82 -0
  78. package/vendor/mbedtls/include/mbedtls/bignum.h +1088 -0
  79. package/vendor/mbedtls/include/mbedtls/block_cipher.h +76 -0
  80. package/vendor/mbedtls/include/mbedtls/build_info.h +194 -0
  81. package/vendor/mbedtls/include/mbedtls/camellia.h +305 -0
  82. package/vendor/mbedtls/include/mbedtls/ccm.h +526 -0
  83. package/vendor/mbedtls/include/mbedtls/chacha20.h +209 -0
  84. package/vendor/mbedtls/include/mbedtls/chachapoly.h +351 -0
  85. package/vendor/mbedtls/include/mbedtls/check_config.h +1149 -0
  86. package/vendor/mbedtls/include/mbedtls/cipher.h +1250 -0
  87. package/vendor/mbedtls/include/mbedtls/cmac.h +246 -0
  88. package/vendor/mbedtls/include/mbedtls/compat-2.x.h +46 -0
  89. package/vendor/mbedtls/include/mbedtls/config_adjust_legacy_crypto.h +578 -0
  90. package/vendor/mbedtls/include/mbedtls/config_adjust_legacy_from_psa.h +873 -0
  91. package/vendor/mbedtls/include/mbedtls/config_adjust_psa_from_legacy.h +359 -0
  92. package/vendor/mbedtls/include/mbedtls/config_adjust_psa_superset_legacy.h +145 -0
  93. package/vendor/mbedtls/include/mbedtls/config_adjust_ssl.h +91 -0
  94. package/vendor/mbedtls/include/mbedtls/config_adjust_x509.h +35 -0
  95. package/vendor/mbedtls/include/mbedtls/config_psa.h +61 -0
  96. package/vendor/mbedtls/include/mbedtls/constant_time.h +36 -0
  97. package/vendor/mbedtls/include/mbedtls/ctr_drbg.h +596 -0
  98. package/vendor/mbedtls/include/mbedtls/debug.h +156 -0
  99. package/vendor/mbedtls/include/mbedtls/des.h +385 -0
  100. package/vendor/mbedtls/include/mbedtls/dhm.h +972 -0
  101. package/vendor/mbedtls/include/mbedtls/ecdh.h +455 -0
  102. package/vendor/mbedtls/include/mbedtls/ecdsa.h +674 -0
  103. package/vendor/mbedtls/include/mbedtls/ecjpake.h +298 -0
  104. package/vendor/mbedtls/include/mbedtls/ecp.h +1517 -0
  105. package/vendor/mbedtls/include/mbedtls/entropy.h +274 -0
  106. package/vendor/mbedtls/include/mbedtls/error.h +201 -0
  107. package/vendor/mbedtls/include/mbedtls/gcm.h +387 -0
  108. package/vendor/mbedtls/include/mbedtls/hkdf.h +124 -0
  109. package/vendor/mbedtls/include/mbedtls/hmac_drbg.h +434 -0
  110. package/vendor/mbedtls/include/mbedtls/lms.h +440 -0
  111. package/vendor/mbedtls/include/mbedtls/mbedtls_config.h +4446 -0
  112. package/vendor/mbedtls/include/mbedtls/md.h +526 -0
  113. package/vendor/mbedtls/include/mbedtls/md5.h +190 -0
  114. package/vendor/mbedtls/include/mbedtls/memory_buffer_alloc.h +142 -0
  115. package/vendor/mbedtls/include/mbedtls/net_sockets.h +299 -0
  116. package/vendor/mbedtls/include/mbedtls/nist_kw.h +166 -0
  117. package/vendor/mbedtls/include/mbedtls/oid.h +727 -0
  118. package/vendor/mbedtls/include/mbedtls/pem.h +160 -0
  119. package/vendor/mbedtls/include/mbedtls/pk.h +1303 -0
  120. package/vendor/mbedtls/include/mbedtls/pkcs12.h +186 -0
  121. package/vendor/mbedtls/include/mbedtls/pkcs5.h +198 -0
  122. package/vendor/mbedtls/include/mbedtls/pkcs7.h +252 -0
  123. package/vendor/mbedtls/include/mbedtls/platform.h +516 -0
  124. package/vendor/mbedtls/include/mbedtls/platform_time.h +79 -0
  125. package/vendor/mbedtls/include/mbedtls/platform_util.h +247 -0
  126. package/vendor/mbedtls/include/mbedtls/poly1305.h +168 -0
  127. package/vendor/mbedtls/include/mbedtls/private_access.h +20 -0
  128. package/vendor/mbedtls/include/mbedtls/psa_util.h +207 -0
  129. package/vendor/mbedtls/include/mbedtls/ripemd160.h +136 -0
  130. package/vendor/mbedtls/include/mbedtls/rsa.h +1170 -0
  131. package/vendor/mbedtls/include/mbedtls/sha1.h +219 -0
  132. package/vendor/mbedtls/include/mbedtls/sha256.h +200 -0
  133. package/vendor/mbedtls/include/mbedtls/sha3.h +172 -0
  134. package/vendor/mbedtls/include/mbedtls/sha512.h +208 -0
  135. package/vendor/mbedtls/include/mbedtls/ssl.h +5887 -0
  136. package/vendor/mbedtls/include/mbedtls/ssl_cache.h +187 -0
  137. package/vendor/mbedtls/include/mbedtls/ssl_ciphersuites.h +482 -0
  138. package/vendor/mbedtls/include/mbedtls/ssl_cookie.h +106 -0
  139. package/vendor/mbedtls/include/mbedtls/ssl_ticket.h +199 -0
  140. package/vendor/mbedtls/include/mbedtls/threading.h +167 -0
  141. package/vendor/mbedtls/include/mbedtls/timing.h +94 -0
  142. package/vendor/mbedtls/include/mbedtls/version.h +78 -0
  143. package/vendor/mbedtls/include/mbedtls/x509.h +500 -0
  144. package/vendor/mbedtls/include/mbedtls/x509_crl.h +184 -0
  145. package/vendor/mbedtls/include/mbedtls/x509_crt.h +1208 -0
  146. package/vendor/mbedtls/include/mbedtls/x509_csr.h +382 -0
  147. package/vendor/mbedtls/include/psa/build_info.h +20 -0
  148. package/vendor/mbedtls/include/psa/crypto.h +4998 -0
  149. package/vendor/mbedtls/include/psa/crypto_adjust_auto_enabled.h +31 -0
  150. package/vendor/mbedtls/include/psa/crypto_adjust_config_dependencies.h +51 -0
  151. package/vendor/mbedtls/include/psa/crypto_adjust_config_key_pair_types.h +101 -0
  152. package/vendor/mbedtls/include/psa/crypto_adjust_config_synonyms.h +49 -0
  153. package/vendor/mbedtls/include/psa/crypto_builtin_composites.h +214 -0
  154. package/vendor/mbedtls/include/psa/crypto_builtin_key_derivation.h +118 -0
  155. package/vendor/mbedtls/include/psa/crypto_builtin_primitives.h +114 -0
  156. package/vendor/mbedtls/include/psa/crypto_compat.h +230 -0
  157. package/vendor/mbedtls/include/psa/crypto_config.h +145 -0
  158. package/vendor/mbedtls/include/psa/crypto_driver_common.h +44 -0
  159. package/vendor/mbedtls/include/psa/crypto_driver_contexts_composites.h +151 -0
  160. package/vendor/mbedtls/include/psa/crypto_driver_contexts_key_derivation.h +52 -0
  161. package/vendor/mbedtls/include/psa/crypto_driver_contexts_primitives.h +105 -0
  162. package/vendor/mbedtls/include/psa/crypto_extra.h +2145 -0
  163. package/vendor/mbedtls/include/psa/crypto_legacy.h +88 -0
  164. package/vendor/mbedtls/include/psa/crypto_platform.h +102 -0
  165. package/vendor/mbedtls/include/psa/crypto_se_driver.h +1383 -0
  166. package/vendor/mbedtls/include/psa/crypto_sizes.h +1319 -0
  167. package/vendor/mbedtls/include/psa/crypto_struct.h +527 -0
  168. package/vendor/mbedtls/include/psa/crypto_types.h +508 -0
  169. package/vendor/mbedtls/include/psa/crypto_values.h +2782 -0
  170. package/vendor/mbedtls/library/aes.c +2294 -0
  171. package/vendor/mbedtls/library/aesce.c +624 -0
  172. package/vendor/mbedtls/library/aesce.h +136 -0
  173. package/vendor/mbedtls/library/aesni.c +846 -0
  174. package/vendor/mbedtls/library/aesni.h +162 -0
  175. package/vendor/mbedtls/library/alignment.h +704 -0
  176. package/vendor/mbedtls/library/aria.c +969 -0
  177. package/vendor/mbedtls/library/asn1parse.c +468 -0
  178. package/vendor/mbedtls/library/asn1write.c +440 -0
  179. package/vendor/mbedtls/library/base64.c +322 -0
  180. package/vendor/mbedtls/library/base64_internal.h +45 -0
  181. package/vendor/mbedtls/library/bignum.c +2583 -0
  182. package/vendor/mbedtls/library/bignum_core.c +1240 -0
  183. package/vendor/mbedtls/library/bignum_core.h +872 -0
  184. package/vendor/mbedtls/library/bignum_core_invasive.h +38 -0
  185. package/vendor/mbedtls/library/bignum_internal.h +122 -0
  186. package/vendor/mbedtls/library/bignum_mod.c +394 -0
  187. package/vendor/mbedtls/library/bignum_mod.h +452 -0
  188. package/vendor/mbedtls/library/bignum_mod_raw.c +276 -0
  189. package/vendor/mbedtls/library/bignum_mod_raw.h +416 -0
  190. package/vendor/mbedtls/library/bignum_mod_raw_invasive.h +34 -0
  191. package/vendor/mbedtls/library/block_cipher.c +207 -0
  192. package/vendor/mbedtls/library/block_cipher_internal.h +99 -0
  193. package/vendor/mbedtls/library/bn_mul.h +1094 -0
  194. package/vendor/mbedtls/library/camellia.c +1058 -0
  195. package/vendor/mbedtls/library/ccm.c +777 -0
  196. package/vendor/mbedtls/library/chacha20.c +563 -0
  197. package/vendor/mbedtls/library/chacha20_internal.h +23 -0
  198. package/vendor/mbedtls/library/chachapoly.c +487 -0
  199. package/vendor/mbedtls/library/check_crypto_config.h +136 -0
  200. package/vendor/mbedtls/library/cipher.c +1712 -0
  201. package/vendor/mbedtls/library/cipher_invasive.h +28 -0
  202. package/vendor/mbedtls/library/cipher_wrap.c +2482 -0
  203. package/vendor/mbedtls/library/cipher_wrap.h +178 -0
  204. package/vendor/mbedtls/library/cmac.c +1067 -0
  205. package/vendor/mbedtls/library/common.h +487 -0
  206. package/vendor/mbedtls/library/constant_time.c +247 -0
  207. package/vendor/mbedtls/library/constant_time_impl.h +541 -0
  208. package/vendor/mbedtls/library/constant_time_internal.h +579 -0
  209. package/vendor/mbedtls/library/ctr.h +35 -0
  210. package/vendor/mbedtls/library/ctr_drbg.c +1016 -0
  211. package/vendor/mbedtls/library/debug.c +475 -0
  212. package/vendor/mbedtls/library/debug_internal.h +185 -0
  213. package/vendor/mbedtls/library/des.c +1042 -0
  214. package/vendor/mbedtls/library/dhm.c +700 -0
  215. package/vendor/mbedtls/library/ecdh.c +696 -0
  216. package/vendor/mbedtls/library/ecdsa.c +858 -0
  217. package/vendor/mbedtls/library/ecjpake.c +1216 -0
  218. package/vendor/mbedtls/library/ecp.c +3674 -0
  219. package/vendor/mbedtls/library/ecp_curves.c +6125 -0
  220. package/vendor/mbedtls/library/ecp_curves_new.c +9 -0
  221. package/vendor/mbedtls/library/ecp_internal_alt.h +287 -0
  222. package/vendor/mbedtls/library/ecp_invasive.h +305 -0
  223. package/vendor/mbedtls/library/entropy.c +680 -0
  224. package/vendor/mbedtls/library/entropy_poll.c +233 -0
  225. package/vendor/mbedtls/library/entropy_poll.h +64 -0
  226. package/vendor/mbedtls/library/error.c +878 -0
  227. package/vendor/mbedtls/library/gcm.c +1330 -0
  228. package/vendor/mbedtls/library/hkdf.c +161 -0
  229. package/vendor/mbedtls/library/hmac_drbg.c +633 -0
  230. package/vendor/mbedtls/library/lmots.c +789 -0
  231. package/vendor/mbedtls/library/lmots.h +288 -0
  232. package/vendor/mbedtls/library/lms.c +778 -0
  233. package/vendor/mbedtls/library/md.c +1108 -0
  234. package/vendor/mbedtls/library/md5.c +426 -0
  235. package/vendor/mbedtls/library/md_psa.h +26 -0
  236. package/vendor/mbedtls/library/md_wrap.h +46 -0
  237. package/vendor/mbedtls/library/memory_buffer_alloc.c +751 -0
  238. package/vendor/mbedtls/library/mps_common.h +181 -0
  239. package/vendor/mbedtls/library/mps_error.h +89 -0
  240. package/vendor/mbedtls/library/mps_reader.c +538 -0
  241. package/vendor/mbedtls/library/mps_reader.h +366 -0
  242. package/vendor/mbedtls/library/mps_trace.c +112 -0
  243. package/vendor/mbedtls/library/mps_trace.h +154 -0
  244. package/vendor/mbedtls/library/net_sockets.c +694 -0
  245. package/vendor/mbedtls/library/nist_kw.c +729 -0
  246. package/vendor/mbedtls/library/oid.c +1166 -0
  247. package/vendor/mbedtls/library/padlock.c +157 -0
  248. package/vendor/mbedtls/library/padlock.h +111 -0
  249. package/vendor/mbedtls/library/pem.c +554 -0
  250. package/vendor/mbedtls/library/pk.c +1602 -0
  251. package/vendor/mbedtls/library/pk_ecc.c +261 -0
  252. package/vendor/mbedtls/library/pk_internal.h +241 -0
  253. package/vendor/mbedtls/library/pk_wrap.c +1618 -0
  254. package/vendor/mbedtls/library/pk_wrap.h +138 -0
  255. package/vendor/mbedtls/library/pkcs12.c +437 -0
  256. package/vendor/mbedtls/library/pkcs5.c +500 -0
  257. package/vendor/mbedtls/library/pkcs7.c +787 -0
  258. package/vendor/mbedtls/library/pkparse.c +1392 -0
  259. package/vendor/mbedtls/library/pkwrite.c +631 -0
  260. package/vendor/mbedtls/library/pkwrite.h +121 -0
  261. package/vendor/mbedtls/library/platform.c +402 -0
  262. package/vendor/mbedtls/library/platform_util.c +258 -0
  263. package/vendor/mbedtls/library/poly1305.c +492 -0
  264. package/vendor/mbedtls/library/psa_crypto.c +9532 -0
  265. package/vendor/mbedtls/library/psa_crypto_aead.c +646 -0
  266. package/vendor/mbedtls/library/psa_crypto_aead.h +499 -0
  267. package/vendor/mbedtls/library/psa_crypto_cipher.c +747 -0
  268. package/vendor/mbedtls/library/psa_crypto_cipher.h +316 -0
  269. package/vendor/mbedtls/library/psa_crypto_client.c +22 -0
  270. package/vendor/mbedtls/library/psa_crypto_core.h +983 -0
  271. package/vendor/mbedtls/library/psa_crypto_core_common.h +52 -0
  272. package/vendor/mbedtls/library/psa_crypto_driver_wrappers.h +2896 -0
  273. package/vendor/mbedtls/library/psa_crypto_driver_wrappers_no_static.c +256 -0
  274. package/vendor/mbedtls/library/psa_crypto_driver_wrappers_no_static.h +31 -0
  275. package/vendor/mbedtls/library/psa_crypto_ecp.c +594 -0
  276. package/vendor/mbedtls/library/psa_crypto_ecp.h +267 -0
  277. package/vendor/mbedtls/library/psa_crypto_ffdh.c +361 -0
  278. package/vendor/mbedtls/library/psa_crypto_ffdh.h +131 -0
  279. package/vendor/mbedtls/library/psa_crypto_hash.c +470 -0
  280. package/vendor/mbedtls/library/psa_crypto_hash.h +211 -0
  281. package/vendor/mbedtls/library/psa_crypto_invasive.h +92 -0
  282. package/vendor/mbedtls/library/psa_crypto_its.h +131 -0
  283. package/vendor/mbedtls/library/psa_crypto_mac.c +505 -0
  284. package/vendor/mbedtls/library/psa_crypto_mac.h +264 -0
  285. package/vendor/mbedtls/library/psa_crypto_pake.c +571 -0
  286. package/vendor/mbedtls/library/psa_crypto_pake.h +159 -0
  287. package/vendor/mbedtls/library/psa_crypto_random.c +181 -0
  288. package/vendor/mbedtls/library/psa_crypto_random.h +72 -0
  289. package/vendor/mbedtls/library/psa_crypto_random_impl.h +200 -0
  290. package/vendor/mbedtls/library/psa_crypto_rsa.c +714 -0
  291. package/vendor/mbedtls/library/psa_crypto_rsa.h +321 -0
  292. package/vendor/mbedtls/library/psa_crypto_se.c +373 -0
  293. package/vendor/mbedtls/library/psa_crypto_se.h +192 -0
  294. package/vendor/mbedtls/library/psa_crypto_slot_management.c +1137 -0
  295. package/vendor/mbedtls/library/psa_crypto_slot_management.h +344 -0
  296. package/vendor/mbedtls/library/psa_crypto_storage.c +481 -0
  297. package/vendor/mbedtls/library/psa_crypto_storage.h +392 -0
  298. package/vendor/mbedtls/library/psa_its_file.c +254 -0
  299. package/vendor/mbedtls/library/psa_util.c +614 -0
  300. package/vendor/mbedtls/library/psa_util_internal.h +100 -0
  301. package/vendor/mbedtls/library/ripemd160.c +490 -0
  302. package/vendor/mbedtls/library/rsa.c +3121 -0
  303. package/vendor/mbedtls/library/rsa_alt_helpers.c +455 -0
  304. package/vendor/mbedtls/library/rsa_alt_helpers.h +209 -0
  305. package/vendor/mbedtls/library/rsa_internal.h +188 -0
  306. package/vendor/mbedtls/library/sha1.c +480 -0
  307. package/vendor/mbedtls/library/sha256.c +983 -0
  308. package/vendor/mbedtls/library/sha3.c +721 -0
  309. package/vendor/mbedtls/library/sha512.c +1115 -0
  310. package/vendor/mbedtls/library/ssl_cache.c +410 -0
  311. package/vendor/mbedtls/library/ssl_ciphersuites.c +2050 -0
  312. package/vendor/mbedtls/library/ssl_ciphersuites_internal.h +154 -0
  313. package/vendor/mbedtls/library/ssl_client.c +1023 -0
  314. package/vendor/mbedtls/library/ssl_client.h +22 -0
  315. package/vendor/mbedtls/library/ssl_cookie.c +384 -0
  316. package/vendor/mbedtls/library/ssl_debug_helpers.h +85 -0
  317. package/vendor/mbedtls/library/ssl_debug_helpers_generated.c +251 -0
  318. package/vendor/mbedtls/library/ssl_misc.h +3198 -0
  319. package/vendor/mbedtls/library/ssl_msg.c +6619 -0
  320. package/vendor/mbedtls/library/ssl_ticket.c +556 -0
  321. package/vendor/mbedtls/library/ssl_tls.c +10234 -0
  322. package/vendor/mbedtls/library/ssl_tls12_client.c +3688 -0
  323. package/vendor/mbedtls/library/ssl_tls12_server.c +4311 -0
  324. package/vendor/mbedtls/library/ssl_tls13_client.c +3208 -0
  325. package/vendor/mbedtls/library/ssl_tls13_generic.c +1793 -0
  326. package/vendor/mbedtls/library/ssl_tls13_invasive.h +23 -0
  327. package/vendor/mbedtls/library/ssl_tls13_keys.c +1918 -0
  328. package/vendor/mbedtls/library/ssl_tls13_keys.h +668 -0
  329. package/vendor/mbedtls/library/ssl_tls13_server.c +3624 -0
  330. package/vendor/mbedtls/library/threading.c +193 -0
  331. package/vendor/mbedtls/library/threading_internal.h +28 -0
  332. package/vendor/mbedtls/library/timing.c +152 -0
  333. package/vendor/mbedtls/library/version.c +32 -0
  334. package/vendor/mbedtls/library/version_features.c +856 -0
  335. package/vendor/mbedtls/library/x509.c +1776 -0
  336. package/vendor/mbedtls/library/x509_create.c +570 -0
  337. package/vendor/mbedtls/library/x509_crl.c +708 -0
  338. package/vendor/mbedtls/library/x509_crt.c +3311 -0
  339. package/vendor/mbedtls/library/x509_csr.c +648 -0
  340. package/vendor/mbedtls/library/x509_internal.h +101 -0
  341. package/vendor/mbedtls/library/x509write.c +174 -0
  342. package/vendor/mbedtls/library/x509write_crt.c +688 -0
  343. package/vendor/mbedtls/library/x509write_csr.c +336 -0
  344. package/vendor/quickjs-ng/CMakeLists.txt +566 -0
  345. package/vendor/quickjs-ng/LICENSE +24 -0
  346. package/vendor/quickjs-ng/README.md +24 -0
  347. package/vendor/quickjs-ng/api-test.c +1195 -0
  348. package/vendor/quickjs-ng/builtin-array-fromasync.h +119 -0
  349. package/vendor/quickjs-ng/builtin-iterator-zip-keyed.h +332 -0
  350. package/vendor/quickjs-ng/builtin-iterator-zip.h +337 -0
  351. package/vendor/quickjs-ng/cutils.h +1998 -0
  352. package/vendor/quickjs-ng/dtoa.c +1619 -0
  353. package/vendor/quickjs-ng/dtoa.h +87 -0
  354. package/vendor/quickjs-ng/gen/function_source.c +81 -0
  355. package/vendor/quickjs-ng/gen/hello.c +53 -0
  356. package/vendor/quickjs-ng/gen/hello_module.c +106 -0
  357. package/vendor/quickjs-ng/gen/repl.c +3036 -0
  358. package/vendor/quickjs-ng/gen/standalone.c +323 -0
  359. package/vendor/quickjs-ng/gen/test_fib.c +81 -0
  360. package/vendor/quickjs-ng/libregexp-opcode.h +73 -0
  361. package/vendor/quickjs-ng/libregexp.c +3474 -0
  362. package/vendor/quickjs-ng/libregexp.h +101 -0
  363. package/vendor/quickjs-ng/libunicode-table.h +5173 -0
  364. package/vendor/quickjs-ng/libunicode.c +2069 -0
  365. package/vendor/quickjs-ng/libunicode.h +172 -0
  366. package/vendor/quickjs-ng/list.h +107 -0
  367. package/vendor/quickjs-ng/lre-test.c +52 -0
  368. package/vendor/quickjs-ng/qjs.c +762 -0
  369. package/vendor/quickjs-ng/qjsc.c +673 -0
  370. package/vendor/quickjs-ng/quickjs-atom.h +280 -0
  371. package/vendor/quickjs-ng/quickjs-c-atomics.h +54 -0
  372. package/vendor/quickjs-ng/quickjs-libc.c +5037 -0
  373. package/vendor/quickjs-ng/quickjs-libc.h +87 -0
  374. package/vendor/quickjs-ng/quickjs-opcode.h +377 -0
  375. package/vendor/quickjs-ng/quickjs.c +64041 -0
  376. package/vendor/quickjs-ng/quickjs.h +1447 -0
  377. package/vendor/quickjs-ng/run-test262.c +2374 -0
  378. package/vendor/quickjs-ng/unicode_gen.c +3121 -0
  379. package/vendor/quickjs-ng/unicode_gen_def.h +310 -0
  380. package/vendor/ryu/LICENSE-Boost +23 -0
  381. package/vendor/ryu/common.h +114 -0
  382. package/vendor/ryu/d2s.c +509 -0
  383. package/vendor/ryu/d2s_full_table.h +367 -0
  384. package/vendor/ryu/d2s_intrinsics.h +357 -0
  385. package/vendor/ryu/d2s_small_table.h +186 -0
  386. package/vendor/ryu/digit_table.h +35 -0
  387. package/vendor/ryu/ryu.h +46 -0
  388. package/vendor/zlib/LICENSE +22 -0
  389. package/vendor/zlib/adler32.c +164 -0
  390. package/vendor/zlib/compress.c +75 -0
  391. package/vendor/zlib/crc32.c +1049 -0
  392. package/vendor/zlib/crc32.h +9446 -0
  393. package/vendor/zlib/deflate.c +2139 -0
  394. package/vendor/zlib/deflate.h +377 -0
  395. package/vendor/zlib/gzclose.c +23 -0
  396. package/vendor/zlib/gzguts.h +214 -0
  397. package/vendor/zlib/gzlib.c +582 -0
  398. package/vendor/zlib/gzread.c +602 -0
  399. package/vendor/zlib/gzwrite.c +631 -0
  400. package/vendor/zlib/infback.c +628 -0
  401. package/vendor/zlib/inffast.c +320 -0
  402. package/vendor/zlib/inffast.h +11 -0
  403. package/vendor/zlib/inffixed.h +94 -0
  404. package/vendor/zlib/inflate.c +1526 -0
  405. package/vendor/zlib/inflate.h +126 -0
  406. package/vendor/zlib/inftrees.c +299 -0
  407. package/vendor/zlib/inftrees.h +62 -0
  408. package/vendor/zlib/trees.c +1117 -0
  409. package/vendor/zlib/trees.h +128 -0
  410. package/vendor/zlib/uncompr.c +85 -0
  411. package/vendor/zlib/zconf.h +543 -0
  412. package/vendor/zlib/zlib.h +1938 -0
  413. package/vendor/zlib/zutil.c +299 -0
  414. package/vendor/zlib/zutil.h +254 -0
  415. package/README.md +0 -3
  416. package/index.js +0 -2
package/src/scr_json.c ADDED
@@ -0,0 +1,2151 @@
1
+ /* JSON + dynamic values (see scr_runtime.h for the API contract).
2
+ *
3
+ * - The ScrDyn DOM is the runtime shape of `unknown`: refcounted, owning
4
+ * its children; releasing the root frees the tree recursively.
5
+ * - scr_json_parse is a full RFC 8259 recursive-descent parser: null/
6
+ * true/false, numbers (strtod after a strict grammar check — doubles
7
+ * only, like JS), strings with every escape including \uXXXX (encoded to
8
+ * UTF-8; surrogate pairs combine, lone surrogates become U+FFFD per house
9
+ * policy), arrays, objects (later duplicate keys win, like JS), and the
10
+ * four JSON whitespace characters. Syntax errors THROW catchable
11
+ * SyntaxError instances (the depth cap a RangeError, like V8's) whose
12
+ * messages are shaped like Node's V8 texts ("Unexpected end of JSON
13
+ * input", "Unexpected token 'x', \"...\" is not valid JSON") —
14
+ * APPROXIMATE message fidelity by design (SEMANTICS.md; e.name is exact,
15
+ * e.message is ours), pinned by the runtime C tests.
16
+ * - scr_dyn_check_fail is the shared failure path of the compiler-emitted
17
+ * dynCheck builders: a TypeError instance carrying "expected <want> at
18
+ * <path>, got <kind>", thrown through the exception cell.
19
+ * scriptc-specific — JS `as` never checks anything (the headline
20
+ * divergence in SEMANTICS.md).
21
+ * - ScrJsonBuf backs the compiler-emitted type-directed stringify
22
+ * serializers (and the error messages here).
23
+ */
24
+ #include "scr_runtime.h"
25
+
26
+ #include <math.h>
27
+ #include <stdio.h>
28
+ #include <stdlib.h>
29
+ #include <string.h>
30
+
31
+ /* Live dyn-node count for the RC audit lane (-DSCR_RC_AUDIT); same contract
32
+ * as scr_str_live_count in scr_string.c. */
33
+ #ifdef SCR_RC_AUDIT
34
+ static long scr_live_dyns = 0;
35
+ long scr_dyn_live_count(void) { return scr_live_dyns; }
36
+ #endif
37
+
38
+ static void scr_json_oom(void) {
39
+ fputs("scriptc: out of memory\n", stderr);
40
+ abort();
41
+ }
42
+
43
+ /* ── output buffer ─────────────────────────────────────────────────────
44
+ * The buffer IS a growing ScrStr allocation (data points at its data[]),
45
+ * so scr_jb_finish hands the bytes over without a copy. A size hint
46
+ * remembers the last finished capacity: a stringify loop allocates once
47
+ * per document instead of doubling its way up every round.
48
+ */
49
+
50
+ /* The ScrStr block behind a non-empty buffer. */
51
+ #define SCR_JB_STR(b) ((ScrStr *)((char *)(b)->data - offsetof(ScrStr, data)))
52
+
53
+ static size_t scr_jb_hint = 64;
54
+
55
+ void scr_jb_init(ScrJsonBuf *b) {
56
+ b->data = NULL;
57
+ b->len = 0;
58
+ b->cap = 0;
59
+ }
60
+
61
+ static void scr_jb_grow(ScrJsonBuf *b, size_t need) {
62
+ if (b->len + need <= b->cap) return;
63
+ if (!b->data) { /* first allocation: len == 0 */
64
+ size_t cap = scr_jb_hint >= need ? scr_jb_hint : need;
65
+ ScrStr *s = scr_str_alloc_raw(0, cap);
66
+ b->data = s->data;
67
+ b->cap = s->cap; /* a reused spare block may be larger */
68
+ return;
69
+ }
70
+ size_t cap = b->cap;
71
+ while (cap < b->len + need) cap *= 2;
72
+ ScrStr *s = scr_str_regrow(SCR_JB_STR(b), cap);
73
+ b->data = s->data;
74
+ b->cap = cap;
75
+ }
76
+
77
+ /* Abandon a buffer (parser error paths). */
78
+ static void scr_jb_dispose(ScrJsonBuf *b) {
79
+ if (b->data) scr_str_release(SCR_JB_STR(b));
80
+ scr_jb_init(b);
81
+ }
82
+
83
+ void scr_jb_putc(ScrJsonBuf *b, char c) {
84
+ scr_jb_grow(b, 1);
85
+ b->data[b->len++] = c;
86
+ }
87
+
88
+ static void scr_jb_write(ScrJsonBuf *b, const char *s, size_t n) {
89
+ scr_jb_grow(b, n);
90
+ memcpy(b->data + b->len, s, n);
91
+ b->len += n;
92
+ }
93
+
94
+ void scr_jb_puts(ScrJsonBuf *b, const char *s) { scr_jb_write(b, s, strlen(s)); }
95
+
96
+ void scr_jb_put_f64(ScrJsonBuf *b, double v) {
97
+ /* JSON.stringify number rules: non-finite → null, -0 → "0" (String(-0)
98
+ * is "0" too, so scr_f64_to_str would agree — the zero test just makes
99
+ * the rule explicit), else shortest-roundtrip digits. */
100
+ if (!isfinite(v)) {
101
+ scr_jb_puts(b, "null");
102
+ return;
103
+ }
104
+ if (v == 0) {
105
+ scr_jb_putc(b, '0');
106
+ return;
107
+ }
108
+ char buf[32];
109
+ size_t n = scr_f64_to_str(v, buf);
110
+ scr_jb_write(b, buf, n);
111
+ }
112
+
113
+ void scr_jb_put_json_str(ScrJsonBuf *b, const ScrStr *s) {
114
+ scr_jb_putc(b, '"');
115
+ /* Bulk-copy runs of unescaped bytes (UTF-8 passes through verbatim,
116
+ * like JS); escapes interrupt the run. */
117
+ size_t i = 0, run = 0;
118
+ while (i + run < s->len) {
119
+ unsigned char c = (unsigned char)s->data[i + run];
120
+ if (c != '"' && c != '\\' && c >= 0x20) {
121
+ run++;
122
+ continue;
123
+ }
124
+ scr_jb_write(b, s->data + i, run);
125
+ switch (c) {
126
+ case '"': scr_jb_puts(b, "\\\""); break;
127
+ case '\\': scr_jb_puts(b, "\\\\"); break;
128
+ case '\n': scr_jb_puts(b, "\\n"); break;
129
+ case '\r': scr_jb_puts(b, "\\r"); break;
130
+ case '\t': scr_jb_puts(b, "\\t"); break;
131
+ case '\b': scr_jb_puts(b, "\\b"); break;
132
+ case '\f': scr_jb_puts(b, "\\f"); break;
133
+ default: {
134
+ char esc[8];
135
+ snprintf(esc, sizeof esc, "\\u%04x", c);
136
+ scr_jb_puts(b, esc);
137
+ }
138
+ }
139
+ i += run + 1;
140
+ run = 0;
141
+ }
142
+ scr_jb_write(b, s->data + i, run);
143
+ scr_jb_putc(b, '"');
144
+ }
145
+
146
+ ScrStr *scr_jb_finish(ScrJsonBuf *b) {
147
+ if (!b->data) return scr_str_new("", 0);
148
+ ScrStr *s = SCR_JB_STR(b);
149
+ s->len = b->len;
150
+ s->data[b->len] = '\0';
151
+ /* Remember the size class for the next buffer (bounded so one giant
152
+ * document cannot pin big allocations forever). */
153
+ if (s->cap > scr_jb_hint) scr_jb_hint = s->cap < (1 << 16) ? s->cap : (1 << 16);
154
+ scr_jb_init(b);
155
+ return s;
156
+ }
157
+
158
+ /* ── DOM lifecycle ─────────────────────────────────────────────────────
159
+ * Parse/release churn (a JSON round-trip loop allocates and frees every
160
+ * node each iteration) runs on freelists instead of calloc/free: one list
161
+ * per shape so arr/obj nodes keep their items/entries buffer across
162
+ * reuse — a loop re-parsing the same document shape stops calling malloc
163
+ * altogether. The freelist link overlays v.arr.len (first union word), so
164
+ * a recycled arr/obj node's buffer and capacity survive intact. Disabled
165
+ * in the audit lane so ASan sees real frees and the live count stays a
166
+ * strict alloc/free balance.
167
+ */
168
+ #ifndef SCR_RC_AUDIT
169
+ static ScrDyn *scr_dyn_free_arr, *scr_dyn_free_obj, *scr_dyn_free_misc;
170
+ static size_t scr_dyn_free_count;
171
+ #define SCR_DYN_FREE_MAX 8192
172
+ #endif
173
+
174
+ static ScrDyn *scr_dyn_alloc(ScrDynKind kind) {
175
+ #ifndef SCR_RC_AUDIT
176
+ ScrDyn **list = kind == SCR_DYN_ARR ? &scr_dyn_free_arr
177
+ : kind == SCR_DYN_OBJ ? &scr_dyn_free_obj
178
+ : &scr_dyn_free_misc;
179
+ ScrDyn *d = *list;
180
+ if (d) {
181
+ *list = (ScrDyn *)d->v.str; /* freelist link */
182
+ scr_dyn_free_count--;
183
+ d->rc = 1;
184
+ d->kind = kind;
185
+ d->buffer = false;
186
+ if (kind == SCR_DYN_ARR) {
187
+ d->v.arr.len = 0; /* cap/items preserved from the node's last life */
188
+ } else if (kind == SCR_DYN_OBJ) {
189
+ d->v.obj.len = 0; /* cap/entries preserved */
190
+ } else {
191
+ memset(&d->v, 0, sizeof d->v);
192
+ }
193
+ return d;
194
+ }
195
+ #endif
196
+ ScrDyn *fresh = calloc(1, sizeof *fresh);
197
+ if (!fresh) scr_json_oom();
198
+ fresh->rc = 1;
199
+ fresh->kind = kind;
200
+ #ifdef SCR_RC_AUDIT
201
+ scr_live_dyns++;
202
+ #endif
203
+ return fresh;
204
+ }
205
+
206
+ static void scr_dyn_handle_release(void *h, ScrDynHandleTag tag);
207
+
208
+ void scr_dyn_release(ScrDyn *d) {
209
+ if (!d || d->rc == SIZE_MAX) return; /* NULL: an uninitialized `let` local */
210
+ if (--d->rc != 0) return;
211
+ switch (d->kind) {
212
+ case SCR_DYN_STR:
213
+ scr_str_release(d->v.str);
214
+ break;
215
+ case SCR_DYN_BYTES:
216
+ scr_bytes_release(d->v.bytes);
217
+ break;
218
+ case SCR_DYN_ARR:
219
+ for (size_t i = 0; i < d->v.arr.len; i++) scr_dyn_release(d->v.arr.items[i]);
220
+ break;
221
+ case SCR_DYN_OBJ:
222
+ for (size_t i = 0; i < d->v.obj.len; i++) {
223
+ free(d->v.obj.entries[i].key);
224
+ scr_dyn_release(d->v.obj.entries[i].value);
225
+ }
226
+ break;
227
+ case SCR_DYN_FUNC:
228
+ scr_closure_release(d->v.fn.clo); /* sig/name are static literals */
229
+ break;
230
+ case SCR_DYN_HANDLE:
231
+ scr_dyn_handle_release(d->v.handle.ptr, d->v.handle.tag);
232
+ break;
233
+ case SCR_DYN_PROMISE:
234
+ /* Installed by scr_dyn_alloc_promise (the gated boxes are the only
235
+ * constructors) — a promise-free link (the runtime unit tests bind
236
+ * scr_json.c without the fiber machinery) never references it. */
237
+ scr_dyn_promise_release_fn(d->v.promise);
238
+ break;
239
+ default:
240
+ break; /* null/bool/num have no children */
241
+ }
242
+ #ifdef SCR_RC_AUDIT
243
+ scr_live_dyns--;
244
+ #endif
245
+ #ifndef SCR_RC_AUDIT
246
+ if (scr_dyn_free_count < SCR_DYN_FREE_MAX) {
247
+ ScrDyn **list = d->kind == SCR_DYN_ARR ? &scr_dyn_free_arr
248
+ : d->kind == SCR_DYN_OBJ ? &scr_dyn_free_obj
249
+ : &scr_dyn_free_misc;
250
+ d->v.str = (ScrStr *)*list; /* overlays arr/obj len; buffer survives */
251
+ *list = d;
252
+ scr_dyn_free_count++;
253
+ return;
254
+ }
255
+ #endif
256
+ if (d->kind == SCR_DYN_ARR) free(d->v.arr.items);
257
+ else if (d->kind == SCR_DYN_OBJ) free(d->v.obj.entries);
258
+ free(d);
259
+ }
260
+
261
+ ScrDyn *scr_dyn_obj_get(const ScrDyn *d, const char *key, size_t key_len) {
262
+ for (size_t i = 0; i < d->v.obj.len; i++) {
263
+ const ScrDynEntry *e = &d->v.obj.entries[i];
264
+ if (e->key_len == key_len && memcmp(e->key, key, key_len) == 0) return e->value;
265
+ }
266
+ return NULL;
267
+ }
268
+
269
+ /* Public: the compiler-emitted static→dyn converters push through this
270
+ * too. Ownership of the item moves in. */
271
+ void scr_dyn_arr_push(ScrDyn *arr, ScrDyn *item) {
272
+ if (arr->v.arr.len == arr->v.arr.cap) {
273
+ size_t cap = arr->v.arr.cap ? arr->v.arr.cap * 2 : 4;
274
+ ScrDyn **items = realloc(arr->v.arr.items, cap * sizeof *items);
275
+ if (!items) scr_json_oom();
276
+ arr->v.arr.items = items;
277
+ arr->v.arr.cap = cap;
278
+ }
279
+ arr->v.arr.items[arr->v.arr.len++] = item; /* ownership moves in */
280
+ }
281
+
282
+ /* Takes ownership of key (malloc'd) and value. Duplicate keys: the LATER
283
+ * value wins (like JS JSON.parse) — the old value is released and the new
284
+ * key buffer freed (the surviving entry keeps its original, equal key). */
285
+ static void scr_dyn_obj_put(ScrDyn *obj, char *key, size_t key_len, ScrDyn *value) {
286
+ for (size_t i = 0; i < obj->v.obj.len; i++) {
287
+ ScrDynEntry *e = &obj->v.obj.entries[i];
288
+ if (e->key_len == key_len && memcmp(e->key, key, key_len) == 0) {
289
+ scr_dyn_release(e->value);
290
+ e->value = value;
291
+ free(key);
292
+ return;
293
+ }
294
+ }
295
+ if (obj->v.obj.len == obj->v.obj.cap) {
296
+ size_t cap = obj->v.obj.cap ? obj->v.obj.cap * 2 : 4;
297
+ ScrDynEntry *entries = realloc(obj->v.obj.entries, cap * sizeof *entries);
298
+ if (!entries) scr_json_oom();
299
+ obj->v.obj.entries = entries;
300
+ obj->v.obj.cap = cap;
301
+ }
302
+ ScrDynEntry *e = &obj->v.obj.entries[obj->v.obj.len++];
303
+ e->key = key;
304
+ e->key_len = key_len;
305
+ e->value = value;
306
+ }
307
+
308
+ /* ── DOM construction (compiler-emitted converters & overflow reads) ───── */
309
+
310
+ /* THE undefined value: one immortal node (rc == SIZE_MAX skips every
311
+ * retain/release and the freelists never see it). */
312
+ ScrDyn *scr_dyn_undefined(void) {
313
+ static ScrDyn undef = { SIZE_MAX, SCR_DYN_UNDEF, { false } };
314
+ return &undef;
315
+ }
316
+
317
+ ScrDyn *scr_dyn_new_null(void) { return scr_dyn_alloc(SCR_DYN_NULL); }
318
+
319
+ ScrDyn *scr_dyn_new_bool(bool b) {
320
+ ScrDyn *d = scr_dyn_alloc(SCR_DYN_BOOL);
321
+ d->v.b = b;
322
+ return d;
323
+ }
324
+
325
+ ScrDyn *scr_dyn_new_num(double n) {
326
+ ScrDyn *d = scr_dyn_alloc(SCR_DYN_NUM);
327
+ d->v.num = n;
328
+ return d;
329
+ }
330
+
331
+ ScrDyn *scr_dyn_new_str(ScrStr *s) {
332
+ ScrDyn *d = scr_dyn_alloc(SCR_DYN_STR);
333
+ d->v.str = scr_str_retain(s);
334
+ return d;
335
+ }
336
+
337
+ ScrDyn *scr_dyn_new_arr(void) { return scr_dyn_alloc(SCR_DYN_ARR); }
338
+ ScrDyn *scr_dyn_new_obj(void) { return scr_dyn_alloc(SCR_DYN_OBJ); }
339
+
340
+ ScrDyn *scr_dyn_new_bytes_copy(const ScrBytes *b) {
341
+ ScrDyn *d = scr_dyn_alloc(SCR_DYN_BYTES);
342
+ d->v.bytes = scr_bytes_copy(b); /* the static→dyn boundary copies */
343
+ return d;
344
+ }
345
+
346
+ ScrDyn *scr_dyn_new_buffer_copy(const ScrBytes *b) {
347
+ ScrDyn *d = scr_dyn_new_bytes_copy(b);
348
+ d->buffer = true;
349
+ return d;
350
+ }
351
+
352
+ ScrBytes *scr_dyn_bytes_copy_out(const ScrDyn *d) {
353
+ return scr_bytes_copy(d->v.bytes); /* extraction copies too (+1) */
354
+ }
355
+
356
+ /* ── the data-chunk encoding window (setEncoding) ─────────────────────
357
+ * Node's readable setEncoding turns 'data' payloads into strings. The
358
+ * delivery ABI carries bytes; the FIRING site (which owns the handle and
359
+ * its encoding flag) opens a window around the listener pass and the
360
+ * boxing helpers below answer string-flavored chunks inside it — the
361
+ * ambient-receiver pattern, one flag instead of a threaded parameter.
362
+ * Per-chunk utf8 decode: a multibyte character split across chunks does
363
+ * not re-join (Node's StringDecoder holds the partial byte); ASCII-clean
364
+ * bodies — the overwhelming test shape — are exact. */
365
+ static bool scr_dyn_chunk_utf8;
366
+ void scr_dyn_chunk_enc(bool utf8) { scr_dyn_chunk_utf8 = utf8; }
367
+
368
+ /* One 'data' payload as the DOM value the current window dictates:
369
+ * a Buffer-flavored bytes box, or a string inside a setEncoding window. */
370
+ ScrDyn *scr_dyn_new_chunk(const ScrBytes *b) {
371
+ if (scr_dyn_chunk_utf8) {
372
+ ScrStr *s = scr_str_new((const char *)b->data, b->len);
373
+ ScrDyn *d = scr_dyn_new_str(s);
374
+ scr_str_release(s);
375
+ return d;
376
+ }
377
+ return scr_dyn_new_buffer_copy(b);
378
+ }
379
+
380
+ /* A boxed static function value (the compiler's static→DOM converters).
381
+ * Ownership of the closure MOVES in; sig/name are static literals. */
382
+ ScrDyn *scr_dyn_new_func(ScrClosure *clo, ScrDynThunk thunk, uint32_t arity, const char *sig, const char *name) {
383
+ ScrDyn *d = scr_dyn_alloc(SCR_DYN_FUNC);
384
+ d->v.fn.clo = clo;
385
+ d->v.fn.thunk = thunk;
386
+ d->v.fn.sig = sig;
387
+ d->v.fn.name = name;
388
+ d->v.fn.arity = arity;
389
+ return d;
390
+ }
391
+
392
+ /* Calling a dyn value: kind check (Node's "<what> is not a function"
393
+ * TypeError, catchable), then the boxed thunk — per-argument validation
394
+ * into the closure's declared parameter types lives THERE (the thunk is
395
+ * compiled per signature). Args borrowed; result owned (+1) or NULL with
396
+ * the exception pending. */
397
+ ScrDyn *scr_dyn_call(const ScrDyn *d, ScrDyn *const *args, size_t argc, const char *what) {
398
+ if (d->kind != SCR_DYN_FUNC) {
399
+ ScrJsonBuf b;
400
+ scr_jb_init(&b);
401
+ scr_jb_puts(&b, what);
402
+ scr_jb_puts(&b, " is not a function");
403
+ scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&b));
404
+ return NULL;
405
+ }
406
+ return d->v.fn.thunk(d->v.fn.clo, args, argc);
407
+ }
408
+
409
+ /* ── native handles in the DOM (SCR_DYN_HANDLE) ───────────────────────
410
+ * Per-tag ops stamped by the owning units at main() (scr_http_dyn_install
411
+ * / scr_net_dyn_install — the scr_net_install hook story), so this
412
+ * always-linked core never references gated units. A missing tag at use
413
+ * is an internal error: emitted programs install a unit's ops whenever
414
+ * they can box its handles. */
415
+ static const ScrDynHandleOps *scr_dynh_ops[SCR_DYNH_COUNT];
416
+
417
+ void scr_dyn_handle_install(ScrDynHandleTag tag, const ScrDynHandleOps *ops) {
418
+ scr_dynh_ops[tag] = ops;
419
+ }
420
+
421
+ static const ScrDynHandleOps *scr_dyn_handle_ops(ScrDynHandleTag tag) {
422
+ const ScrDynHandleOps *ops = scr_dynh_ops[tag];
423
+ if (!ops) {
424
+ fputs("scriptc: internal error: dyn handle ops not installed\n", stderr);
425
+ abort();
426
+ }
427
+ return ops;
428
+ }
429
+
430
+ /* The class display name for error texts ("IncomingMessage"); safe on
431
+ * uninstalled tags (error paths render before anyone dispatches). */
432
+ const char *scr_dyn_handle_cls(const ScrDyn *d) {
433
+ const ScrDynHandleOps *ops = scr_dynh_ops[d->v.handle.tag];
434
+ return ops ? ops->cls : "object";
435
+ }
436
+
437
+ const ScrDynHandleOps *scr_dyn_handle_ops_of(const ScrDyn *d) {
438
+ return scr_dyn_handle_ops(d->v.handle.tag);
439
+ }
440
+
441
+ static void scr_dyn_handle_release(void *h, ScrDynHandleTag tag) {
442
+ scr_dyn_handle_ops(tag)->release(h);
443
+ }
444
+
445
+ ScrDyn *scr_dyn_new_handle(void *h, ScrDynHandleTag tag) {
446
+ ScrDyn *d = scr_dyn_alloc(SCR_DYN_HANDLE);
447
+ d->v.handle.ptr = scr_dyn_handle_ops(tag)->retain(h);
448
+ d->v.handle.tag = tag;
449
+ return d;
450
+ }
451
+
452
+ /* The gated promise boxes (scr_async_dyn.c) build through this thin
453
+ * allocator view; the freelist stays private, and the release arm's
454
+ * scr_promise_release edge installs HERE — a promise-free link never
455
+ * references the fiber machinery (the unit-test subset links). */
456
+ void (*scr_dyn_promise_release_fn)(ScrPromise *p) = NULL;
457
+
458
+ ScrDyn *scr_dyn_alloc_promise(void (*release_fn)(ScrPromise *p)) {
459
+ scr_dyn_promise_release_fn = release_fn;
460
+ return scr_dyn_alloc(SCR_DYN_PROMISE);
461
+ }
462
+
463
+ void *scr_dyn_handle_unbox(const ScrDyn *d, ScrDynHandleTag tag, const ScrDynPath *path, const char *want) {
464
+ if (d->kind != SCR_DYN_HANDLE || d->v.handle.tag != tag) {
465
+ scr_dyn_check_fail(path, want, d);
466
+ return NULL;
467
+ }
468
+ return scr_dyn_handle_ops(tag)->retain(d->v.handle.ptr);
469
+ }
470
+
471
+ ScrDyn *scr_dyn_handle_key_get(const ScrDyn *d, const ScrStr *k) {
472
+ const ScrDynHandleOps *ops = scr_dyn_handle_ops(d->v.handle.tag);
473
+ ScrDyn *r = ops->get(d->v.handle.ptr, k->data, k->len);
474
+ if (scr_exc_pending()) {
475
+ scr_dyn_release(r);
476
+ return NULL;
477
+ }
478
+ /* Unmodeled names answer undefined — the DOM's own-property stance
479
+ * (real-but-unmodeled members fence loudly inside ops->get instead;
480
+ * SEMANTICS.md documents the remainder). */
481
+ return r ? r : scr_dyn_retain(scr_dyn_undefined());
482
+ }
483
+
484
+ /* ── the ambient receiver (scr_runtime.h's design note) ───────────────
485
+ * A strictly nested push/pop stack: firing sites bind the owner around
486
+ * each listener call, dyn OBJ method dispatch binds the object, and the
487
+ * emitted dyn.this read answers the innermost binding. Handle entries
488
+ * are BORROWED (the firing site retains the owner across the call);
489
+ * dyn entries are retained for the window. */
490
+ typedef struct {
491
+ void *ptr; /* handle entry (borrowed); NULL when dv or undefined */
492
+ ScrDynHandleTag tag;
493
+ ScrDyn *dv; /* dyn entry (+1); NULL for handle/undefined entries */
494
+ } ScrDynThisEnt;
495
+
496
+ static ScrDynThisEnt *scr_dyn_this_stack;
497
+ static size_t scr_dyn_this_n, scr_dyn_this_cap;
498
+
499
+ static ScrDynThisEnt *scr_dyn_this_grow(void) {
500
+ if (scr_dyn_this_n == scr_dyn_this_cap) {
501
+ size_t cap = scr_dyn_this_cap ? scr_dyn_this_cap * 2 : 8;
502
+ ScrDynThisEnt *s = realloc(scr_dyn_this_stack, cap * sizeof *s);
503
+ if (!s) {
504
+ fputs("scriptc: out of memory\n", stderr);
505
+ abort();
506
+ }
507
+ scr_dyn_this_stack = s;
508
+ scr_dyn_this_cap = cap;
509
+ }
510
+ return &scr_dyn_this_stack[scr_dyn_this_n++];
511
+ }
512
+
513
+ void scr_dyn_this_push(void *h, ScrDynHandleTag tag) {
514
+ ScrDynThisEnt *e = scr_dyn_this_grow();
515
+ e->ptr = h;
516
+ e->tag = tag;
517
+ e->dv = NULL;
518
+ }
519
+
520
+ void scr_dyn_this_push_dyn(const ScrDyn *v) {
521
+ ScrDynThisEnt *e = scr_dyn_this_grow();
522
+ e->ptr = NULL;
523
+ e->tag = 0;
524
+ e->dv = scr_dyn_retain((ScrDyn *)v);
525
+ }
526
+
527
+ void scr_dyn_this_pop(void) {
528
+ if (scr_dyn_this_n == 0) {
529
+ fputs("scriptc: internal error: receiver stack underflow\n", stderr);
530
+ abort();
531
+ }
532
+ ScrDynThisEnt *e = &scr_dyn_this_stack[--scr_dyn_this_n];
533
+ if (e->dv) scr_dyn_release(e->dv);
534
+ }
535
+
536
+ ScrDyn *scr_dyn_this_get(void) {
537
+ if (scr_dyn_this_n > 0) {
538
+ ScrDynThisEnt *e = &scr_dyn_this_stack[scr_dyn_this_n - 1];
539
+ if (e->dv) return scr_dyn_retain(e->dv);
540
+ /* An uninstalled tag never binds: a unit that fires without its dyn
541
+ * half (no boxes can exist there) keeps the undefined answer. */
542
+ if (e->ptr && scr_dynh_ops[e->tag]) return scr_dyn_new_handle(e->ptr, e->tag);
543
+ }
544
+ return scr_dyn_retain(scr_dyn_undefined());
545
+ }
546
+
547
+ /* The keyed-write arm (see scr_dyn_key_set): modeled setters land on the
548
+ * handle; everything else fences loudly — Node would take a silent
549
+ * expando, but expandos per BOX would break handle identity. */
550
+ static void scr_dyn_handle_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) {
551
+ const ScrDynHandleOps *ops = scr_dyn_handle_ops(recv->v.handle.tag);
552
+ if (ops->set(recv->v.handle.ptr, key->data, key->len, value)) return;
553
+ ScrJsonBuf b;
554
+ scr_jb_init(&b);
555
+ scr_jb_puts(&b, "setting '");
556
+ for (size_t i = 0; i < key->len; i++) scr_jb_putc(&b, key->data[i]);
557
+ scr_jb_puts(&b, "' on a dynamic ");
558
+ scr_jb_puts(&b, ops->cls);
559
+ scr_jb_puts(&b, " is not supported yet");
560
+ scr_throw_error(SCR_ERR_ERROR, scr_jb_finish(&b));
561
+ }
562
+
563
+ /* Public obj insertion: COPIES the key bytes (the internal put takes a
564
+ * malloc'd buffer), owns the value, later duplicate keys win. */
565
+ void scr_dyn_obj_set(ScrDyn *obj, const char *key, size_t key_len, ScrDyn *value) {
566
+ char *copy = malloc(key_len + 1);
567
+ if (!copy) scr_json_oom();
568
+ memcpy(copy, key, key_len);
569
+ copy[key_len] = '\0';
570
+ scr_dyn_obj_put(obj, copy, key_len, value);
571
+ }
572
+
573
+ /* ToBoolean over a DOM value (`v || dflt`, `if (v)` on a dyn operand):
574
+ * bool by value; number falsy exactly for 0, -0, and NaN; string falsy
575
+ * exactly when empty; obj/arr/bytes/func always true; undefined and null
576
+ * always false — JS-exact for every kind. Borrowed; never throws. */
577
+ bool scr_dyn_truthy(const ScrDyn *d) {
578
+ switch (d->kind) {
579
+ case SCR_DYN_BOOL: return d->v.b;
580
+ case SCR_DYN_NUM: return d->v.num == d->v.num && d->v.num != 0;
581
+ case SCR_DYN_STR: return d->v.str->len != 0;
582
+ case SCR_DYN_OBJ:
583
+ case SCR_DYN_ARR:
584
+ case SCR_DYN_BYTES:
585
+ case SCR_DYN_FUNC:
586
+ case SCR_DYN_HANDLE:
587
+ case SCR_DYN_PROMISE: return true;
588
+ default: return false; /* undefined, null */
589
+ }
590
+ }
591
+
592
+ /* Bare `typeof v` on a dyn value: the DOM kind's JS answer (+1 string).
593
+ * null answers "object" — JS's oldest wart, preserved. */
594
+ ScrStr *scr_dyn_typeof(const ScrDyn *d) {
595
+ const char *s;
596
+ switch (d->kind) {
597
+ case SCR_DYN_UNDEF: s = "undefined"; break;
598
+ case SCR_DYN_NULL:
599
+ case SCR_DYN_OBJ:
600
+ case SCR_DYN_ARR:
601
+ case SCR_DYN_BYTES:
602
+ case SCR_DYN_HANDLE:
603
+ case SCR_DYN_PROMISE: s = "object"; break;
604
+ case SCR_DYN_BOOL: s = "boolean"; break;
605
+ case SCR_DYN_NUM: s = "number"; break;
606
+ case SCR_DYN_STR: s = "string"; break;
607
+ case SCR_DYN_FUNC: s = "function"; break;
608
+ default: s = "undefined"; break;
609
+ }
610
+ return scr_str_new(s, strlen(s));
611
+ }
612
+
613
+ /* ── JSON.stringify over a DOM value (util.format's %j) ───────────────
614
+ * The RUNTIME walk the type-directed serializers deliberately avoid for
615
+ * static values — a dyn value has no static type, so the DOM's own kinds
616
+ * drive it, JS-exactly: insertion-ordered objects with undefined/function
617
+ * members OMITTED, arrays rendering those as null, Buffer's toJSON shape
618
+ * ({"type":"Buffer","data":[...]}), shortest-roundtrip numbers, escaped
619
+ * strings. HANDLE values fence loudly (Node walks own enumerable props
620
+ * this runtime does not model). Returns false when the VALUE ITSELF is
621
+ * absent under stringify (root undefined/function — %j prints
622
+ * "undefined" there, Node's tryStringify tail). */
623
+ static bool scr_dyn_json_write(ScrJsonBuf *b, const ScrDyn *d) {
624
+ switch (d->kind) {
625
+ case SCR_DYN_UNDEF:
626
+ case SCR_DYN_FUNC:
627
+ return false;
628
+ case SCR_DYN_NULL: scr_jb_puts(b, "null"); return true;
629
+ case SCR_DYN_BOOL: scr_jb_puts(b, d->v.b ? "true" : "false"); return true;
630
+ case SCR_DYN_NUM: scr_jb_put_f64(b, d->v.num); return true;
631
+ case SCR_DYN_STR: scr_jb_put_json_str(b, d->v.str); return true;
632
+ case SCR_DYN_ARR: {
633
+ scr_jb_putc(b, '[');
634
+ for (size_t i = 0; i < d->v.arr.len; i++) {
635
+ if (i > 0) scr_jb_putc(b, ',');
636
+ if (!scr_dyn_json_write(b, d->v.arr.items[i])) scr_jb_puts(b, "null");
637
+ }
638
+ scr_jb_putc(b, ']');
639
+ return true;
640
+ }
641
+ case SCR_DYN_OBJ: {
642
+ scr_jb_putc(b, '{');
643
+ bool first = true;
644
+ for (size_t i = 0; i < d->v.obj.len; i++) {
645
+ ScrJsonBuf probe;
646
+ scr_jb_init(&probe);
647
+ if (!scr_dyn_json_write(&probe, d->v.obj.entries[i].value)) {
648
+ scr_jb_dispose(&probe);
649
+ continue; /* undefined/function members drop, like Node */
650
+ }
651
+ if (!first) scr_jb_putc(b, ',');
652
+ first = false;
653
+ ScrStr *k = scr_str_new(d->v.obj.entries[i].key, d->v.obj.entries[i].key_len);
654
+ scr_jb_put_json_str(b, k);
655
+ scr_str_release(k);
656
+ scr_jb_putc(b, ':');
657
+ ScrStr *body = scr_jb_finish(&probe);
658
+ scr_jb_write(b, body->data, body->len);
659
+ scr_str_release(body);
660
+ }
661
+ scr_jb_putc(b, '}');
662
+ return true;
663
+ }
664
+ case SCR_DYN_BYTES: {
665
+ /* Buffer/typed-array toJSON — Node's {"type":"Buffer","data":[...]}
666
+ * for the Buffer flavor; a plain Uint8Array stringifies index-keyed
667
+ * ({"0":1,...}), also Node. */
668
+ const ScrBytes *bytes = d->v.bytes;
669
+ if (d->buffer) scr_jb_puts(b, "{\"type\":\"Buffer\",\"data\":[");
670
+ else scr_jb_putc(b, '{');
671
+ for (size_t i = 0; i < bytes->len; i++) {
672
+ if (i > 0) scr_jb_putc(b, ',');
673
+ if (!d->buffer) {
674
+ char idx[32];
675
+ int n = snprintf(idx, sizeof idx, "\"%zu\":", i);
676
+ scr_jb_write(b, idx, (size_t)n);
677
+ }
678
+ scr_jb_put_f64(b, scr_bytes_get(bytes, (double)i));
679
+ }
680
+ scr_jb_puts(b, d->buffer ? "]}" : "}");
681
+ return true;
682
+ }
683
+ case SCR_DYN_PROMISE:
684
+ /* No own enumerable properties — Node stringifies a promise as {}. */
685
+ scr_jb_puts(b, "{}");
686
+ return true;
687
+ case SCR_DYN_HANDLE:
688
+ default: {
689
+ const char *msg = "JSON.stringify of a runtime handle is not supported yet";
690
+ scr_throw_error_msg(SCR_ERR_ERROR, msg, strlen(msg));
691
+ return true; /* pending exception; caller checks */
692
+ }
693
+ }
694
+ }
695
+
696
+ /* util.format's %j argument (+1): the stringify text, "undefined" for a
697
+ * root the stringify drops, or NULL with a pending exception (a handle
698
+ * inside the tree). */
699
+ ScrStr *scr_dyn_format_j(const ScrDyn *d) {
700
+ ScrJsonBuf b;
701
+ scr_jb_init(&b);
702
+ bool present = scr_dyn_json_write(&b, d);
703
+ if (scr_exc_pending()) {
704
+ scr_jb_dispose(&b);
705
+ return NULL;
706
+ }
707
+ if (!present) {
708
+ scr_jb_dispose(&b);
709
+ return scr_str_new("undefined", 9);
710
+ }
711
+ return scr_jb_finish(&b);
712
+ }
713
+
714
+ /* An %Error instance as a DOM object ({name, message[, code]}) — the
715
+ * checked-dynamic boundary's error shape (the exception-snapshot
716
+ * convention emit-walkers uses). Borrows e; +1 result.
717
+ *
718
+ * IDENTITY-CACHED: one error instance boxes to ONE DOM node, however many
719
+ * times it crosses (Node passes the error OBJECT through, so `found.error
720
+ * === thrown` and re-crossings compare reference-equal — the tracing
721
+ * suite's shape). The cache retains both sides for the process (like the
722
+ * dc registry; released atexit before the RC audit) and the node
723
+ * SNAPSHOTS name/message/code at first crossing — a later mutation of the
724
+ * error is invisible through it (SEMANTICS.md). Linear scan: error
725
+ * crossings are test/reporting paths, not hot loops. */
726
+ typedef struct {
727
+ ScrError *err; /* retained (pins the address — no pointer reuse) */
728
+ ScrDyn *dyn; /* retained */
729
+ } ScrErrDynEnt;
730
+
731
+ static ScrErrDynEnt *scr_errdyn_cache = NULL;
732
+ static size_t scr_errdyn_n = 0, scr_errdyn_cap = 0;
733
+ static bool scr_errdyn_teardown_registered = false;
734
+
735
+ static void scr_errdyn_teardown(void) {
736
+ for (size_t i = 0; i < scr_errdyn_n; i++) {
737
+ scr_error_release(scr_errdyn_cache[i].err);
738
+ scr_dyn_release(scr_errdyn_cache[i].dyn);
739
+ }
740
+ free(scr_errdyn_cache);
741
+ scr_errdyn_cache = NULL;
742
+ scr_errdyn_n = scr_errdyn_cap = 0;
743
+ }
744
+
745
+ ScrDyn *scr_dyn_from_error(const ScrError *e) {
746
+ for (size_t i = 0; i < scr_errdyn_n; i++) {
747
+ if (scr_errdyn_cache[i].err == e) return scr_dyn_retain(scr_errdyn_cache[i].dyn);
748
+ }
749
+ ScrDyn *d = scr_dyn_new_obj();
750
+ scr_dyn_obj_set(d, "%error", 6, scr_dyn_new_bool(true)); /* the DOM's error marker */
751
+ scr_dyn_obj_set(d, "name", 4, scr_dyn_new_str(e->name));
752
+ scr_dyn_obj_set(d, "message", 7, scr_dyn_new_str(e->message));
753
+ if (e->code) scr_dyn_obj_set(d, "code", 4, scr_dyn_new_str(e->code));
754
+ /* DOMException: `code` is the WebIDL legacy NUMBER (never the errno
755
+ * string slot), and the options form's cause crosses as itself. */
756
+ if (e->vt == &scr_error_vts[SCR_ERR_DOMEX]) {
757
+ scr_dyn_obj_set(d, "code", 4, scr_dyn_new_num(scr_domex_code(( ScrError *)e)));
758
+ if (scr_domex_has_cause((ScrError *)e)) {
759
+ scr_dyn_obj_set(d, "cause", 5, scr_domex_cause((ScrError *)e));
760
+ }
761
+ }
762
+ if (scr_errdyn_n == scr_errdyn_cap) {
763
+ scr_errdyn_cap = scr_errdyn_cap ? scr_errdyn_cap * 2 : 8;
764
+ scr_errdyn_cache = realloc(scr_errdyn_cache, scr_errdyn_cap * sizeof *scr_errdyn_cache);
765
+ if (!scr_errdyn_cache) {
766
+ fputs("scriptc: out of memory\n", stderr);
767
+ abort();
768
+ }
769
+ }
770
+ if (!scr_errdyn_teardown_registered) {
771
+ scr_errdyn_teardown_registered = true;
772
+ atexit(scr_errdyn_teardown);
773
+ }
774
+ scr_errdyn_cache[scr_errdyn_n].err = scr_error_retain((ScrError *)e);
775
+ scr_errdyn_cache[scr_errdyn_n].dyn = scr_dyn_retain(d);
776
+ scr_errdyn_n++;
777
+ return d;
778
+ }
779
+
780
+ /* The %Error EXTRACTION (dynCheck of `u as Error` / an instanceof-Error
781
+ * narrow, and the dyn-boxed thunk's Error-typed parameters): the REVERSE
782
+ * of scr_dyn_from_error, riding the same identity cache — a DOM error
783
+ * that came from a runtime ScrError answers THAT instance (+1), so an
784
+ * error crossing out and back compares reference-equal (the tracing
785
+ * suite's shape); an alien %error object rebuilds a runtime error from
786
+ * its name/message/code (the vtable kind resolves from the name so a
787
+ * later `instanceof TypeError` still answers) and ENTERS the cache, so
788
+ * its next boxing answers the same DOM node. The DOM node is borrowed. */
789
+ ScrError *scr_error_from_dyn(const ScrDyn *d) {
790
+ ScrError *hit = scr_errdyn_err_of(d);
791
+ if (hit) return hit;
792
+ const ScrDyn *en = scr_dyn_obj_get(d, "name", 4);
793
+ const ScrDyn *em = scr_dyn_obj_get(d, "message", 7);
794
+ const ScrDyn *ec = scr_dyn_obj_get(d, "code", 4);
795
+ int k = SCR_ERR_ERROR;
796
+ if (en && en->kind == SCR_DYN_STR) {
797
+ const ScrStr *n = en->v.str;
798
+ if (n->len == 9 && memcmp(n->data, "TypeError", 9) == 0) k = SCR_ERR_TYPE;
799
+ else if (n->len == 10 && memcmp(n->data, "RangeError", 10) == 0) k = SCR_ERR_RANGE;
800
+ else if (n->len == 11 && memcmp(n->data, "SyntaxError", 11) == 0) k = SCR_ERR_SYNTAX;
801
+ }
802
+ ScrError *e = scr_error_new(k, (em && em->kind == SCR_DYN_STR) ? em->v.str : NULL);
803
+ if (en && en->kind == SCR_DYN_STR) {
804
+ scr_str_release(e->name);
805
+ e->name = scr_str_retain(en->v.str);
806
+ }
807
+ if (ec && ec->kind == SCR_DYN_STR) e->code = scr_str_retain(ec->v.str);
808
+ scr_errdyn_put(e, (ScrDyn *)d);
809
+ return e;
810
+ }
811
+
812
+ /* Identity-cache access for the tracing/dc surfaces (the cache storage
813
+ * stays private here). Reverse lookup answers +1 or NULL; put retains
814
+ * both sides for the process. */
815
+ ScrError *scr_errdyn_err_of(const ScrDyn *d) {
816
+ for (size_t i = 0; i < scr_errdyn_n; i++) {
817
+ if (scr_errdyn_cache[i].dyn == d) return scr_error_retain(scr_errdyn_cache[i].err);
818
+ }
819
+ return NULL;
820
+ }
821
+
822
+ void scr_errdyn_put(ScrError *e, ScrDyn *d) {
823
+ if (scr_errdyn_n == scr_errdyn_cap) {
824
+ scr_errdyn_cap = scr_errdyn_cap ? scr_errdyn_cap * 2 : 8;
825
+ scr_errdyn_cache = realloc(scr_errdyn_cache, scr_errdyn_cap * sizeof *scr_errdyn_cache);
826
+ if (!scr_errdyn_cache) {
827
+ fputs("scriptc: out of memory\n", stderr);
828
+ abort();
829
+ }
830
+ }
831
+ if (!scr_errdyn_teardown_registered) {
832
+ scr_errdyn_teardown_registered = true;
833
+ atexit(scr_errdyn_teardown);
834
+ }
835
+ scr_errdyn_cache[scr_errdyn_n].err = scr_error_retain(e);
836
+ scr_errdyn_cache[scr_errdyn_n].dyn = scr_dyn_retain(d);
837
+ scr_errdyn_n++;
838
+ }
839
+
840
+
841
+ /* Receiver-kind-dispatched toString() on a checked-dynamic value (the
842
+ * dyn method surface — a stream's 'data'/for-await chunk is the common
843
+ * receiver): bytes decode per the encoding (Node's Buffer.toString,
844
+ * utf8 default), strings answer themselves, numbers/booleans format
845
+ * JS-exactly, arrays join their DOM elements with ',' (recursively via
846
+ * JS's Array.prototype.toString), plain objects answer
847
+ * "[object Object]", and undefined/null throw Node's TypeError. Borrows
848
+ * both; +1 result. */
849
+ ScrStr *scr_dyn_to_string(const ScrDyn *d, const ScrStr *enc) {
850
+ switch (d->kind) {
851
+ case SCR_DYN_BYTES:
852
+ if (d->buffer) return scr_bytes_to_str(d->v.bytes, enc);
853
+ /* Uint8Array.prototype.toString is Array's: elements joined */
854
+ {
855
+ ScrStr *out = scr_str_new("", 0);
856
+ for (size_t i = 0; i < d->v.bytes->len; i++) {
857
+ char n[16];
858
+ int w = snprintf(n, sizeof n, i > 0 ? ",%u" : "%u", (unsigned)d->v.bytes->data[i]);
859
+ ScrStr *piece = scr_str_new(n, (size_t)w);
860
+ ScrStr *joined = scr_str_concat(out, piece);
861
+ scr_str_release(out);
862
+ scr_str_release(piece);
863
+ out = joined;
864
+ }
865
+ return out;
866
+ }
867
+ case SCR_DYN_STR:
868
+ return scr_str_retain(d->v.str);
869
+ case SCR_DYN_NUM:
870
+ return scr_f64_to_scrstr(d->v.num);
871
+ case SCR_DYN_BOOL:
872
+ return d->v.b ? scr_str_new("true", 4) : scr_str_new("false", 5);
873
+ case SCR_DYN_OBJ:
874
+ return scr_str_new("[object Object]", 15);
875
+ case SCR_DYN_HANDLE:
876
+ /* IncomingMessage/ServerResponse/Socket inherit
877
+ * Object.prototype.toString — Node's String() answer exactly. */
878
+ return scr_str_new("[object Object]", 15);
879
+ case SCR_DYN_PROMISE:
880
+ /* Object.prototype.toString with the Promise @@toStringTag. */
881
+ return scr_str_new("[object Promise]", 16);
882
+ case SCR_DYN_ARR: {
883
+ ScrStr *out = scr_str_new("", 0);
884
+ for (size_t i = 0; i < d->v.arr.len; i++) {
885
+ if (i > 0) {
886
+ ScrStr *comma = scr_str_new(",", 1);
887
+ ScrStr *joined = scr_str_concat(out, comma);
888
+ scr_str_release(out);
889
+ scr_str_release(comma);
890
+ out = joined;
891
+ }
892
+ const ScrDyn *e = d->v.arr.items[i];
893
+ if (e->kind == SCR_DYN_UNDEF || e->kind == SCR_DYN_NULL) continue; /* JS join: empty */
894
+ ScrStr *piece = scr_dyn_to_string(e, enc);
895
+ ScrStr *joined = scr_str_concat(out, piece);
896
+ scr_str_release(out);
897
+ scr_str_release(piece);
898
+ out = joined;
899
+ }
900
+ return out;
901
+ }
902
+ case SCR_DYN_FUNC: {
903
+ static const char f[] = "function () { [native code] }";
904
+ return scr_str_new(f, sizeof f - 1);
905
+ }
906
+ case SCR_DYN_UNDEF:
907
+ case SCR_DYN_NULL:
908
+ default: {
909
+ static const char msg[] = "Cannot read properties of undefined (reading 'toString')";
910
+ static const char msgn[] = "Cannot read properties of null (reading 'toString')";
911
+ if (d->kind == SCR_DYN_NULL) scr_throw_error_msg(SCR_ERR_TYPE, msgn, sizeof msgn - 1);
912
+ else scr_throw_error_msg(SCR_ERR_TYPE, msg, sizeof msg - 1);
913
+ return scr_str_new("", 0);
914
+ }
915
+ }
916
+ }
917
+
918
+ /* JS String() over the DOM kind — the WebIDL ToString the web globals
919
+ * (atob/btoa, DOMException's name resolution) run on their arguments:
920
+ * the unit kinds RENDER ("null"/"undefined") where the .toString() twin
921
+ * above throws Node's property-read TypeError; every other kind matches
922
+ * scr_dyn_to_string. Borrows d; returns +1. */
923
+ ScrStr *scr_dyn_string_coerce(const ScrDyn *d) {
924
+ if (d->kind == SCR_DYN_NULL) return scr_str_new("null", 4);
925
+ if (d->kind == SCR_DYN_UNDEF) return scr_str_new("undefined", 9);
926
+ return scr_dyn_to_string(d, NULL);
927
+ }
928
+
929
+ /* The checked-dynamic keyed WRITE (`h.k = v` on a dyn receiver): OBJ sets
930
+ * the member (later writes win, insertion order — JS); undefined/null
931
+ * throws Node's "Cannot set properties of ..."; every other kind throws
932
+ * Node's strict-mode "Cannot create property ..." (sloppy mode would
933
+ * ignore silently — the loud choice, SEMANTICS.md). Receiver, key, and
934
+ * value are all BORROWED (the member retains the value in). */
935
+ static const char *scr_dyn_kind_name(const ScrDyn *d);
936
+ void scr_dyn_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) {
937
+ if (recv->kind == SCR_DYN_OBJ) {
938
+ scr_dyn_obj_set(recv, key->data, key->len, scr_dyn_retain(value));
939
+ return;
940
+ }
941
+ if (recv->kind == SCR_DYN_HANDLE) {
942
+ scr_dyn_handle_key_set(recv, key, value);
943
+ return;
944
+ }
945
+ ScrJsonBuf b;
946
+ scr_jb_init(&b);
947
+ if (recv->kind == SCR_DYN_UNDEF || recv->kind == SCR_DYN_NULL) {
948
+ scr_jb_puts(&b, "Cannot set properties of ");
949
+ scr_jb_puts(&b, recv->kind == SCR_DYN_UNDEF ? "undefined" : "null");
950
+ scr_jb_puts(&b, " (setting '");
951
+ for (size_t i = 0; i < key->len; i++) scr_jb_putc(&b, key->data[i]);
952
+ scr_jb_puts(&b, "')");
953
+ } else {
954
+ scr_jb_puts(&b, "Cannot create property '");
955
+ for (size_t i = 0; i < key->len; i++) scr_jb_putc(&b, key->data[i]);
956
+ scr_jb_puts(&b, "' on ");
957
+ scr_jb_puts(&b, scr_dyn_kind_name(recv));
958
+ }
959
+ scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&b));
960
+ }
961
+
962
+ /* Node's JSON.stringify over a DOM: object members holding undefined DROP,
963
+ * array slots holding undefined print null. A bare undefined never arrives
964
+ * (the record serializer drops the entry first); print null defensively. */
965
+ void scr_jb_put_dyn(ScrJsonBuf *b, const ScrDyn *d) {
966
+ switch (d->kind) {
967
+ case SCR_DYN_NULL:
968
+ case SCR_DYN_UNDEF:
969
+ case SCR_DYN_FUNC: /* JSON.stringify: functions serialize like undefined */
970
+ scr_jb_puts(b, "null");
971
+ return;
972
+ case SCR_DYN_BOOL:
973
+ scr_jb_puts(b, d->v.b ? "true" : "false");
974
+ return;
975
+ case SCR_DYN_NUM:
976
+ scr_jb_put_f64(b, d->v.num);
977
+ return;
978
+ case SCR_DYN_STR:
979
+ scr_jb_put_json_str(b, d->v.str);
980
+ return;
981
+ case SCR_DYN_BYTES: {
982
+ /* Node's JSON.stringify over a typed array: the index-keyed object
983
+ * form — {"0":1,"1":2}. u8 payloads only reach the DOM today. */
984
+ scr_jb_putc(b, '{');
985
+ for (size_t i = 0; i < d->v.bytes->len; i++) {
986
+ if (i > 0) scr_jb_putc(b, ',');
987
+ char idx[32];
988
+ snprintf(idx, sizeof idx, "\"%zu\":%u", i, (unsigned)d->v.bytes->data[i]);
989
+ scr_jb_puts(b, idx);
990
+ }
991
+ scr_jb_putc(b, '}');
992
+ return;
993
+ }
994
+ case SCR_DYN_ARR:
995
+ scr_jb_putc(b, '[');
996
+ for (size_t i = 0; i < d->v.arr.len; i++) {
997
+ if (i > 0) scr_jb_putc(b, ',');
998
+ scr_jb_put_dyn(b, d->v.arr.items[i]);
999
+ }
1000
+ scr_jb_putc(b, ']');
1001
+ return;
1002
+ case SCR_DYN_PROMISE:
1003
+ /* No own enumerable properties — Node stringifies a promise as {}. */
1004
+ scr_jb_puts(b, "{}");
1005
+ return;
1006
+ case SCR_DYN_HANDLE: {
1007
+ /* Node's JSON.stringify over these classes throws the circular-
1008
+ * structure TypeError with a V8 path dump we cannot reproduce —
1009
+ * fence loudly instead of a silent-wrong shape (SEMANTICS.md). */
1010
+ ScrJsonBuf m;
1011
+ scr_jb_init(&m);
1012
+ scr_jb_puts(&m, "JSON.stringify of a dynamic ");
1013
+ scr_jb_puts(&m, scr_dyn_handle_cls(d));
1014
+ scr_jb_puts(&m, " is not supported yet");
1015
+ scr_throw_error(SCR_ERR_ERROR, scr_jb_finish(&m));
1016
+ scr_jb_puts(b, "null"); /* the buffer never surfaces: the pending throw wins */
1017
+ return;
1018
+ }
1019
+ case SCR_DYN_OBJ: {
1020
+ scr_jb_putc(b, '{');
1021
+ bool first = true;
1022
+ for (size_t i = 0; i < d->v.obj.len; i++) {
1023
+ const ScrDynEntry *e = &d->v.obj.entries[i];
1024
+ if (e->value->kind == SCR_DYN_UNDEF || e->value->kind == SCR_DYN_FUNC) continue; /* dropped, like Node */
1025
+ if (!first) scr_jb_putc(b, ',');
1026
+ first = false;
1027
+ /* Keys escape exactly like string values (put_json_str quotes). */
1028
+ ScrStr *k = scr_str_new(e->key, e->key_len);
1029
+ scr_jb_put_json_str(b, k);
1030
+ scr_str_release(k);
1031
+ scr_jb_putc(b, ':');
1032
+ scr_jb_put_dyn(b, e->value);
1033
+ }
1034
+ scr_jb_putc(b, '}');
1035
+ return;
1036
+ }
1037
+ }
1038
+ fputs("scriptc: internal error: invalid dyn kind\n", stderr);
1039
+ abort();
1040
+ }
1041
+
1042
+ /* ── dynCheck failure path ─────────────────────────────────────────────── */
1043
+
1044
+ static void scr_dyn_path_render(ScrJsonBuf *b, const ScrDynPath *p) {
1045
+ if (!p) {
1046
+ scr_jb_putc(b, '$');
1047
+ return;
1048
+ }
1049
+ scr_dyn_path_render(b, p->parent);
1050
+ if (p->key) {
1051
+ scr_jb_putc(b, '.');
1052
+ scr_jb_puts(b, p->key);
1053
+ } else {
1054
+ char idx[32];
1055
+ snprintf(idx, sizeof idx, "[%zu]", p->index);
1056
+ scr_jb_puts(b, idx);
1057
+ }
1058
+ }
1059
+
1060
+ static const char *scr_dyn_kind_name(const ScrDyn *d) {
1061
+ if (!d) return "undefined"; /* a missing object member */
1062
+ switch (d->kind) {
1063
+ case SCR_DYN_NULL: return "null";
1064
+ case SCR_DYN_BOOL: return "boolean";
1065
+ case SCR_DYN_NUM: return "number";
1066
+ case SCR_DYN_STR: return "string";
1067
+ case SCR_DYN_ARR: return "array";
1068
+ case SCR_DYN_OBJ: return "object";
1069
+ case SCR_DYN_UNDEF: return "undefined";
1070
+ case SCR_DYN_BYTES: return "Uint8Array";
1071
+ case SCR_DYN_FUNC: return "function";
1072
+ case SCR_DYN_HANDLE: return scr_dyn_handle_cls(d); /* "got IncomingMessage" */
1073
+ case SCR_DYN_PROMISE: return "Promise"; /* "got Promise" */
1074
+ }
1075
+ return "unknown";
1076
+ }
1077
+
1078
+ void scr_dyn_check_fail(const ScrDynPath *path, const char *want, const ScrDyn *got) {
1079
+ ScrJsonBuf b;
1080
+ scr_jb_init(&b);
1081
+ scr_jb_puts(&b, "expected ");
1082
+ scr_jb_puts(&b, want);
1083
+ scr_jb_puts(&b, " at ");
1084
+ scr_dyn_path_render(&b, path);
1085
+ scr_jb_puts(&b, ", got ");
1086
+ scr_jb_puts(&b, scr_dyn_kind_name(got));
1087
+ /* A real TypeError instance: catch bindings narrow it with instanceof
1088
+ * and read the path off e.message; the uncaught line ("Uncaught
1089
+ * TypeError: expected ...") is byte-identical to the old string form. */
1090
+ scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&b));
1091
+ }
1092
+
1093
+ /* ── parser ────────────────────────────────────────────────────────────── */
1094
+
1095
+ #define SCR_JSON_MAX_DEPTH 1000
1096
+
1097
+ typedef struct {
1098
+ const char *s;
1099
+ size_t len;
1100
+ size_t pos;
1101
+ int depth;
1102
+ } ScrJsonP;
1103
+
1104
+ static void scr_json_throw(const char *msg) {
1105
+ scr_throw_error_msg(SCR_ERR_SYNTAX, msg, strlen(msg));
1106
+ }
1107
+
1108
+ static void scr_json_throw_pos(const char *what, size_t pos) {
1109
+ char buf[128];
1110
+ int n = snprintf(buf, sizeof buf, "%s in JSON at position %zu", what, pos);
1111
+ scr_throw_error_msg(SCR_ERR_SYNTAX, buf, (size_t)n);
1112
+ }
1113
+
1114
+ /* V8-flavored bad-token message with a short snippet of the input around
1115
+ * the offending character. Approximate fidelity (documented). */
1116
+ static void scr_json_throw_token(const ScrJsonP *p) {
1117
+ size_t start = p->pos > 8 ? p->pos - 8 : 0;
1118
+ size_t take = p->len - start < 16 ? p->len - start : 16;
1119
+ char buf[192];
1120
+ int n = snprintf(buf, sizeof buf, "Unexpected token '%c', %s\"%.*s\"%s is not valid JSON",
1121
+ p->s[p->pos], start > 0 ? "..." : "", (int)take, p->s + start,
1122
+ start + take < p->len ? "..." : "");
1123
+ scr_throw_error_msg(SCR_ERR_SYNTAX, buf, (size_t)n);
1124
+ }
1125
+
1126
+ static void scr_json_ws(ScrJsonP *p) {
1127
+ while (p->pos < p->len) {
1128
+ char c = p->s[p->pos];
1129
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') p->pos++;
1130
+ else break;
1131
+ }
1132
+ }
1133
+
1134
+ /* Append the UTF-8 encoding of cp (valid scalar values only — callers map
1135
+ * lone surrogates to U+FFFD first). */
1136
+ static void scr_json_put_cp(ScrJsonBuf *b, uint32_t cp) {
1137
+ if (cp < 0x80) {
1138
+ scr_jb_putc(b, (char)cp);
1139
+ } else if (cp < 0x800) {
1140
+ scr_jb_putc(b, (char)(0xC0 | (cp >> 6)));
1141
+ scr_jb_putc(b, (char)(0x80 | (cp & 0x3F)));
1142
+ } else if (cp < 0x10000) {
1143
+ scr_jb_putc(b, (char)(0xE0 | (cp >> 12)));
1144
+ scr_jb_putc(b, (char)(0x80 | ((cp >> 6) & 0x3F)));
1145
+ scr_jb_putc(b, (char)(0x80 | (cp & 0x3F)));
1146
+ } else {
1147
+ scr_jb_putc(b, (char)(0xF0 | (cp >> 18)));
1148
+ scr_jb_putc(b, (char)(0x80 | ((cp >> 12) & 0x3F)));
1149
+ scr_jb_putc(b, (char)(0x80 | ((cp >> 6) & 0x3F)));
1150
+ scr_jb_putc(b, (char)(0x80 | (cp & 0x3F)));
1151
+ }
1152
+ }
1153
+
1154
+ /* Four hex digits at pos, or -1 (throws). */
1155
+ static int32_t scr_json_hex4(ScrJsonP *p) {
1156
+ if (p->len - p->pos < 4) {
1157
+ scr_json_throw("Unexpected end of JSON input");
1158
+ return -1;
1159
+ }
1160
+ uint32_t v = 0;
1161
+ for (int i = 0; i < 4; i++) {
1162
+ char c = p->s[p->pos + (size_t)i];
1163
+ uint32_t digit;
1164
+ if (c >= '0' && c <= '9') digit = (uint32_t)(c - '0');
1165
+ else if (c >= 'a' && c <= 'f') digit = (uint32_t)(c - 'a' + 10);
1166
+ else if (c >= 'A' && c <= 'F') digit = (uint32_t)(c - 'A' + 10);
1167
+ else {
1168
+ scr_json_throw_pos("Bad Unicode escape", p->pos + (size_t)i);
1169
+ return -1;
1170
+ }
1171
+ v = v * 16 + digit;
1172
+ }
1173
+ p->pos += 4;
1174
+ return (int32_t)v;
1175
+ }
1176
+
1177
+ /* Fast scan of the string literal at p->pos (the opening quote): when it
1178
+ * contains no escapes, sets *span and *span_len to the raw bytes inside the
1179
+ * quotes, consumes the literal and returns 1. An escape returns 0 with
1180
+ * p->pos still at the opening quote (the slow path re-parses). A control
1181
+ * character or missing close quote throws (same message and position the
1182
+ * slow path would produce) and returns -1. */
1183
+ static int scr_json_string_span(ScrJsonP *p, const char **span,
1184
+ size_t *span_len) {
1185
+ size_t i = p->pos + 1;
1186
+ while (i < p->len) {
1187
+ unsigned char c = (unsigned char)p->s[i];
1188
+ if (c == '"') {
1189
+ *span = p->s + p->pos + 1;
1190
+ *span_len = i - (p->pos + 1);
1191
+ p->pos = i + 1;
1192
+ return 1;
1193
+ }
1194
+ if (c == '\\') return 0;
1195
+ if (c < 0x20) {
1196
+ scr_json_throw_pos("Bad control character in string literal", i);
1197
+ return -1;
1198
+ }
1199
+ i++;
1200
+ }
1201
+ scr_json_throw_pos("Unterminated string", p->pos);
1202
+ return -1;
1203
+ }
1204
+
1205
+ /* Slow path: parses the string literal at p->pos (the opening quote),
1206
+ * decoding escapes, into a +1 ScrStr. NULL on error (thrown). */
1207
+ static ScrStr *scr_json_string_slow(ScrJsonP *p) {
1208
+ size_t open = p->pos;
1209
+ p->pos++; /* opening quote */
1210
+ ScrJsonBuf b;
1211
+ scr_jb_init(&b);
1212
+ for (;;) {
1213
+ if (p->pos >= p->len) {
1214
+ scr_jb_dispose(&b);
1215
+ scr_json_throw_pos("Unterminated string", open);
1216
+ return NULL;
1217
+ }
1218
+ unsigned char c = (unsigned char)p->s[p->pos];
1219
+ if (c == '"') {
1220
+ p->pos++;
1221
+ return scr_jb_finish(&b);
1222
+ }
1223
+ if (c < 0x20) {
1224
+ scr_jb_dispose(&b);
1225
+ scr_json_throw_pos("Bad control character in string literal", p->pos);
1226
+ return NULL;
1227
+ }
1228
+ if (c != '\\') {
1229
+ scr_jb_putc(&b, (char)c); /* raw UTF-8 passes through */
1230
+ p->pos++;
1231
+ continue;
1232
+ }
1233
+ p->pos++; /* backslash */
1234
+ if (p->pos >= p->len) {
1235
+ scr_jb_dispose(&b);
1236
+ scr_json_throw("Unexpected end of JSON input");
1237
+ return NULL;
1238
+ }
1239
+ char e = p->s[p->pos];
1240
+ switch (e) {
1241
+ case '"': scr_jb_putc(&b, '"'); p->pos++; break;
1242
+ case '\\': scr_jb_putc(&b, '\\'); p->pos++; break;
1243
+ case '/': scr_jb_putc(&b, '/'); p->pos++; break;
1244
+ case 'b': scr_jb_putc(&b, '\b'); p->pos++; break;
1245
+ case 'f': scr_jb_putc(&b, '\f'); p->pos++; break;
1246
+ case 'n': scr_jb_putc(&b, '\n'); p->pos++; break;
1247
+ case 'r': scr_jb_putc(&b, '\r'); p->pos++; break;
1248
+ case 't': scr_jb_putc(&b, '\t'); p->pos++; break;
1249
+ case 'u': {
1250
+ p->pos++;
1251
+ int32_t cp = scr_json_hex4(p);
1252
+ if (cp < 0) {
1253
+ scr_jb_dispose(&b);
1254
+ return NULL;
1255
+ }
1256
+ if (cp >= 0xD800 && cp <= 0xDBFF) {
1257
+ /* High surrogate: combine with a following \uDC00-\uDFFF; a lone
1258
+ * surrogate becomes U+FFFD (house policy: strings stay well-formed
1259
+ * UTF-8 — JS would keep the lone surrogate; see SEMANTICS.md). */
1260
+ if (p->len - p->pos >= 2 && p->s[p->pos] == '\\' && p->s[p->pos + 1] == 'u') {
1261
+ size_t save = p->pos;
1262
+ p->pos += 2;
1263
+ int32_t lo = scr_json_hex4(p);
1264
+ if (lo < 0) {
1265
+ scr_jb_dispose(&b);
1266
+ return NULL;
1267
+ }
1268
+ if (lo >= 0xDC00 && lo <= 0xDFFF) {
1269
+ uint32_t combined =
1270
+ 0x10000 + (((uint32_t)cp - 0xD800) << 10) + ((uint32_t)lo - 0xDC00);
1271
+ scr_json_put_cp(&b, combined);
1272
+ break;
1273
+ }
1274
+ /* Not a low surrogate: emit U+FFFD, reparse the escape normally. */
1275
+ p->pos = save;
1276
+ scr_json_put_cp(&b, 0xFFFD);
1277
+ break;
1278
+ }
1279
+ scr_json_put_cp(&b, 0xFFFD);
1280
+ break;
1281
+ }
1282
+ if (cp >= 0xDC00 && cp <= 0xDFFF) {
1283
+ scr_json_put_cp(&b, 0xFFFD); /* lone low surrogate */
1284
+ break;
1285
+ }
1286
+ scr_json_put_cp(&b, (uint32_t)cp);
1287
+ break;
1288
+ }
1289
+ default:
1290
+ scr_jb_dispose(&b);
1291
+ scr_json_throw_pos("Bad escaped character", p->pos);
1292
+ return NULL;
1293
+ }
1294
+ }
1295
+ }
1296
+
1297
+ /* String literal at p->pos as a +1 ScrStr (span fast path, escapes via the
1298
+ * slow path). NULL on error (thrown). */
1299
+ static ScrStr *scr_json_string_scr(ScrJsonP *p) {
1300
+ const char *span;
1301
+ size_t span_len;
1302
+ int r = scr_json_string_span(p, &span, &span_len);
1303
+ if (r < 0) return NULL;
1304
+ if (r > 0) return scr_str_new(span, span_len);
1305
+ return scr_json_string_slow(p);
1306
+ }
1307
+
1308
+ static ScrDyn *scr_json_value(ScrJsonP *p);
1309
+
1310
+ /* Exact powers of ten: 10^k is an exact double for k <= 22. */
1311
+ static const double scr_json_pow10[23] = {
1312
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
1313
+ 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
1314
+
1315
+ static ScrDyn *scr_json_number(ScrJsonP *p) {
1316
+ size_t start = p->pos;
1317
+ /* Grammar validation and value accumulation in one pass. Clinger's fast
1318
+ * path: with at most 15 significant digits the mantissa is exact in a
1319
+ * double, and scaling by an exact power of ten (|exp| <= 22) rounds
1320
+ * once — bit-identical to strtod. Everything else falls back. */
1321
+ uint64_t mant = 0;
1322
+ int ndig = 0; /* significant digits folded into mant */
1323
+ int exp10 = 0; /* decimal exponent (fraction shift + explicit exponent) */
1324
+ bool neg = false, precise = true;
1325
+ if (p->s[p->pos] == '-') {
1326
+ neg = true;
1327
+ p->pos++;
1328
+ if (p->pos >= p->len || p->s[p->pos] < '0' || p->s[p->pos] > '9') {
1329
+ scr_json_throw_pos("No number after minus sign", p->pos);
1330
+ return NULL;
1331
+ }
1332
+ }
1333
+ if (p->s[p->pos] == '0') {
1334
+ p->pos++;
1335
+ } else {
1336
+ while (p->pos < p->len && p->s[p->pos] >= '0' && p->s[p->pos] <= '9') {
1337
+ if (ndig < 15) {
1338
+ mant = mant * 10 + (uint64_t)(p->s[p->pos] - '0');
1339
+ ndig++;
1340
+ } else {
1341
+ precise = false;
1342
+ }
1343
+ p->pos++;
1344
+ }
1345
+ }
1346
+ if (p->pos < p->len && p->s[p->pos] == '.') {
1347
+ p->pos++;
1348
+ if (p->pos >= p->len || p->s[p->pos] < '0' || p->s[p->pos] > '9') {
1349
+ scr_json_throw_pos("Unterminated fractional number", p->pos);
1350
+ return NULL;
1351
+ }
1352
+ while (p->pos < p->len && p->s[p->pos] >= '0' && p->s[p->pos] <= '9') {
1353
+ unsigned digit = (unsigned)(p->s[p->pos] - '0');
1354
+ if (mant == 0 && digit == 0) {
1355
+ exp10--; /* leading fractional zeros scale without a digit */
1356
+ } else if (ndig < 15) {
1357
+ mant = mant * 10 + digit;
1358
+ ndig++;
1359
+ exp10--;
1360
+ } else {
1361
+ precise = false;
1362
+ }
1363
+ p->pos++;
1364
+ }
1365
+ }
1366
+ if (p->pos < p->len && (p->s[p->pos] == 'e' || p->s[p->pos] == 'E')) {
1367
+ p->pos++;
1368
+ bool eneg = false;
1369
+ if (p->pos < p->len && (p->s[p->pos] == '+' || p->s[p->pos] == '-')) {
1370
+ eneg = p->s[p->pos] == '-';
1371
+ p->pos++;
1372
+ }
1373
+ if (p->pos >= p->len || p->s[p->pos] < '0' || p->s[p->pos] > '9') {
1374
+ scr_json_throw_pos("Exponent part is missing a number", p->pos);
1375
+ return NULL;
1376
+ }
1377
+ int ev = 0;
1378
+ while (p->pos < p->len && p->s[p->pos] >= '0' && p->s[p->pos] <= '9') {
1379
+ if (ev < 100000) ev = ev * 10 + (p->s[p->pos] - '0');
1380
+ p->pos++;
1381
+ }
1382
+ exp10 += eneg ? -ev : ev;
1383
+ }
1384
+ ScrDyn *d = scr_dyn_alloc(SCR_DYN_NUM);
1385
+ if (precise && exp10 >= -22 && exp10 <= 22) {
1386
+ double v = (double)mant; /* exact: mant < 10^15 < 2^53 */
1387
+ if (exp10 > 0) v *= scr_json_pow10[exp10];
1388
+ else if (exp10 < 0) v /= scr_json_pow10[-exp10];
1389
+ d->v.num = neg ? -v : v;
1390
+ return d;
1391
+ }
1392
+ /* The validated span re-parses with strtod (correctly rounded, and the
1393
+ * grammar above is a strict subset of what strtod accepts). The ScrStr
1394
+ * data is NUL-terminated, and strtod stops at the first non-number char,
1395
+ * so parsing from `start` reads exactly the validated token. */
1396
+ d->v.num = strtod(p->s + start, NULL);
1397
+ return d;
1398
+ }
1399
+
1400
+ static bool scr_json_lit(ScrJsonP *p, const char *word, size_t n) {
1401
+ if (p->len - p->pos >= n && memcmp(p->s + p->pos, word, n) == 0) {
1402
+ p->pos += n;
1403
+ return true;
1404
+ }
1405
+ scr_json_throw_token(p);
1406
+ return false;
1407
+ }
1408
+
1409
+ static ScrDyn *scr_json_array(ScrJsonP *p) {
1410
+ p->pos++; /* '[' */
1411
+ ScrDyn *arr = scr_dyn_alloc(SCR_DYN_ARR);
1412
+ scr_json_ws(p);
1413
+ if (p->pos < p->len && p->s[p->pos] == ']') {
1414
+ p->pos++;
1415
+ return arr;
1416
+ }
1417
+ for (;;) {
1418
+ ScrDyn *item = scr_json_value(p);
1419
+ if (!item) {
1420
+ scr_dyn_release(arr);
1421
+ return NULL;
1422
+ }
1423
+ scr_dyn_arr_push(arr, item);
1424
+ scr_json_ws(p);
1425
+ if (p->pos >= p->len) {
1426
+ scr_dyn_release(arr);
1427
+ scr_json_throw("Unexpected end of JSON input");
1428
+ return NULL;
1429
+ }
1430
+ if (p->s[p->pos] == ',') {
1431
+ p->pos++;
1432
+ continue;
1433
+ }
1434
+ if (p->s[p->pos] == ']') {
1435
+ p->pos++;
1436
+ return arr;
1437
+ }
1438
+ scr_dyn_release(arr);
1439
+ scr_json_throw_pos("Expected ',' or ']' after array element", p->pos);
1440
+ return NULL;
1441
+ }
1442
+ }
1443
+
1444
+ static ScrDyn *scr_json_object(ScrJsonP *p) {
1445
+ p->pos++; /* '{' */
1446
+ ScrDyn *obj = scr_dyn_alloc(SCR_DYN_OBJ);
1447
+ scr_json_ws(p);
1448
+ if (p->pos < p->len && p->s[p->pos] == '}') {
1449
+ p->pos++;
1450
+ return obj;
1451
+ }
1452
+ for (;;) {
1453
+ scr_json_ws(p);
1454
+ if (p->pos >= p->len) {
1455
+ scr_dyn_release(obj);
1456
+ scr_json_throw("Unexpected end of JSON input");
1457
+ return NULL;
1458
+ }
1459
+ if (p->s[p->pos] != '"') {
1460
+ scr_dyn_release(obj);
1461
+ scr_json_throw_pos("Expected property name or '}'", p->pos);
1462
+ return NULL;
1463
+ }
1464
+ /* Key: fast span path (no escapes) copies straight into the malloc'd
1465
+ * key buffer; the slow path decodes into a ScrStr first. */
1466
+ size_t key_len = 0;
1467
+ char *key;
1468
+ {
1469
+ const char *span;
1470
+ size_t span_len;
1471
+ int r = scr_json_string_span(p, &span, &span_len);
1472
+ if (r < 0) {
1473
+ scr_dyn_release(obj);
1474
+ return NULL;
1475
+ }
1476
+ if (r > 0) {
1477
+ key = malloc(span_len + 1);
1478
+ if (!key) scr_json_oom();
1479
+ memcpy(key, span, span_len);
1480
+ key[span_len] = '\0';
1481
+ key_len = span_len;
1482
+ } else {
1483
+ ScrStr *ks = scr_json_string_slow(p);
1484
+ if (!ks) {
1485
+ scr_dyn_release(obj);
1486
+ return NULL;
1487
+ }
1488
+ key = malloc(ks->len + 1);
1489
+ if (!key) scr_json_oom();
1490
+ memcpy(key, ks->data, ks->len + 1);
1491
+ key_len = ks->len;
1492
+ scr_str_release(ks);
1493
+ }
1494
+ }
1495
+ scr_json_ws(p);
1496
+ if (p->pos >= p->len || p->s[p->pos] != ':') {
1497
+ free(key);
1498
+ scr_dyn_release(obj);
1499
+ if (p->pos >= p->len) scr_json_throw("Unexpected end of JSON input");
1500
+ else scr_json_throw_pos("Expected ':' after property name", p->pos);
1501
+ return NULL;
1502
+ }
1503
+ p->pos++; /* ':' */
1504
+ ScrDyn *value = scr_json_value(p);
1505
+ if (!value) {
1506
+ free(key);
1507
+ scr_dyn_release(obj);
1508
+ return NULL;
1509
+ }
1510
+ scr_dyn_obj_put(obj, key, key_len, value); /* later duplicate keys win */
1511
+ scr_json_ws(p);
1512
+ if (p->pos >= p->len) {
1513
+ scr_dyn_release(obj);
1514
+ scr_json_throw("Unexpected end of JSON input");
1515
+ return NULL;
1516
+ }
1517
+ if (p->s[p->pos] == ',') {
1518
+ p->pos++;
1519
+ continue;
1520
+ }
1521
+ if (p->s[p->pos] == '}') {
1522
+ p->pos++;
1523
+ return obj;
1524
+ }
1525
+ scr_dyn_release(obj);
1526
+ scr_json_throw_pos("Expected ',' or '}' after property value", p->pos);
1527
+ return NULL;
1528
+ }
1529
+ }
1530
+
1531
+ static ScrDyn *scr_json_value(ScrJsonP *p) {
1532
+ scr_json_ws(p);
1533
+ if (p->pos >= p->len) {
1534
+ scr_json_throw("Unexpected end of JSON input");
1535
+ return NULL;
1536
+ }
1537
+ char c = p->s[p->pos];
1538
+ if (c == '{' || c == '[') {
1539
+ if (++p->depth > SCR_JSON_MAX_DEPTH) {
1540
+ /* JS overflows the engine stack here (RangeError); a native recursive
1541
+ * descent must cap instead. Same message and kind, catchable. */
1542
+ const char msg[] = "Maximum call stack size exceeded";
1543
+ scr_throw_error_msg(SCR_ERR_RANGE, msg, sizeof msg - 1);
1544
+ return NULL;
1545
+ }
1546
+ ScrDyn *d = c == '{' ? scr_json_object(p) : scr_json_array(p);
1547
+ p->depth--;
1548
+ return d;
1549
+ }
1550
+ if (c == '"') {
1551
+ ScrStr *sv = scr_json_string_scr(p);
1552
+ if (!sv) return NULL;
1553
+ ScrDyn *d = scr_dyn_alloc(SCR_DYN_STR);
1554
+ d->v.str = sv;
1555
+ return d;
1556
+ }
1557
+ if (c == 't') {
1558
+ if (!scr_json_lit(p, "true", 4)) return NULL;
1559
+ ScrDyn *d = scr_dyn_alloc(SCR_DYN_BOOL);
1560
+ d->v.b = true;
1561
+ return d;
1562
+ }
1563
+ if (c == 'f') {
1564
+ if (!scr_json_lit(p, "false", 5)) return NULL;
1565
+ ScrDyn *d = scr_dyn_alloc(SCR_DYN_BOOL);
1566
+ d->v.b = false;
1567
+ return d;
1568
+ }
1569
+ if (c == 'n') {
1570
+ if (!scr_json_lit(p, "null", 4)) return NULL;
1571
+ return scr_dyn_alloc(SCR_DYN_NULL);
1572
+ }
1573
+ if (c == '-' || (c >= '0' && c <= '9')) return scr_json_number(p);
1574
+ scr_json_throw_token(p);
1575
+ return NULL;
1576
+ }
1577
+
1578
+ ScrDyn *scr_json_parse(ScrStr *text) {
1579
+ ScrJsonP p = { text->data, text->len, 0, 0 };
1580
+ scr_json_ws(&p);
1581
+ if (p.pos >= p.len) {
1582
+ scr_json_throw("Unexpected end of JSON input");
1583
+ return NULL;
1584
+ }
1585
+ ScrDyn *d = scr_json_value(&p);
1586
+ if (!d) return NULL;
1587
+ scr_json_ws(&p);
1588
+ if (p.pos < p.len) {
1589
+ scr_dyn_release(d);
1590
+ char buf[96];
1591
+ int n = snprintf(buf, sizeof buf,
1592
+ "Unexpected non-whitespace character after JSON at position %zu", p.pos);
1593
+ scr_throw_error_msg(SCR_ERR_SYNTAX, buf, (size_t)n);
1594
+ return NULL;
1595
+ }
1596
+ return d;
1597
+ }
1598
+
1599
+ /* Untyped RC adapters (box/promise/exception-cell currency). */
1600
+ void *scr_dyn_retain_v(void *d) { return scr_dyn_retain((ScrDyn *)d); }
1601
+ void scr_dyn_release_v(void *d) { scr_dyn_release((ScrDyn *)d); }
1602
+
1603
+ /* JS === over two DOM values: scalars by value (NaN false, ±0 equal via
1604
+ * C ==; strings bytewise), units by kind, everything reference-shaped by
1605
+ * node IDENTITY (the DOM's object identity). Never throws. */
1606
+ bool scr_dyn_strict_eq(const ScrDyn *a, const ScrDyn *b) {
1607
+ if (a->kind != b->kind) return false;
1608
+ switch (a->kind) {
1609
+ case SCR_DYN_UNDEF:
1610
+ case SCR_DYN_NULL: return true;
1611
+ case SCR_DYN_BOOL: return a->v.b == b->v.b;
1612
+ case SCR_DYN_NUM: return a->v.num == b->v.num;
1613
+ case SCR_DYN_STR:
1614
+ return a->v.str->len == b->v.str->len &&
1615
+ memcmp(a->v.str->data, b->v.str->data, a->v.str->len) == 0;
1616
+ case SCR_DYN_FUNC:
1617
+ /* The ScrDyn box is a boundary artifact — one closure crossing the
1618
+ * dyn boundary twice is still ONE JS function value, so identity
1619
+ * lives in the boxed closure, not the box. */
1620
+ return a == b || a->v.fn.clo == b->v.fn.clo;
1621
+ case SCR_DYN_HANDLE:
1622
+ /* Same story: identity is the HANDLE — one req boxed into two
1623
+ * listeners is still one JS object. */
1624
+ return a->v.handle.tag == b->v.handle.tag && a->v.handle.ptr == b->v.handle.ptr;
1625
+ case SCR_DYN_PROMISE:
1626
+ /* And the PROMISE: one promise crossing twice is one JS value. */
1627
+ return a->v.promise == b->v.promise;
1628
+ default: return a == b;
1629
+ }
1630
+ }
1631
+
1632
+ /* Keyed read on a FUNC node (see scr_runtime.h): own props first, then
1633
+ * the function-instance built-ins name/length. +1 or NULL. */
1634
+ ScrDyn *scr_dyn_fn_get(const ScrDyn *d, const char *key, size_t key_len) {
1635
+ if (d->v.fn.clo->props) {
1636
+ ScrDyn *table = (ScrDyn *)scr_box_get_ref(d->v.fn.clo->props); /* +1 */
1637
+ ScrDyn *m = table ? scr_dyn_obj_get(table, key, key_len) : NULL;
1638
+ ScrDyn *r = m ? scr_dyn_retain(m) : NULL;
1639
+ scr_dyn_release(table);
1640
+ if (r) return r;
1641
+ }
1642
+ if (key_len == 4 && memcmp(key, "name", 4) == 0) {
1643
+ const char *n = d->v.fn.name ? d->v.fn.name : "";
1644
+ ScrStr *s = scr_str_new(n, strlen(n));
1645
+ ScrDyn *r = scr_dyn_new_str(s); /* retains */
1646
+ scr_str_release(s);
1647
+ return r;
1648
+ }
1649
+ if (key_len == 6 && memcmp(key, "length", 6) == 0) {
1650
+ return scr_dyn_new_num((double)d->v.fn.arity);
1651
+ }
1652
+ return NULL;
1653
+ }
1654
+
1655
+ /* ── structuredClone over the DOM ─────────────────────────────────────
1656
+ * The JSON-safe subset plus bytes, deep. Functions and handle kinds
1657
+ * throw the spec's catchable DataCloneError; cycles throw the scriptc
1658
+ * fence (the DOM cannot represent them — Node clones cycles; documented
1659
+ * divergence). Option validation throws Node's exact TypeErrors and is
1660
+ * shared with scr_domex_clone. */
1661
+
1662
+ void scr_sc_validate_options(const ScrDyn *options) {
1663
+ if (options == NULL || options->kind == SCR_DYN_UNDEF || options->kind == SCR_DYN_NULL) return;
1664
+ if (options->kind != SCR_DYN_OBJ) {
1665
+ static const char msg[] =
1666
+ "Failed to execute 'structuredClone': Options cannot be converted to a dictionary";
1667
+ scr_throw_error_msg_code(SCR_ERR_TYPE, msg, sizeof msg - 1, "ERR_INVALID_ARG_TYPE");
1668
+ return;
1669
+ }
1670
+ ScrDyn *tr = scr_dyn_obj_get(options, "transfer", 8); /* borrowed */
1671
+ if (tr == NULL || tr->kind == SCR_DYN_UNDEF) return;
1672
+ if (tr->kind != SCR_DYN_ARR) {
1673
+ static const char msg[] =
1674
+ "Failed to execute 'structuredClone': transfer in Options can not be converted to sequence.";
1675
+ scr_throw_error_msg_code(SCR_ERR_TYPE, msg, sizeof msg - 1, "ERR_INVALID_ARG_TYPE");
1676
+ return;
1677
+ }
1678
+ if (tr->v.arr.len > 0) {
1679
+ /* Nothing in the static world is transferable — Node's own error for
1680
+ * a non-transferable list member. */
1681
+ scr_throw_domex("DataCloneError", "Found invalid value in transferList.");
1682
+ }
1683
+ }
1684
+
1685
+ /* The parent chain rides the C stack: a revisit is a cycle. */
1686
+ typedef struct ScrScParent {
1687
+ const ScrDyn *node;
1688
+ const struct ScrScParent *up;
1689
+ } ScrScParent;
1690
+
1691
+ static ScrDyn *scr_sc_clone(const ScrDyn *v, const ScrScParent *up) {
1692
+ switch (v->kind) {
1693
+ case SCR_DYN_UNDEF:
1694
+ return scr_dyn_retain(scr_dyn_undefined());
1695
+ case SCR_DYN_NULL:
1696
+ return scr_dyn_new_null();
1697
+ case SCR_DYN_BOOL:
1698
+ return scr_dyn_new_bool(v->v.b);
1699
+ case SCR_DYN_NUM:
1700
+ return scr_dyn_new_num(v->v.num);
1701
+ case SCR_DYN_STR:
1702
+ return scr_dyn_new_str(v->v.str);
1703
+ case SCR_DYN_BYTES:
1704
+ /* A fresh byte copy; the Buffer flavor drops (Node: structuredClone
1705
+ * of a Buffer answers a plain Uint8Array). */
1706
+ return scr_dyn_new_bytes_copy(v->v.bytes);
1707
+ case SCR_DYN_ARR:
1708
+ case SCR_DYN_OBJ: {
1709
+ for (const ScrScParent *p = up; p != NULL; p = p->up) {
1710
+ if (p->node == v) {
1711
+ static const char msg[] =
1712
+ "structuredClone of cyclic values (the runtime's DOM cannot represent cycles) is not supported yet";
1713
+ scr_throw_error_msg(SCR_ERR_ERROR, msg, sizeof msg - 1);
1714
+ return NULL;
1715
+ }
1716
+ }
1717
+ ScrScParent self = {v, up};
1718
+ if (v->kind == SCR_DYN_ARR) {
1719
+ ScrDyn *out = scr_dyn_new_arr();
1720
+ for (size_t i = 0; i < v->v.arr.len; i++) {
1721
+ ScrDyn *c = scr_sc_clone(v->v.arr.items[i], &self);
1722
+ if (c == NULL) { /* threw */
1723
+ scr_dyn_release(out);
1724
+ return NULL;
1725
+ }
1726
+ scr_dyn_arr_push(out, c); /* ownership moves */
1727
+ }
1728
+ return out;
1729
+ }
1730
+ ScrDyn *out = scr_dyn_new_obj();
1731
+ for (size_t i = 0; i < v->v.obj.len; i++) {
1732
+ const ScrDynEntry *e = &v->v.obj.entries[i];
1733
+ ScrDyn *c = scr_sc_clone(e->value, &self);
1734
+ if (c == NULL) {
1735
+ scr_dyn_release(out);
1736
+ return NULL;
1737
+ }
1738
+ scr_dyn_obj_set(out, e->key, e->key_len, c); /* ownership moves */
1739
+ }
1740
+ return out;
1741
+ }
1742
+ case SCR_DYN_FUNC:
1743
+ case SCR_DYN_HANDLE:
1744
+ default: {
1745
+ /* Node renders the value's source text; the DOM has none — the
1746
+ * String() rendering stands in ("function () { [native code] } could
1747
+ * not be cloned."). */
1748
+ ScrStr *what = scr_dyn_string_coerce(v);
1749
+ static const char suffix[] = " could not be cloned.";
1750
+ size_t len = what->len + sizeof suffix - 1;
1751
+ char *msg = malloc(len + 1);
1752
+ if (!msg) {
1753
+ fputs("scriptc: out of memory\n", stderr);
1754
+ abort();
1755
+ }
1756
+ memcpy(msg, what->data, what->len);
1757
+ memcpy(msg + what->len, suffix, sizeof suffix);
1758
+ scr_str_release(what);
1759
+ ScrStr *m = scr_str_new(msg, len);
1760
+ free(msg);
1761
+ scr_throw_domex_str("DataCloneError", m); /* takes ownership of m */
1762
+ return NULL;
1763
+ }
1764
+ }
1765
+ }
1766
+
1767
+ ScrDyn *scr_structured_clone(const ScrDyn *value, const ScrDyn *options) {
1768
+ scr_sc_validate_options(options);
1769
+ if (scr_exc_pending()) return NULL;
1770
+ return scr_sc_clone(value, NULL);
1771
+ }
1772
+
1773
+ ScrDyn *scr_structured_clone_transfer_fail(void) {
1774
+ scr_throw_domex("DataCloneError", "Found invalid value in transferList.");
1775
+ return NULL;
1776
+ }
1777
+
1778
+ ScrDyn *scr_structured_clone_missing(void) {
1779
+ /* Node's message, verbatim (its own template double-wraps the text). */
1780
+ static const char msg[] =
1781
+ "The \"The value argument must be specified\" argument must be specified";
1782
+ scr_throw_error_msg_code(SCR_ERR_TYPE, msg, sizeof msg - 1, "ERR_MISSING_ARGS");
1783
+ return NULL;
1784
+ }
1785
+
1786
+ bool scr_dyn_err_instanceof(const ScrDyn *d, double kind) {
1787
+ for (size_t i = 0; i < scr_errdyn_n; i++) {
1788
+ if (scr_errdyn_cache[i].dyn == d) {
1789
+ const ScrVt *vt = scr_errdyn_cache[i].err->vt;
1790
+ int k = (int)kind;
1791
+ return scr_error_vts[k].pre <= vt->pre && vt->pre <= scr_error_vts[k].post;
1792
+ }
1793
+ }
1794
+ return false;
1795
+ }
1796
+
1797
+ /* ── Object.keys/values/entries over the DOM ──────────────────────────
1798
+ * JS own-key order: array-index keys ascending first, then the rest in
1799
+ * insertion order. entries answers [key, value] pairs; values RETAIN
1800
+ * the member nodes (reference semantics, like JS). Strings/arrays/bytes
1801
+ * answer their index keys; other scalars an empty array; null/undefined
1802
+ * throw Node's catchable TypeError. */
1803
+
1804
+ /* The array-index test (ECMA: a canonical numeric string < 2^32-1). */
1805
+ static bool scr_dyn_key_is_index(const char *key, size_t len, double *out) {
1806
+ if (len == 0 || len > 10) return false;
1807
+ if (key[0] == '0' && len > 1) return false;
1808
+ double v = 0;
1809
+ for (size_t i = 0; i < len; i++) {
1810
+ if (key[i] < '0' || key[i] > '9') return false;
1811
+ v = v * 10 + (key[i] - '0');
1812
+ }
1813
+ if (v > 4294967294.0) return false;
1814
+ *out = v;
1815
+ return true;
1816
+ }
1817
+
1818
+ typedef enum { SCR_OBJWALK_KEYS, SCR_OBJWALK_VALUES, SCR_OBJWALK_ENTRIES } ScrObjWalk;
1819
+
1820
+ /* A fresh key string boxed into the DOM: scr_dyn_new_str RETAINS its
1821
+ * argument, so the local +1 drops right after. */
1822
+ static ScrDyn *scr_dyn_objwalk_key(const char *key, size_t key_len) {
1823
+ ScrStr *k = scr_str_new(key, key_len);
1824
+ ScrDyn *d = scr_dyn_new_str(k);
1825
+ scr_str_release(k);
1826
+ return d;
1827
+ }
1828
+
1829
+ static void scr_dyn_objwalk_push(ScrDyn *out, ScrObjWalk mode, const char *key,
1830
+ size_t key_len, ScrDyn *value /* borrowed */) {
1831
+ switch (mode) {
1832
+ case SCR_OBJWALK_KEYS:
1833
+ scr_dyn_arr_push(out, scr_dyn_objwalk_key(key, key_len));
1834
+ break;
1835
+ case SCR_OBJWALK_VALUES:
1836
+ scr_dyn_arr_push(out, scr_dyn_retain(value));
1837
+ break;
1838
+ case SCR_OBJWALK_ENTRIES: {
1839
+ ScrDyn *pair = scr_dyn_new_arr();
1840
+ scr_dyn_arr_push(pair, scr_dyn_objwalk_key(key, key_len));
1841
+ scr_dyn_arr_push(pair, scr_dyn_retain(value));
1842
+ scr_dyn_arr_push(out, pair);
1843
+ break;
1844
+ }
1845
+ }
1846
+ }
1847
+
1848
+ static ScrDyn *scr_dyn_objwalk(const ScrDyn *v, ScrObjWalk mode) {
1849
+ if (v->kind == SCR_DYN_UNDEF || v->kind == SCR_DYN_NULL) {
1850
+ static const char msg[] = "Cannot convert undefined or null to object";
1851
+ scr_throw_error_msg(SCR_ERR_TYPE, msg, sizeof msg - 1);
1852
+ return NULL;
1853
+ }
1854
+ ScrDyn *out = scr_dyn_new_arr();
1855
+ if (v->kind == SCR_DYN_OBJ) {
1856
+ size_t n = v->v.obj.len;
1857
+ /* Two passes: array-index keys ascending, then the rest in insertion
1858
+ * order (JS's own-key order). Index keys are rare in DOM objects —
1859
+ * the ascending pass is a simple selection scan. */
1860
+ bool *is_index = malloc(n ? n * sizeof *is_index : 1);
1861
+ double *idx = malloc(n ? n * sizeof *idx : 1);
1862
+ if (!is_index || !idx) {
1863
+ fputs("scriptc: out of memory\n", stderr);
1864
+ abort();
1865
+ }
1866
+ size_t index_count = 0;
1867
+ for (size_t i = 0; i < n; i++) {
1868
+ const ScrDynEntry *e = &v->v.obj.entries[i];
1869
+ /* Reserved '%'-prefixed members (the DOM's error marker) never
1870
+ * appear in user objects — '%' cannot start a JS identifier-ish
1871
+ * JSON key from this runtime's own producers; skip defensively. */
1872
+ is_index[i] = scr_dyn_key_is_index(e->key, e->key_len, &idx[i]);
1873
+ if (is_index[i]) index_count++;
1874
+ }
1875
+ double last = -1;
1876
+ for (size_t done = 0; done < index_count; done++) {
1877
+ size_t best = (size_t)-1;
1878
+ for (size_t i = 0; i < n; i++) {
1879
+ if (!is_index[i] || idx[i] <= last) continue;
1880
+ if (best == (size_t)-1 || idx[i] < idx[best]) best = i;
1881
+ }
1882
+ if (best == (size_t)-1) break;
1883
+ last = idx[best];
1884
+ const ScrDynEntry *e = &v->v.obj.entries[best];
1885
+ scr_dyn_objwalk_push(out, mode, e->key, e->key_len, e->value);
1886
+ }
1887
+ for (size_t i = 0; i < n; i++) {
1888
+ if (is_index[i]) continue;
1889
+ const ScrDynEntry *e = &v->v.obj.entries[i];
1890
+ scr_dyn_objwalk_push(out, mode, e->key, e->key_len, e->value);
1891
+ }
1892
+ free(is_index);
1893
+ free(idx);
1894
+ return out;
1895
+ }
1896
+ if (v->kind == SCR_DYN_ARR || v->kind == SCR_DYN_BYTES) {
1897
+ size_t n = v->kind == SCR_DYN_ARR ? v->v.arr.len : v->v.bytes->len;
1898
+ for (size_t i = 0; i < n; i++) {
1899
+ char key[24];
1900
+ int klen = snprintf(key, sizeof key, "%zu", i);
1901
+ ScrDyn *val = NULL;
1902
+ if (mode != SCR_OBJWALK_KEYS) {
1903
+ val = v->kind == SCR_DYN_ARR ? scr_dyn_retain(v->v.arr.items[i])
1904
+ : scr_dyn_new_num((double)v->v.bytes->data[i]);
1905
+ }
1906
+ if (mode == SCR_OBJWALK_KEYS) {
1907
+ scr_dyn_arr_push(out, scr_dyn_objwalk_key(key, (size_t)klen));
1908
+ } else if (mode == SCR_OBJWALK_VALUES) {
1909
+ scr_dyn_arr_push(out, val);
1910
+ } else {
1911
+ ScrDyn *pair = scr_dyn_new_arr();
1912
+ scr_dyn_arr_push(pair, scr_dyn_objwalk_key(key, (size_t)klen));
1913
+ scr_dyn_arr_push(pair, val);
1914
+ scr_dyn_arr_push(out, pair);
1915
+ }
1916
+ }
1917
+ return out;
1918
+ }
1919
+ if (v->kind == SCR_DYN_STR) {
1920
+ /* JS indexes strings by UTF-16 code units; the DOM stores UTF-8.
1921
+ * Code points walk one at a time — an astral code point stays WHOLE
1922
+ * (one entry where JS lists two lone surrogates; documented
1923
+ * approximation, the keys stay dense). */
1924
+ const ScrStr *s = v->v.str;
1925
+ size_t unit = 0;
1926
+ for (size_t i = 0; i < s->len;) {
1927
+ unsigned char c = (unsigned char)s->data[i];
1928
+ size_t step = c < 0x80 ? 1 : c < 0xe0 ? 2 : c < 0xf0 ? 3 : 4;
1929
+ char key[24];
1930
+ int klen = snprintf(key, sizeof key, "%zu", unit);
1931
+ if (mode == SCR_OBJWALK_KEYS) {
1932
+ scr_dyn_arr_push(out, scr_dyn_objwalk_key(key, (size_t)klen));
1933
+ } else {
1934
+ ScrDyn *val = scr_dyn_objwalk_key(s->data + i, step);
1935
+ if (mode == SCR_OBJWALK_VALUES) {
1936
+ scr_dyn_arr_push(out, val);
1937
+ } else {
1938
+ ScrDyn *pair = scr_dyn_new_arr();
1939
+ scr_dyn_arr_push(pair, scr_dyn_objwalk_key(key, (size_t)klen));
1940
+ scr_dyn_arr_push(pair, val);
1941
+ scr_dyn_arr_push(out, pair);
1942
+ }
1943
+ }
1944
+ unit++;
1945
+ i += step;
1946
+ }
1947
+ return out;
1948
+ }
1949
+ /* Scalars (numbers, booleans, functions, handles): no own enumerable
1950
+ * string keys. */
1951
+ return out;
1952
+ }
1953
+
1954
+ ScrDyn *scr_dyn_obj_keys(const ScrDyn *v) { return scr_dyn_objwalk(v, SCR_OBJWALK_KEYS); }
1955
+ ScrDyn *scr_dyn_obj_values(const ScrDyn *v) { return scr_dyn_objwalk(v, SCR_OBJWALK_VALUES); }
1956
+ ScrDyn *scr_dyn_obj_entries(const ScrDyn *v) { return scr_dyn_objwalk(v, SCR_OBJWALK_ENTRIES); }
1957
+
1958
+ /* ── DOMException's dyn-touching half ─────────────────────────────────
1959
+ * Construction/cause/clone live HERE (not scr_error.c) so the error unit
1960
+ * stays linkable without the DOM (the runtime C-unit tests link
1961
+ * subsets). The cause teardown installs through scr_error.c's hook
1962
+ * before any cause can exist. */
1963
+
1964
+ static void scr_domex_cause_drop_impl(void *obj) {
1965
+ ScrDomException *d = (ScrDomException *)obj;
1966
+ scr_dyn_release(d->cause);
1967
+ d->cause = NULL;
1968
+ }
1969
+
1970
+ ScrError *scr_domex_new(const ScrDyn *message, const ScrDyn *name_or_options) {
1971
+ scr_domex_install_cause_drop(&scr_domex_cause_drop_impl);
1972
+ ScrDomException *d = (ScrDomException *)scr_domex_alloc();
1973
+ d->message = (message == NULL || message->kind == SCR_DYN_UNDEF)
1974
+ ? scr_str_new("", 0)
1975
+ : scr_dyn_string_coerce(message);
1976
+ const ScrDyn *no = name_or_options;
1977
+ if (no == NULL || no->kind == SCR_DYN_UNDEF) {
1978
+ d->name = scr_str_new("Error", 5);
1979
+ } else if (no->kind == SCR_DYN_OBJ) {
1980
+ /* The options form (Node's extension): name is ToString of the `name`
1981
+ * member — String(undefined) is "undefined" when absent, exactly
1982
+ * Node — and `cause` records own-property PRESENCE (undefined-valued
1983
+ * members count, like `'cause' in options`). */
1984
+ ScrDyn *nm = scr_dyn_obj_get(no, "name", 4);
1985
+ d->name = nm ? scr_dyn_string_coerce(nm) : scr_str_new("undefined", 9);
1986
+ ScrDyn *cause = scr_dyn_obj_get(no, "cause", 5); /* borrowed; NULL = absent */
1987
+ if (cause) {
1988
+ d->has_cause = true;
1989
+ d->cause = scr_dyn_retain(cause);
1990
+ }
1991
+ } else {
1992
+ /* Everything else ToStrings (Node: new DOMException('m', null) has
1993
+ * name "null", a number names its decimal rendering). */
1994
+ d->name = scr_dyn_string_coerce(no);
1995
+ }
1996
+ d->dom_code = scr_domex_code_of(d->name);
1997
+ return (ScrError *)d;
1998
+ }
1999
+
2000
+ ScrDyn *scr_domex_cause(ScrError *e) {
2001
+ ScrDomException *d = (ScrDomException *)e;
2002
+ return d->cause ? scr_dyn_retain(d->cause) : scr_dyn_undefined();
2003
+ }
2004
+
2005
+ ScrError *scr_domex_clone(ScrError *e, const ScrDyn *options) {
2006
+ scr_sc_validate_options(options);
2007
+ if (scr_exc_pending()) return NULL;
2008
+ ScrDomException *src = (ScrDomException *)e;
2009
+ ScrDomException *d = (ScrDomException *)scr_domex_alloc();
2010
+ d->name = scr_str_retain(src->name);
2011
+ d->message = scr_str_retain(src->message);
2012
+ /* The legacy code re-derives from the name (spec: serialization carries
2013
+ * name + message; cause does not serialize). */
2014
+ d->dom_code = scr_domex_code_of(d->name);
2015
+ return (ScrError *)d;
2016
+ }
2017
+
2018
+ /* ── atob/btoa — the WHATWG base64 globals (Node globals since v16) ───
2019
+ * They live HERE (not scr_string.c) because the argument is a DOM value:
2020
+ * WebIDL ToString runs over the DOM kind (Node's atob(null) decodes the
2021
+ * string "null"), and the string unit must stay linkable without the
2022
+ * DOM. atob is forgiving-base64 exactly — ASCII whitespace stripped, a
2023
+ * %4==0 input sheds up to two trailing '=', %4==1 refuses, leftover
2024
+ * bits discard — decoding to the latin1 code points as a UTF-8 string;
2025
+ * btoa refuses any code point over U+00FF. Malformed input throws the
2026
+ * catchable DOMException InvalidCharacterError with Node's exact
2027
+ * message. */
2028
+
2029
+ static int scr_b64_val(unsigned char c) {
2030
+ if (c >= 'A' && c <= 'Z') return c - 'A';
2031
+ if (c >= 'a' && c <= 'z') return c - 'a' + 26;
2032
+ if (c >= '0' && c <= '9') return c - '0' + 52;
2033
+ if (c == '+') return 62;
2034
+ if (c == '/') return 63;
2035
+ return -1;
2036
+ }
2037
+
2038
+ ScrStr *scr_atob(const ScrDyn *data) {
2039
+ ScrStr *s = scr_dyn_string_coerce(data);
2040
+ /* Strip ASCII whitespace (the forgiving step). */
2041
+ char *buf = malloc(s->len ? s->len : 1);
2042
+ if (!buf) scr_json_oom();
2043
+ size_t n = 0;
2044
+ for (size_t i = 0; i < s->len; i++) {
2045
+ unsigned char c = (unsigned char)s->data[i];
2046
+ if (c == 0x09 || c == 0x0a || c == 0x0c || c == 0x0d || c == 0x20) continue;
2047
+ buf[n++] = (char)c;
2048
+ }
2049
+ scr_str_release(s);
2050
+ /* A %4==0 input sheds one or two trailing '='. */
2051
+ if (n % 4 == 0 && n > 0) {
2052
+ if (buf[n - 1] == '=') n--;
2053
+ if (n > 0 && buf[n - 1] == '=') n--;
2054
+ }
2055
+ if (n % 4 == 1) goto invalid;
2056
+ /* Decode 6-bit groups; latin1 code points expand to UTF-8 (bytes over
2057
+ * 0x7F become two-byte sequences). */
2058
+ {
2059
+ size_t outBytes = (n / 4) * 3 + (n % 4 == 2 ? 1 : n % 4 == 3 ? 2 : 0);
2060
+ char *out = malloc(outBytes * 2 ? outBytes * 2 : 1);
2061
+ if (!out) scr_json_oom();
2062
+ size_t w = 0;
2063
+ unsigned acc = 0;
2064
+ int bits = 0;
2065
+ for (size_t i = 0; i < n; i++) {
2066
+ int v = scr_b64_val((unsigned char)buf[i]);
2067
+ if (v < 0) {
2068
+ free(out);
2069
+ goto invalid;
2070
+ }
2071
+ acc = (acc << 6) | (unsigned)v;
2072
+ bits += 6;
2073
+ if (bits >= 8) {
2074
+ bits -= 8;
2075
+ unsigned char b = (unsigned char)((acc >> bits) & 0xff);
2076
+ if (b < 0x80) {
2077
+ out[w++] = (char)b;
2078
+ } else {
2079
+ out[w++] = (char)(0xc0 | (b >> 6));
2080
+ out[w++] = (char)(0x80 | (b & 0x3f));
2081
+ }
2082
+ }
2083
+ }
2084
+ /* Leftover bits discard (forgiving-base64's final step). */
2085
+ free(buf);
2086
+ ScrStr *result = scr_str_new(out, w);
2087
+ free(out);
2088
+ return result;
2089
+ }
2090
+ invalid:
2091
+ free(buf);
2092
+ scr_throw_domex("InvalidCharacterError",
2093
+ "The string to be decoded is not correctly encoded.");
2094
+ return NULL;
2095
+ }
2096
+
2097
+ ScrStr *scr_btoa(const ScrDyn *data) {
2098
+ static const char alphabet[] =
2099
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2100
+ ScrStr *s = scr_dyn_string_coerce(data);
2101
+ /* UTF-8 → code points, each must fit latin1 (one byte). The runtime's
2102
+ * strings are well-formed UTF-8, so only C2/C3 leads can stay in
2103
+ * range; every other lead byte names a code point over U+00FF. */
2104
+ char *bytes = malloc(s->len ? s->len : 1);
2105
+ if (!bytes) scr_json_oom();
2106
+ size_t n = 0;
2107
+ for (size_t i = 0; i < s->len;) {
2108
+ unsigned char c = (unsigned char)s->data[i];
2109
+ if (c < 0x80) {
2110
+ bytes[n++] = (char)c;
2111
+ i += 1;
2112
+ } else if ((c == 0xc2 || c == 0xc3) && i + 1 < s->len) {
2113
+ unsigned char c1 = (unsigned char)s->data[i + 1];
2114
+ bytes[n++] = (char)(((c & 0x1f) << 6) | (c1 & 0x3f));
2115
+ i += 2;
2116
+ } else {
2117
+ free(bytes);
2118
+ scr_str_release(s);
2119
+ scr_throw_domex("InvalidCharacterError", "Invalid character");
2120
+ return NULL;
2121
+ }
2122
+ }
2123
+ scr_str_release(s);
2124
+ {
2125
+ size_t cap = ((n + 2) / 3) * 4;
2126
+ char *out = malloc(cap ? cap : 1);
2127
+ if (!out) scr_json_oom();
2128
+ size_t w = 0;
2129
+ for (size_t i = 0; i < n; i += 3) {
2130
+ unsigned b0 = (unsigned char)bytes[i];
2131
+ unsigned b1 = i + 1 < n ? (unsigned char)bytes[i + 1] : 0;
2132
+ unsigned b2 = i + 2 < n ? (unsigned char)bytes[i + 2] : 0;
2133
+ unsigned triple = (b0 << 16) | (b1 << 8) | b2;
2134
+ out[w++] = alphabet[(triple >> 18) & 0x3f];
2135
+ out[w++] = alphabet[(triple >> 12) & 0x3f];
2136
+ out[w++] = i + 1 < n ? alphabet[(triple >> 6) & 0x3f] : '=';
2137
+ out[w++] = i + 2 < n ? alphabet[triple & 0x3f] : '=';
2138
+ }
2139
+ free(bytes);
2140
+ ScrStr *result = scr_str_new(out, w);
2141
+ free(out);
2142
+ return result;
2143
+ }
2144
+ }
2145
+
2146
+ ScrStr *scr_b64_missing_arg(void) {
2147
+ static const char msg[] = "The \"input\" argument must be specified";
2148
+ scr_throw_error_msg_code(SCR_ERR_TYPE, msg, sizeof msg - 1,
2149
+ "ERR_MISSING_ARGS");
2150
+ return NULL;
2151
+ }