@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.
- package/LICENSE +202 -0
- package/package.json +16 -6
- package/src/scr_array.c +488 -0
- package/src/scr_assert.c +1393 -0
- package/src/scr_async.c +2593 -0
- package/src/scr_async_dyn.c +607 -0
- package/src/scr_bytes.c +1425 -0
- package/src/scr_bytes_io.c +161 -0
- package/src/scr_child.c +3587 -0
- package/src/scr_closure.c +214 -0
- package/src/scr_console.c +143 -0
- package/src/scr_cycle.c +218 -0
- package/src/scr_dc.c +805 -0
- package/src/scr_dgram.c +1007 -0
- package/src/scr_dyn_handle.c +154 -0
- package/src/scr_dyn_invoke.c +644 -0
- package/src/scr_error.c +332 -0
- package/src/scr_events.c +714 -0
- package/src/scr_events_emitter.c +699 -0
- package/src/scr_exception.c +242 -0
- package/src/scr_fetch.c +1130 -0
- package/src/scr_fetch_curl.c +654 -0
- package/src/scr_http.c +3482 -0
- package/src/scr_http2.c +3030 -0
- package/src/scr_inspect.c +803 -0
- package/src/scr_inspect_island.c +51 -0
- package/src/scr_island.c +9363 -0
- package/src/scr_json.c +2151 -0
- package/src/scr_lib.c +3612 -0
- package/src/scr_loop_epoll.c +241 -0
- package/src/scr_loop_kqueue.c +121 -0
- package/src/scr_loop_wsapoll.c +208 -0
- package/src/scr_map.c +591 -0
- package/src/scr_net.c +3017 -0
- package/src/scr_net_island.c +458 -0
- package/src/scr_number.c +115 -0
- package/src/scr_object.c +25 -0
- package/src/scr_path.c +1266 -0
- package/src/scr_platform.h +119 -0
- package/src/scr_readline.c +287 -0
- package/src/scr_regex.c +1080 -0
- package/src/scr_runtime.h +5046 -0
- package/src/scr_stream.c +2877 -0
- package/src/scr_string.c +1264 -0
- package/src/scr_symbol.c +132 -0
- package/src/scr_test.c +662 -0
- package/src/scr_tls.c +1520 -0
- package/src/scr_union.c +105 -0
- package/src/scr_url.c +911 -0
- package/src/scr_url_internal.h +29 -0
- package/src/scr_url_params.c +561 -0
- package/src/scr_watch.c +568 -0
- package/src/scr_web.c +1778 -0
- package/src/scr_win.c +76 -0
- package/src/scr_zlib.c +197 -0
- package/src/scr_zlib_island.c +15 -0
- package/vendor/README.md +52 -0
- package/vendor/curl/COPYRIGHT +534 -0
- package/vendor/curl/include/curl/curl.h +3214 -0
- package/vendor/curl/include/curl/curlver.h +79 -0
- package/vendor/curl/include/curl/easy.h +125 -0
- package/vendor/curl/include/curl/header.h +74 -0
- package/vendor/curl/include/curl/mprintf.h +52 -0
- package/vendor/curl/include/curl/multi.h +460 -0
- package/vendor/curl/include/curl/options.h +70 -0
- package/vendor/curl/include/curl/stdcheaders.h +35 -0
- package/vendor/curl/include/curl/system.h +508 -0
- package/vendor/curl/include/curl/typecheck-gcc.h +716 -0
- package/vendor/curl/include/curl/urlapi.h +149 -0
- package/vendor/curl/include/curl/websockets.h +84 -0
- package/vendor/mbedtls/LICENSE +553 -0
- package/vendor/mbedtls/include/CMakeLists.txt +22 -0
- package/vendor/mbedtls/include/mbedtls/aes.h +631 -0
- package/vendor/mbedtls/include/mbedtls/aria.h +343 -0
- package/vendor/mbedtls/include/mbedtls/asn1.h +642 -0
- package/vendor/mbedtls/include/mbedtls/asn1write.h +390 -0
- package/vendor/mbedtls/include/mbedtls/base64.h +82 -0
- package/vendor/mbedtls/include/mbedtls/bignum.h +1088 -0
- package/vendor/mbedtls/include/mbedtls/block_cipher.h +76 -0
- package/vendor/mbedtls/include/mbedtls/build_info.h +194 -0
- package/vendor/mbedtls/include/mbedtls/camellia.h +305 -0
- package/vendor/mbedtls/include/mbedtls/ccm.h +526 -0
- package/vendor/mbedtls/include/mbedtls/chacha20.h +209 -0
- package/vendor/mbedtls/include/mbedtls/chachapoly.h +351 -0
- package/vendor/mbedtls/include/mbedtls/check_config.h +1149 -0
- package/vendor/mbedtls/include/mbedtls/cipher.h +1250 -0
- package/vendor/mbedtls/include/mbedtls/cmac.h +246 -0
- package/vendor/mbedtls/include/mbedtls/compat-2.x.h +46 -0
- package/vendor/mbedtls/include/mbedtls/config_adjust_legacy_crypto.h +578 -0
- package/vendor/mbedtls/include/mbedtls/config_adjust_legacy_from_psa.h +873 -0
- package/vendor/mbedtls/include/mbedtls/config_adjust_psa_from_legacy.h +359 -0
- package/vendor/mbedtls/include/mbedtls/config_adjust_psa_superset_legacy.h +145 -0
- package/vendor/mbedtls/include/mbedtls/config_adjust_ssl.h +91 -0
- package/vendor/mbedtls/include/mbedtls/config_adjust_x509.h +35 -0
- package/vendor/mbedtls/include/mbedtls/config_psa.h +61 -0
- package/vendor/mbedtls/include/mbedtls/constant_time.h +36 -0
- package/vendor/mbedtls/include/mbedtls/ctr_drbg.h +596 -0
- package/vendor/mbedtls/include/mbedtls/debug.h +156 -0
- package/vendor/mbedtls/include/mbedtls/des.h +385 -0
- package/vendor/mbedtls/include/mbedtls/dhm.h +972 -0
- package/vendor/mbedtls/include/mbedtls/ecdh.h +455 -0
- package/vendor/mbedtls/include/mbedtls/ecdsa.h +674 -0
- package/vendor/mbedtls/include/mbedtls/ecjpake.h +298 -0
- package/vendor/mbedtls/include/mbedtls/ecp.h +1517 -0
- package/vendor/mbedtls/include/mbedtls/entropy.h +274 -0
- package/vendor/mbedtls/include/mbedtls/error.h +201 -0
- package/vendor/mbedtls/include/mbedtls/gcm.h +387 -0
- package/vendor/mbedtls/include/mbedtls/hkdf.h +124 -0
- package/vendor/mbedtls/include/mbedtls/hmac_drbg.h +434 -0
- package/vendor/mbedtls/include/mbedtls/lms.h +440 -0
- package/vendor/mbedtls/include/mbedtls/mbedtls_config.h +4446 -0
- package/vendor/mbedtls/include/mbedtls/md.h +526 -0
- package/vendor/mbedtls/include/mbedtls/md5.h +190 -0
- package/vendor/mbedtls/include/mbedtls/memory_buffer_alloc.h +142 -0
- package/vendor/mbedtls/include/mbedtls/net_sockets.h +299 -0
- package/vendor/mbedtls/include/mbedtls/nist_kw.h +166 -0
- package/vendor/mbedtls/include/mbedtls/oid.h +727 -0
- package/vendor/mbedtls/include/mbedtls/pem.h +160 -0
- package/vendor/mbedtls/include/mbedtls/pk.h +1303 -0
- package/vendor/mbedtls/include/mbedtls/pkcs12.h +186 -0
- package/vendor/mbedtls/include/mbedtls/pkcs5.h +198 -0
- package/vendor/mbedtls/include/mbedtls/pkcs7.h +252 -0
- package/vendor/mbedtls/include/mbedtls/platform.h +516 -0
- package/vendor/mbedtls/include/mbedtls/platform_time.h +79 -0
- package/vendor/mbedtls/include/mbedtls/platform_util.h +247 -0
- package/vendor/mbedtls/include/mbedtls/poly1305.h +168 -0
- package/vendor/mbedtls/include/mbedtls/private_access.h +20 -0
- package/vendor/mbedtls/include/mbedtls/psa_util.h +207 -0
- package/vendor/mbedtls/include/mbedtls/ripemd160.h +136 -0
- package/vendor/mbedtls/include/mbedtls/rsa.h +1170 -0
- package/vendor/mbedtls/include/mbedtls/sha1.h +219 -0
- package/vendor/mbedtls/include/mbedtls/sha256.h +200 -0
- package/vendor/mbedtls/include/mbedtls/sha3.h +172 -0
- package/vendor/mbedtls/include/mbedtls/sha512.h +208 -0
- package/vendor/mbedtls/include/mbedtls/ssl.h +5887 -0
- package/vendor/mbedtls/include/mbedtls/ssl_cache.h +187 -0
- package/vendor/mbedtls/include/mbedtls/ssl_ciphersuites.h +482 -0
- package/vendor/mbedtls/include/mbedtls/ssl_cookie.h +106 -0
- package/vendor/mbedtls/include/mbedtls/ssl_ticket.h +199 -0
- package/vendor/mbedtls/include/mbedtls/threading.h +167 -0
- package/vendor/mbedtls/include/mbedtls/timing.h +94 -0
- package/vendor/mbedtls/include/mbedtls/version.h +78 -0
- package/vendor/mbedtls/include/mbedtls/x509.h +500 -0
- package/vendor/mbedtls/include/mbedtls/x509_crl.h +184 -0
- package/vendor/mbedtls/include/mbedtls/x509_crt.h +1208 -0
- package/vendor/mbedtls/include/mbedtls/x509_csr.h +382 -0
- package/vendor/mbedtls/include/psa/build_info.h +20 -0
- package/vendor/mbedtls/include/psa/crypto.h +4998 -0
- package/vendor/mbedtls/include/psa/crypto_adjust_auto_enabled.h +31 -0
- package/vendor/mbedtls/include/psa/crypto_adjust_config_dependencies.h +51 -0
- package/vendor/mbedtls/include/psa/crypto_adjust_config_key_pair_types.h +101 -0
- package/vendor/mbedtls/include/psa/crypto_adjust_config_synonyms.h +49 -0
- package/vendor/mbedtls/include/psa/crypto_builtin_composites.h +214 -0
- package/vendor/mbedtls/include/psa/crypto_builtin_key_derivation.h +118 -0
- package/vendor/mbedtls/include/psa/crypto_builtin_primitives.h +114 -0
- package/vendor/mbedtls/include/psa/crypto_compat.h +230 -0
- package/vendor/mbedtls/include/psa/crypto_config.h +145 -0
- package/vendor/mbedtls/include/psa/crypto_driver_common.h +44 -0
- package/vendor/mbedtls/include/psa/crypto_driver_contexts_composites.h +151 -0
- package/vendor/mbedtls/include/psa/crypto_driver_contexts_key_derivation.h +52 -0
- package/vendor/mbedtls/include/psa/crypto_driver_contexts_primitives.h +105 -0
- package/vendor/mbedtls/include/psa/crypto_extra.h +2145 -0
- package/vendor/mbedtls/include/psa/crypto_legacy.h +88 -0
- package/vendor/mbedtls/include/psa/crypto_platform.h +102 -0
- package/vendor/mbedtls/include/psa/crypto_se_driver.h +1383 -0
- package/vendor/mbedtls/include/psa/crypto_sizes.h +1319 -0
- package/vendor/mbedtls/include/psa/crypto_struct.h +527 -0
- package/vendor/mbedtls/include/psa/crypto_types.h +508 -0
- package/vendor/mbedtls/include/psa/crypto_values.h +2782 -0
- package/vendor/mbedtls/library/aes.c +2294 -0
- package/vendor/mbedtls/library/aesce.c +624 -0
- package/vendor/mbedtls/library/aesce.h +136 -0
- package/vendor/mbedtls/library/aesni.c +846 -0
- package/vendor/mbedtls/library/aesni.h +162 -0
- package/vendor/mbedtls/library/alignment.h +704 -0
- package/vendor/mbedtls/library/aria.c +969 -0
- package/vendor/mbedtls/library/asn1parse.c +468 -0
- package/vendor/mbedtls/library/asn1write.c +440 -0
- package/vendor/mbedtls/library/base64.c +322 -0
- package/vendor/mbedtls/library/base64_internal.h +45 -0
- package/vendor/mbedtls/library/bignum.c +2583 -0
- package/vendor/mbedtls/library/bignum_core.c +1240 -0
- package/vendor/mbedtls/library/bignum_core.h +872 -0
- package/vendor/mbedtls/library/bignum_core_invasive.h +38 -0
- package/vendor/mbedtls/library/bignum_internal.h +122 -0
- package/vendor/mbedtls/library/bignum_mod.c +394 -0
- package/vendor/mbedtls/library/bignum_mod.h +452 -0
- package/vendor/mbedtls/library/bignum_mod_raw.c +276 -0
- package/vendor/mbedtls/library/bignum_mod_raw.h +416 -0
- package/vendor/mbedtls/library/bignum_mod_raw_invasive.h +34 -0
- package/vendor/mbedtls/library/block_cipher.c +207 -0
- package/vendor/mbedtls/library/block_cipher_internal.h +99 -0
- package/vendor/mbedtls/library/bn_mul.h +1094 -0
- package/vendor/mbedtls/library/camellia.c +1058 -0
- package/vendor/mbedtls/library/ccm.c +777 -0
- package/vendor/mbedtls/library/chacha20.c +563 -0
- package/vendor/mbedtls/library/chacha20_internal.h +23 -0
- package/vendor/mbedtls/library/chachapoly.c +487 -0
- package/vendor/mbedtls/library/check_crypto_config.h +136 -0
- package/vendor/mbedtls/library/cipher.c +1712 -0
- package/vendor/mbedtls/library/cipher_invasive.h +28 -0
- package/vendor/mbedtls/library/cipher_wrap.c +2482 -0
- package/vendor/mbedtls/library/cipher_wrap.h +178 -0
- package/vendor/mbedtls/library/cmac.c +1067 -0
- package/vendor/mbedtls/library/common.h +487 -0
- package/vendor/mbedtls/library/constant_time.c +247 -0
- package/vendor/mbedtls/library/constant_time_impl.h +541 -0
- package/vendor/mbedtls/library/constant_time_internal.h +579 -0
- package/vendor/mbedtls/library/ctr.h +35 -0
- package/vendor/mbedtls/library/ctr_drbg.c +1016 -0
- package/vendor/mbedtls/library/debug.c +475 -0
- package/vendor/mbedtls/library/debug_internal.h +185 -0
- package/vendor/mbedtls/library/des.c +1042 -0
- package/vendor/mbedtls/library/dhm.c +700 -0
- package/vendor/mbedtls/library/ecdh.c +696 -0
- package/vendor/mbedtls/library/ecdsa.c +858 -0
- package/vendor/mbedtls/library/ecjpake.c +1216 -0
- package/vendor/mbedtls/library/ecp.c +3674 -0
- package/vendor/mbedtls/library/ecp_curves.c +6125 -0
- package/vendor/mbedtls/library/ecp_curves_new.c +9 -0
- package/vendor/mbedtls/library/ecp_internal_alt.h +287 -0
- package/vendor/mbedtls/library/ecp_invasive.h +305 -0
- package/vendor/mbedtls/library/entropy.c +680 -0
- package/vendor/mbedtls/library/entropy_poll.c +233 -0
- package/vendor/mbedtls/library/entropy_poll.h +64 -0
- package/vendor/mbedtls/library/error.c +878 -0
- package/vendor/mbedtls/library/gcm.c +1330 -0
- package/vendor/mbedtls/library/hkdf.c +161 -0
- package/vendor/mbedtls/library/hmac_drbg.c +633 -0
- package/vendor/mbedtls/library/lmots.c +789 -0
- package/vendor/mbedtls/library/lmots.h +288 -0
- package/vendor/mbedtls/library/lms.c +778 -0
- package/vendor/mbedtls/library/md.c +1108 -0
- package/vendor/mbedtls/library/md5.c +426 -0
- package/vendor/mbedtls/library/md_psa.h +26 -0
- package/vendor/mbedtls/library/md_wrap.h +46 -0
- package/vendor/mbedtls/library/memory_buffer_alloc.c +751 -0
- package/vendor/mbedtls/library/mps_common.h +181 -0
- package/vendor/mbedtls/library/mps_error.h +89 -0
- package/vendor/mbedtls/library/mps_reader.c +538 -0
- package/vendor/mbedtls/library/mps_reader.h +366 -0
- package/vendor/mbedtls/library/mps_trace.c +112 -0
- package/vendor/mbedtls/library/mps_trace.h +154 -0
- package/vendor/mbedtls/library/net_sockets.c +694 -0
- package/vendor/mbedtls/library/nist_kw.c +729 -0
- package/vendor/mbedtls/library/oid.c +1166 -0
- package/vendor/mbedtls/library/padlock.c +157 -0
- package/vendor/mbedtls/library/padlock.h +111 -0
- package/vendor/mbedtls/library/pem.c +554 -0
- package/vendor/mbedtls/library/pk.c +1602 -0
- package/vendor/mbedtls/library/pk_ecc.c +261 -0
- package/vendor/mbedtls/library/pk_internal.h +241 -0
- package/vendor/mbedtls/library/pk_wrap.c +1618 -0
- package/vendor/mbedtls/library/pk_wrap.h +138 -0
- package/vendor/mbedtls/library/pkcs12.c +437 -0
- package/vendor/mbedtls/library/pkcs5.c +500 -0
- package/vendor/mbedtls/library/pkcs7.c +787 -0
- package/vendor/mbedtls/library/pkparse.c +1392 -0
- package/vendor/mbedtls/library/pkwrite.c +631 -0
- package/vendor/mbedtls/library/pkwrite.h +121 -0
- package/vendor/mbedtls/library/platform.c +402 -0
- package/vendor/mbedtls/library/platform_util.c +258 -0
- package/vendor/mbedtls/library/poly1305.c +492 -0
- package/vendor/mbedtls/library/psa_crypto.c +9532 -0
- package/vendor/mbedtls/library/psa_crypto_aead.c +646 -0
- package/vendor/mbedtls/library/psa_crypto_aead.h +499 -0
- package/vendor/mbedtls/library/psa_crypto_cipher.c +747 -0
- package/vendor/mbedtls/library/psa_crypto_cipher.h +316 -0
- package/vendor/mbedtls/library/psa_crypto_client.c +22 -0
- package/vendor/mbedtls/library/psa_crypto_core.h +983 -0
- package/vendor/mbedtls/library/psa_crypto_core_common.h +52 -0
- package/vendor/mbedtls/library/psa_crypto_driver_wrappers.h +2896 -0
- package/vendor/mbedtls/library/psa_crypto_driver_wrappers_no_static.c +256 -0
- package/vendor/mbedtls/library/psa_crypto_driver_wrappers_no_static.h +31 -0
- package/vendor/mbedtls/library/psa_crypto_ecp.c +594 -0
- package/vendor/mbedtls/library/psa_crypto_ecp.h +267 -0
- package/vendor/mbedtls/library/psa_crypto_ffdh.c +361 -0
- package/vendor/mbedtls/library/psa_crypto_ffdh.h +131 -0
- package/vendor/mbedtls/library/psa_crypto_hash.c +470 -0
- package/vendor/mbedtls/library/psa_crypto_hash.h +211 -0
- package/vendor/mbedtls/library/psa_crypto_invasive.h +92 -0
- package/vendor/mbedtls/library/psa_crypto_its.h +131 -0
- package/vendor/mbedtls/library/psa_crypto_mac.c +505 -0
- package/vendor/mbedtls/library/psa_crypto_mac.h +264 -0
- package/vendor/mbedtls/library/psa_crypto_pake.c +571 -0
- package/vendor/mbedtls/library/psa_crypto_pake.h +159 -0
- package/vendor/mbedtls/library/psa_crypto_random.c +181 -0
- package/vendor/mbedtls/library/psa_crypto_random.h +72 -0
- package/vendor/mbedtls/library/psa_crypto_random_impl.h +200 -0
- package/vendor/mbedtls/library/psa_crypto_rsa.c +714 -0
- package/vendor/mbedtls/library/psa_crypto_rsa.h +321 -0
- package/vendor/mbedtls/library/psa_crypto_se.c +373 -0
- package/vendor/mbedtls/library/psa_crypto_se.h +192 -0
- package/vendor/mbedtls/library/psa_crypto_slot_management.c +1137 -0
- package/vendor/mbedtls/library/psa_crypto_slot_management.h +344 -0
- package/vendor/mbedtls/library/psa_crypto_storage.c +481 -0
- package/vendor/mbedtls/library/psa_crypto_storage.h +392 -0
- package/vendor/mbedtls/library/psa_its_file.c +254 -0
- package/vendor/mbedtls/library/psa_util.c +614 -0
- package/vendor/mbedtls/library/psa_util_internal.h +100 -0
- package/vendor/mbedtls/library/ripemd160.c +490 -0
- package/vendor/mbedtls/library/rsa.c +3121 -0
- package/vendor/mbedtls/library/rsa_alt_helpers.c +455 -0
- package/vendor/mbedtls/library/rsa_alt_helpers.h +209 -0
- package/vendor/mbedtls/library/rsa_internal.h +188 -0
- package/vendor/mbedtls/library/sha1.c +480 -0
- package/vendor/mbedtls/library/sha256.c +983 -0
- package/vendor/mbedtls/library/sha3.c +721 -0
- package/vendor/mbedtls/library/sha512.c +1115 -0
- package/vendor/mbedtls/library/ssl_cache.c +410 -0
- package/vendor/mbedtls/library/ssl_ciphersuites.c +2050 -0
- package/vendor/mbedtls/library/ssl_ciphersuites_internal.h +154 -0
- package/vendor/mbedtls/library/ssl_client.c +1023 -0
- package/vendor/mbedtls/library/ssl_client.h +22 -0
- package/vendor/mbedtls/library/ssl_cookie.c +384 -0
- package/vendor/mbedtls/library/ssl_debug_helpers.h +85 -0
- package/vendor/mbedtls/library/ssl_debug_helpers_generated.c +251 -0
- package/vendor/mbedtls/library/ssl_misc.h +3198 -0
- package/vendor/mbedtls/library/ssl_msg.c +6619 -0
- package/vendor/mbedtls/library/ssl_ticket.c +556 -0
- package/vendor/mbedtls/library/ssl_tls.c +10234 -0
- package/vendor/mbedtls/library/ssl_tls12_client.c +3688 -0
- package/vendor/mbedtls/library/ssl_tls12_server.c +4311 -0
- package/vendor/mbedtls/library/ssl_tls13_client.c +3208 -0
- package/vendor/mbedtls/library/ssl_tls13_generic.c +1793 -0
- package/vendor/mbedtls/library/ssl_tls13_invasive.h +23 -0
- package/vendor/mbedtls/library/ssl_tls13_keys.c +1918 -0
- package/vendor/mbedtls/library/ssl_tls13_keys.h +668 -0
- package/vendor/mbedtls/library/ssl_tls13_server.c +3624 -0
- package/vendor/mbedtls/library/threading.c +193 -0
- package/vendor/mbedtls/library/threading_internal.h +28 -0
- package/vendor/mbedtls/library/timing.c +152 -0
- package/vendor/mbedtls/library/version.c +32 -0
- package/vendor/mbedtls/library/version_features.c +856 -0
- package/vendor/mbedtls/library/x509.c +1776 -0
- package/vendor/mbedtls/library/x509_create.c +570 -0
- package/vendor/mbedtls/library/x509_crl.c +708 -0
- package/vendor/mbedtls/library/x509_crt.c +3311 -0
- package/vendor/mbedtls/library/x509_csr.c +648 -0
- package/vendor/mbedtls/library/x509_internal.h +101 -0
- package/vendor/mbedtls/library/x509write.c +174 -0
- package/vendor/mbedtls/library/x509write_crt.c +688 -0
- package/vendor/mbedtls/library/x509write_csr.c +336 -0
- package/vendor/quickjs-ng/CMakeLists.txt +566 -0
- package/vendor/quickjs-ng/LICENSE +24 -0
- package/vendor/quickjs-ng/README.md +24 -0
- package/vendor/quickjs-ng/api-test.c +1195 -0
- package/vendor/quickjs-ng/builtin-array-fromasync.h +119 -0
- package/vendor/quickjs-ng/builtin-iterator-zip-keyed.h +332 -0
- package/vendor/quickjs-ng/builtin-iterator-zip.h +337 -0
- package/vendor/quickjs-ng/cutils.h +1998 -0
- package/vendor/quickjs-ng/dtoa.c +1619 -0
- package/vendor/quickjs-ng/dtoa.h +87 -0
- package/vendor/quickjs-ng/gen/function_source.c +81 -0
- package/vendor/quickjs-ng/gen/hello.c +53 -0
- package/vendor/quickjs-ng/gen/hello_module.c +106 -0
- package/vendor/quickjs-ng/gen/repl.c +3036 -0
- package/vendor/quickjs-ng/gen/standalone.c +323 -0
- package/vendor/quickjs-ng/gen/test_fib.c +81 -0
- package/vendor/quickjs-ng/libregexp-opcode.h +73 -0
- package/vendor/quickjs-ng/libregexp.c +3474 -0
- package/vendor/quickjs-ng/libregexp.h +101 -0
- package/vendor/quickjs-ng/libunicode-table.h +5173 -0
- package/vendor/quickjs-ng/libunicode.c +2069 -0
- package/vendor/quickjs-ng/libunicode.h +172 -0
- package/vendor/quickjs-ng/list.h +107 -0
- package/vendor/quickjs-ng/lre-test.c +52 -0
- package/vendor/quickjs-ng/qjs.c +762 -0
- package/vendor/quickjs-ng/qjsc.c +673 -0
- package/vendor/quickjs-ng/quickjs-atom.h +280 -0
- package/vendor/quickjs-ng/quickjs-c-atomics.h +54 -0
- package/vendor/quickjs-ng/quickjs-libc.c +5037 -0
- package/vendor/quickjs-ng/quickjs-libc.h +87 -0
- package/vendor/quickjs-ng/quickjs-opcode.h +377 -0
- package/vendor/quickjs-ng/quickjs.c +64041 -0
- package/vendor/quickjs-ng/quickjs.h +1447 -0
- package/vendor/quickjs-ng/run-test262.c +2374 -0
- package/vendor/quickjs-ng/unicode_gen.c +3121 -0
- package/vendor/quickjs-ng/unicode_gen_def.h +310 -0
- package/vendor/ryu/LICENSE-Boost +23 -0
- package/vendor/ryu/common.h +114 -0
- package/vendor/ryu/d2s.c +509 -0
- package/vendor/ryu/d2s_full_table.h +367 -0
- package/vendor/ryu/d2s_intrinsics.h +357 -0
- package/vendor/ryu/d2s_small_table.h +186 -0
- package/vendor/ryu/digit_table.h +35 -0
- package/vendor/ryu/ryu.h +46 -0
- package/vendor/zlib/LICENSE +22 -0
- package/vendor/zlib/adler32.c +164 -0
- package/vendor/zlib/compress.c +75 -0
- package/vendor/zlib/crc32.c +1049 -0
- package/vendor/zlib/crc32.h +9446 -0
- package/vendor/zlib/deflate.c +2139 -0
- package/vendor/zlib/deflate.h +377 -0
- package/vendor/zlib/gzclose.c +23 -0
- package/vendor/zlib/gzguts.h +214 -0
- package/vendor/zlib/gzlib.c +582 -0
- package/vendor/zlib/gzread.c +602 -0
- package/vendor/zlib/gzwrite.c +631 -0
- package/vendor/zlib/infback.c +628 -0
- package/vendor/zlib/inffast.c +320 -0
- package/vendor/zlib/inffast.h +11 -0
- package/vendor/zlib/inffixed.h +94 -0
- package/vendor/zlib/inflate.c +1526 -0
- package/vendor/zlib/inflate.h +126 -0
- package/vendor/zlib/inftrees.c +299 -0
- package/vendor/zlib/inftrees.h +62 -0
- package/vendor/zlib/trees.c +1117 -0
- package/vendor/zlib/trees.h +128 -0
- package/vendor/zlib/uncompr.c +85 -0
- package/vendor/zlib/zconf.h +543 -0
- package/vendor/zlib/zlib.h +1938 -0
- package/vendor/zlib/zutil.c +299 -0
- package/vendor/zlib/zutil.h +254 -0
- package/README.md +0 -3
- 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
|
+
}
|