diff --git a/Cargo.lock b/Cargo.lock index adc585a..c704367 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,33 +4,24 @@ version = 4 [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ - "gimli 0.31.1", -] - -[[package]] -name = "addr2line" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9acbfca36652500c911ddb767ed433e3ed99b032b5d935be73c6923662db1d43" -dependencies = [ - "gimli 0.32.2", + "gimli", ] [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -73,9 +64,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.17" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -88,62 +79,62 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.9" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", - "windows-sys 0.59.0", + "once_cell_polyfill", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" dependencies = [ "backtrace", ] [[package]] name = "arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" [[package]] name = "assert_cmd" -version = "2.0.16" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" +checksum = "bcbb6924530aa9e0432442af08bbcafdad182db80d2e560da42a6d442535bf85" dependencies = [ "anstyle", "bstr", - "doc-comment", "libc", "predicates", "predicates-core", @@ -153,31 +144,34 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.23" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" +checksum = "5a89bce6054c720275ac2432fbba080a66a2106a44a1b804553930ca6909f4e0" dependencies = [ - "brotli", - "flate2", + "compression-codecs", + "compression-core", "futures-core", - "memchr", "pin-project-lite", "tokio", - "zstd", - "zstd-safe", ] [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "auditable-serde" version = "0.8.0" @@ -192,23 +186,23 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ - "addr2line 0.24.2", + "addr2line", "cfg-if", "libc", "miniz_oxide", - "object 0.36.5", + "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -228,24 +222,24 @@ dependencies = [ [[package]] name = "bit-set" -version = "0.5.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.6.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitmaps" @@ -267,9 +261,9 @@ dependencies = [ [[package]] name = "brotli" -version = "8.0.0" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf19e729cdbd51af9a397fb9ef8ac8378007b797f8273cfbfdf45dcaa316167b" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -288,9 +282,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", "regex-automata", @@ -299,30 +293,24 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" dependencies = [ "allocator-api2", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" -version = "1.8.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cap-fs-ext" -version = "3.4.4" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e41cc18551193fe8fa6f15c1e3c799bc5ec9e2cfbfaa8ed46f37013e3e6c173c" +checksum = "d5528f85b1e134ae811704e41ef80930f56e795923f866813255bc342cc20654" dependencies = [ "cap-primitives", "cap-std", @@ -332,21 +320,21 @@ dependencies = [ [[package]] name = "cap-net-ext" -version = "3.4.4" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f83833816c66c986e913b22ac887cec216ea09301802054316fc5301809702c" +checksum = "20a158160765c6a7d0d8c072a53d772e4cb243f38b04bfcf6b4939cfbe7482e7" dependencies = [ "cap-primitives", "cap-std", - "rustix 1.0.7", + "rustix 1.1.2", "smallvec", ] [[package]] name = "cap-primitives" -version = "3.4.4" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e394ed14f39f8bc26f59d4c0c010dbe7f0a1b9bafff451b1f98b67c8af62a" +checksum = "b6cf3aea8a5081171859ef57bc1606b1df6999df4f1110f8eef68b30098d1d3a" dependencies = [ "ambient-authority", "fs-set-times", @@ -354,7 +342,7 @@ dependencies = [ "io-lifetimes", "ipnet", "maybe-owned", - "rustix 1.0.7", + "rustix 1.1.2", "rustix-linux-procfs", "windows-sys 0.59.0", "winx", @@ -362,9 +350,9 @@ dependencies = [ [[package]] name = "cap-rand" -version = "3.4.4" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0acb89ccf798a28683f00089d0630dfaceec087234eae0d308c05ddeaa941b40" +checksum = "d8144c22e24bbcf26ade86cb6501a0916c46b7e4787abdb0045a467eb1645a1d" dependencies = [ "ambient-authority", "rand 0.8.5", @@ -372,36 +360,37 @@ dependencies = [ [[package]] name = "cap-std" -version = "3.4.4" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c0355ca583dd58f176c3c12489d684163861ede3c9efa6fd8bba314c984189" +checksum = "b6dc3090992a735d23219de5c204927163d922f42f575a0189b005c62d37549a" dependencies = [ "cap-primitives", "io-extras", "io-lifetimes", - "rustix 1.0.7", + "rustix 1.1.2", ] [[package]] name = "cap-time-ext" -version = "3.4.4" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "491af520b8770085daa0466978c75db90368c71896523f2464214e38359b1a5b" +checksum = "def102506ce40c11710a9b16e614af0cde8e76ae51b1f48c04b8d79f4b671a80" dependencies = [ "ambient-authority", "cap-primitives", "iana-time-zone", "once_cell", - "rustix 1.0.7", + "rustix 1.1.2", "winx", ] [[package]] name = "cc" -version = "1.2.19" +version = "1.2.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -409,9 +398,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -421,9 +410,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clap" -version = "4.5.20" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive", @@ -431,9 +420,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -443,11 +432,11 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn", @@ -455,38 +444,41 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "cobs" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.17", +] [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "component-init-transform" version = "0.2.0" -source = "git+https://github.com/dicej/component-init?rev=3b9680fe#3b9680fe652421d4d80e3d00c93d8b5eecb8acdc" +source = "git+https://github.com/dicej/component-init?rev=2d965957#2d96595716615426e26b4346003a29f557e2f31b" dependencies = [ "anyhow", "async-trait", "futures", - "wasm-encoder 0.240.0", - "wasm-metadata 0.240.0", - "wasmparser 0.240.0", + "wasm-encoder 0.240.0 (git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59)", + "wasm-metadata 0.240.0 (git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59)", + "wasmparser 0.240.0 (git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59)", ] [[package]] name = "componentize-py" -version = "0.18.0" +version = "0.19.0" dependencies = [ "anyhow", "assert_cmd", @@ -499,7 +491,7 @@ dependencies = [ "flate2", "fs_extra", "futures", - "heck", + "heck 0.5.0", "hex", "im-rc", "indexmap", @@ -515,14 +507,14 @@ dependencies = [ "tempfile", "test-generator", "tokio", - "toml", - "wasm-encoder 0.240.0", - "wasmparser 0.240.0", + "toml 0.8.23", + "wasm-encoder 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", + "wasmparser 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", "wasmtime", "wasmtime-wasi", "wit-component 0.240.0", "wit-dylib", - "wit-parser 0.240.0", + "wit-parser 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", "zstd", ] @@ -539,6 +531,26 @@ dependencies = [ "wit-dylib-ffi", ] +[[package]] +name = "compression-codecs" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8a506ec4b81c460798f572caead636d57d3d7e940f998160f52bd254bf2d23" +dependencies = [ + "brotli", + "compression-core", + "flate2", + "memchr", + "zstd", + "zstd-safe", +] + +[[package]] +name = "compression-core" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -547,54 +559,50 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpp_demangle" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d" +checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253" dependencies = [ "cfg-if", ] [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] [[package]] name = "cranelift-assembler-x64" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e2df3d5caad11e71bb0b70115a5210c3af4a0bcb2893f78ee9311b1b266b05" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "cranelift-assembler-x64-meta", ] [[package]] name = "cranelift-assembler-x64-meta" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63e87985fc9166a2541b05fd5f913a398cff9aec6b13ebca865253cdee15806" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "cranelift-srcgen", ] [[package]] name = "cranelift-bforest" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b5364dfe182d4b89af2f4bd0dafc8f6c590bbf0216ee8ce60bfd8893c3d14a6" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3067ca8c10796434497a5faac73d949b5ac0008ed572013debe88694bfef426e" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "serde", "serde_derive", @@ -602,9 +610,8 @@ dependencies = [ [[package]] name = "cranelift-codegen" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb420cc46d7f0956e2e3d9e6389036c612ada3542a29edc6f5deedf86d568ba" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "bumpalo", "cranelift-assembler-x64", @@ -615,8 +622,8 @@ dependencies = [ "cranelift-control", "cranelift-entity", "cranelift-isle", - "gimli 0.32.2", - "hashbrown", + "gimli", + "hashbrown 0.15.5", "log", "pulley-interpreter", "regalloc2", @@ -629,37 +636,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "440d31dd36e477fb6292821b593da65df60328bca1046ea5881f424aa5a44b5d" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "cranelift-assembler-x64-meta", "cranelift-codegen-shared", "cranelift-srcgen", - "heck", + "heck 0.5.0", "pulley-interpreter", ] [[package]] name = "cranelift-codegen-shared" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b342ef4835787577f6e7553747cdd902797509eb5af733cd89e5ce97cea0f0" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" [[package]] name = "cranelift-control" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34de54534b61c3f3e475558cf19c90b2a7a758c7018e557e5d1a47b9a1fbb03" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d7bf1aae1800d053aa965381dcb01054404d0bcd8ea5ffe65bb855b8e3f654" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "cranelift-bitset", "serde", @@ -668,9 +671,8 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36158c03d70e1f443cc2d6d9adc838fc0a031b166f3861534e9cb77742380e2a" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "cranelift-codegen", "log", @@ -680,15 +682,13 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa194bbc189c965454f3a94c1acb6c89d63d5d0b183e60edc17db758bfbe519" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" [[package]] name = "cranelift-native" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c469bb98ffe9f38a1a5ada0427ab096f0f1b9a22a30149fc705205c56cf8985" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "cranelift-codegen", "libc", @@ -697,24 +697,23 @@ dependencies = [ [[package]] name = "cranelift-srcgen" -version = "0.124.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12d694cd4c6b28fb8a4d0cf5b58d532b6b3b6e4afb2b65603e2ab8dc35bf18bd" +version = "0.126.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -731,9 +730,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" @@ -792,16 +791,21 @@ dependencies = [ ] [[package]] -name = "doc-comment" -version = "0.3.3" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "embedded-io" @@ -839,18 +843,18 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -861,38 +865,50 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fd-lock" -version = "4.0.2" +version = "4.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", - "rustix 0.38.44", - "windows-sys 0.52.0", + "rustix 1.1.2", + "windows-sys 0.59.0", ] [[package]] name = "filetime" -version = "0.2.25" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -900,9 +916,9 @@ dependencies = [ [[package]] name = "float-cmp" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" dependencies = [ "num-traits", ] @@ -915,28 +931,28 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] [[package]] name = "fs-set-times" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" +checksum = "94e7099f6313ecacbe1256e8ff9d617b75d1bcb16a6fddef94866d225a01a14a" dependencies = [ "io-lifetimes", - "rustix 0.38.44", - "windows-sys 0.52.0", + "rustix 1.1.2", + "windows-sys 0.59.0", ] [[package]] @@ -1034,33 +1050,25 @@ dependencies = [ "slab", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - [[package]] name = "fxprof-processed-profile" -version = "0.6.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" +checksum = "25234f20a3ec0a962a61770cfe39ecf03cb529a6e474ad8cff025ed497eda557" dependencies = [ "bitflags", "debugid", - "fxhash", + "rustc-hash", "serde", + "serde_derive", "serde_json", ] [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -1068,42 +1076,36 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.7+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] [[package]] name = "gimli" -version = "0.31.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - -[[package]] -name = "gimli" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6298e594375a7fead9efd5568f0a46e6a154fb6a9bdcbe3c06946ffd81a5f6" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" dependencies = [ "fallible-iterator", "indexmap", @@ -1112,31 +1114,37 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "foldhash", "serde", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "heck" -version = "0.5.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] -name = "hermit-abi" -version = "0.3.9" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -1186,24 +1194,26 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "humantime" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "http", "http-body", "httparse", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -1211,11 +1221,10 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", "http", "hyper", "hyper-util", @@ -1224,24 +1233,28 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 0.26.11", + "webpki-roots", ] [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ + "base64", "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", - "socket2 0.5.9", + "socket2", "tokio", "tower-service", "tracing", @@ -1249,14 +1262,15 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -1270,6 +1284,87 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "id-arena" version = "2.2.1" @@ -1278,12 +1373,23 @@ checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" [[package]] name = "idna" -version = "0.5.0" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", ] [[package]] @@ -1302,20 +1408,24 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.8.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.0", "serde", + "serde_core", ] [[package]] name = "indoc" -version = "2.0.5" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] [[package]] name = "io-extras" @@ -1329,43 +1439,42 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" +checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" [[package]] -name = "io-uring" -version = "0.7.10" +name = "ipnet" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] -name = "ipnet" -version = "2.10.1" +name = "iri-string" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ - "hermit-abi 0.4.0", + "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -1404,29 +1513,24 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "leb128" version = "0.2.5" @@ -1441,21 +1545,21 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libm" -version = "0.2.8" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ "bitflags", "libc", @@ -1464,21 +1568,27 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "lru-slab" @@ -1488,9 +1598,9 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "mach2" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" dependencies = [ "libc", ] @@ -1503,17 +1613,17 @@ checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memfd" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" dependencies = [ - "rustix 0.38.44", + "rustix 1.1.2", ] [[package]] @@ -1525,31 +1635,25 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ - "hermit-abi 0.3.9", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi", + "windows-sys 0.61.2", ] [[package]] @@ -1584,16 +1688,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", -] - -[[package]] -name = "object" -version = "0.36.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" -dependencies = [ - "memchr", ] [[package]] @@ -1603,7 +1697,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "crc32fast", - "hashbrown", + "hashbrown 0.15.5", "indexmap", "memchr", ] @@ -1614,17 +1708,33 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "petgraph" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1634,21 +1744,21 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "portable-atomic" -version = "1.9.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "postcard" -version = "1.0.10" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" dependencies = [ "cobs", "embedded-io 0.4.0", @@ -1656,20 +1766,29 @@ dependencies = [ "serde", ] +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "predicates" -version = "3.1.2" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ "anstyle", "difflib", @@ -1681,15 +1800,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", @@ -1707,9 +1826,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.30" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1ccf34da56fc294e7d4ccf69a85992b7dfb826b7cf57bac6a70bba3494cc08a" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", "syn", @@ -1717,26 +1836,25 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.5.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set", "bit-vec", "bitflags", - "lazy_static", "num-traits", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand 0.9.2", + "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", "rusty-fork", @@ -1746,9 +1864,8 @@ dependencies = [ [[package]] name = "pulley-interpreter" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cff3cc2c3a933419d4989b9dcdee724ebc9ee4cdc1f175dbaeef71d3b963336" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "cranelift-bitset", "log", @@ -1758,9 +1875,8 @@ dependencies = [ [[package]] name = "pulley-macros" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56fca4a86a359a71f35f188de13bdfa9dc5b56a36d236fa98cb0db601ef4d21" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "proc-macro2", "quote", @@ -1822,7 +1938,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "100246c0ecf400b475341b8455a9213344569af29a3c841d29270e53102e0fcf" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "pyo3-build-config", "quote", @@ -1848,8 +1964,8 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.0", - "thiserror 2.0.12", + "socket2", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -1862,7 +1978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", - "getrandom 0.3.3", + "getrandom 0.3.4", "lru-slab", "rand 0.9.2", "ring", @@ -1870,7 +1986,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -1885,16 +2001,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.0", + "socket2", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -1952,7 +2068,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -1961,16 +2077,16 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] name = "rand_xorshift" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.6.4", + "rand_core 0.9.3", ] [[package]] @@ -1984,9 +2100,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -1994,9 +2110,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -2004,9 +2120,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags", ] @@ -2017,9 +2133,9 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", - "thiserror 1.0.65", + "thiserror 1.0.69", ] [[package]] @@ -2030,7 +2146,7 @@ checksum = "efd8138ce7c3d7c13be4f61893154b5d711bd798d2d7be3ecb8dcc7e7a06ca98" dependencies = [ "allocator-api2", "bumpalo", - "hashbrown", + "hashbrown 0.15.5", "log", "rustc-hash", "smallvec", @@ -2038,9 +2154,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -2050,9 +2166,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -2061,15 +2177,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "async-compression", "base64", @@ -2083,16 +2199,12 @@ dependencies = [ "hyper", "hyper-rustls", "hyper-util", - "ipnet", "js-sys", "log", - "mime", - "once_cell", "percent-encoding", "pin-project-lite", "quinn", "rustls", - "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", @@ -2102,13 +2214,13 @@ dependencies = [ "tokio-rustls", "tokio-util", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.26.11", - "windows-registry", + "webpki-roots", ] [[package]] @@ -2119,7 +2231,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -2127,15 +2239,15 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustix" @@ -2146,21 +2258,21 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.4.14", + "linux-raw-sys 0.4.15", "windows-sys 0.59.0", ] [[package]] name = "rustix" -version = "1.0.7" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", ] [[package]] @@ -2170,14 +2282,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fc84bf7e9aa16c4f2c758f27412dc9841341e16aa682d9c7ac308fe3ee12056" dependencies = [ "once_cell", - "rustix 1.0.7", + "rustix 1.1.2", ] [[package]] name = "rustls" -version = "0.23.26" +version = "0.23.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7" dependencies = [ "once_cell", "ring", @@ -2187,29 +2299,21 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "web-time", + "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", @@ -2218,15 +2322,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -2236,33 +2340,44 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "semver" -version = "1.0.23" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ "serde", + "serde_core", ] [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -2271,25 +2386,35 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +dependencies = [ + "serde_core", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2302,11 +2427,24 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -2319,6 +2457,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "sized-chunks" version = "0.6.5" @@ -2331,56 +2475,43 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" dependencies = [ "serde", ] [[package]] name = "socket2" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "spdx" -version = "0.10.6" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47317bbaf63785b53861e1ae2d11b80d6b624211d42cb20efcd210ee6f8a14bc" +checksum = "c3e17e880bafaeb362a7b751ec46bdc5b61445a188f80e0606e68167cd540fa3" dependencies = [ "smallvec", ] [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "strsim" @@ -2396,9 +2527,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", @@ -2414,11 +2545,22 @@ dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "system-interface" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b858526d22750088a9b3cf2e3c2aacebd5377f13adeec02860c30d09113010a6" +checksum = "cc4592f674ce18521c2a81483873a49596655b179f71c5e05d10c1fe66c78745" dependencies = [ "bitflags", "cap-fs-ext", @@ -2426,15 +2568,15 @@ dependencies = [ "fd-lock", "io-lifetimes", "rustix 0.38.44", - "windows-sys 0.52.0", + "windows-sys 0.59.0", "winx", ] [[package]] name = "tar" -version = "0.4.42" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", @@ -2443,21 +2585,21 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ - "cfg-if", "fastrand", + "getrandom 0.3.4", "once_cell", - "rustix 0.38.44", - "windows-sys 0.59.0", + "rustix 1.1.2", + "windows-sys 0.61.2", ] [[package]] @@ -2471,43 +2613,43 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "test-generator" version = "0.1.0" dependencies = [ "anyhow", - "getrandom 0.2.15", + "getrandom 0.2.16", "hex", "proptest", ] [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.65", + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -2516,20 +2658,30 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -2542,27 +2694,24 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "pin-project-lite", - "slab", - "socket2 0.6.0", + "socket2", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -2571,9 +2720,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -2581,9 +2730,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -2594,38 +2743,84 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.19" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit", ] +[[package]] +name = "toml" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.0.3", + "toml_datetime 0.7.3", + "toml_parser", + "toml_writer", + "winnow", +] + [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" + [[package]] name = "topological-sort" version = "0.2.2" @@ -2647,6 +2842,24 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -2661,9 +2874,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -2672,9 +2885,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -2683,9 +2896,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] @@ -2698,9 +2911,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unarray" @@ -2708,32 +2921,17 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] +checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -2743,9 +2941,15 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unindent" -version = "0.2.3" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" @@ -2755,15 +2959,22 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -2772,9 +2983,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "version_check" @@ -2784,9 +2999,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] @@ -2802,18 +3017,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" @@ -2826,35 +3032,22 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", @@ -2865,9 +3058,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2875,26 +3068,47 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-compose" +version = "0.240.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feeb9a231e63bd5d5dfe07e9f8daa53d5c85e4f7de5ef756d3b4e6a5f501c578" +dependencies = [ + "anyhow", + "heck 0.4.1", + "im-rc", + "indexmap", + "log", + "petgraph", + "serde", + "serde_derive", + "serde_yaml", + "smallvec", + "wasm-encoder 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wat", +] + [[package]] name = "wasm-encoder" version = "0.227.1" @@ -2907,12 +3121,21 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.239.0" +version = "0.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c" +checksum = "06d642d8c5ecc083aafe9ceb32809276a304547a3a6eeecceb5d8152598bc71f" +dependencies = [ + "leb128fmt", + "wasmparser 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-encoder" +version = "0.240.0" +source = "git+https://github.com/dicej/wasm-tools?rev=b072b0ca#b072b0caa8307779558d96a62bc9522abda6a7fb" dependencies = [ "leb128fmt", - "wasmparser 0.239.0", + "wasmparser 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", ] [[package]] @@ -2921,7 +3144,7 @@ version = "0.240.0" source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" dependencies = [ "leb128fmt", - "wasmparser 0.240.0", + "wasmparser 0.240.0 (git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59)", ] [[package]] @@ -2943,6 +3166,17 @@ dependencies = [ "wasmparser 0.227.1", ] +[[package]] +name = "wasm-metadata" +version = "0.240.0" +source = "git+https://github.com/dicej/wasm-tools?rev=b072b0ca#b072b0caa8307779558d96a62bc9522abda6a7fb" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", + "wasmparser 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", +] + [[package]] name = "wasm-metadata" version = "0.240.0" @@ -2957,8 +3191,8 @@ dependencies = [ "serde_json", "spdx", "url", - "wasm-encoder 0.240.0", - "wasmparser 0.240.0", + "wasm-encoder 0.240.0 (git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59)", + "wasmparser 0.240.0 (git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59)", ] [[package]] @@ -2968,19 +3202,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2" dependencies = [ "bitflags", - "hashbrown", + "hashbrown 0.15.5", "indexmap", "semver", ] [[package]] name = "wasmparser" -version = "0.239.0" +version = "0.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" +checksum = "b722dcf61e0ea47440b53ff83ccb5df8efec57a69d150e4f24882e4eba7e24a4" dependencies = [ "bitflags", - "hashbrown", + "hashbrown 0.15.5", + "indexmap", + "semver", + "serde", +] + +[[package]] +name = "wasmparser" +version = "0.240.0" +source = "git+https://github.com/dicej/wasm-tools?rev=b072b0ca#b072b0caa8307779558d96a62bc9522abda6a7fb" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", "indexmap", "semver", "serde", @@ -2992,7 +3238,7 @@ version = "0.240.0" source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" dependencies = [ "bitflags", - "hashbrown", + "hashbrown 0.15.5", "indexmap", "semver", "serde", @@ -3000,55 +3246,56 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.239.0" +version = "0.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3981f3d51f39f24f5fc90f93049a90f08dbbca8deba602cd46bb8ca67a94718" +checksum = "a84d6e25c198da67d0150ee7c2c62d33d784f0a565d1e670bdf1eeccca8158bc" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.239.0", + "wasmparser 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmtime" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1092d79769b1f888940a572ed881eec18f837cb96201985eb5928226759ebc3e" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ - "addr2line 0.25.0", + "addr2line", "anyhow", "async-trait", "bitflags", "bumpalo", + "bytes", "cc", "cfg-if", "encoding_rs", "futures", "fxprof-processed-profile", - "gimli 0.32.2", - "hashbrown", + "gimli", + "hashbrown 0.15.5", "indexmap", "ittapi", "libc", "log", "mach2", "memfd", - "object 0.37.3", + "object", "once_cell", "postcard", "pulley-interpreter", "rayon", - "rustix 1.0.7", + "rustix 1.1.2", "semver", "serde", "serde_derive", "serde_json", "smallvec", "target-lexicon", - "wasm-encoder 0.239.0", - "wasmparser 0.239.0", + "tempfile", + "wasm-compose", + "wasm-encoder 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmtime-environ", - "wasmtime-internal-asm-macros", "wasmtime-internal-cache", "wasmtime-internal-component-macro", "wasmtime-internal-component-util", @@ -3067,18 +3314,17 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "484db5c4dd06e96559217b60893b5dbb9fe3cd28489c83a4257b277c9ead959c" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", "cpp_demangle", "cranelift-bitset", "cranelift-entity", - "gimli 0.32.2", + "gimli", "indexmap", "log", - "object 0.37.3", + "object", "postcard", "rustc-demangle", "semver", @@ -3086,46 +3332,35 @@ dependencies = [ "serde_derive", "smallvec", "target-lexicon", - "wasm-encoder 0.239.0", - "wasmparser 0.239.0", + "wasm-encoder 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmprinter", "wasmtime-internal-component-util", ] -[[package]] -name = "wasmtime-internal-asm-macros" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d3903b8db592bb4f5e847bd6dc38f485791d4d9ebc9e2df167d3c7f072115c" -dependencies = [ - "cfg-if", -] - [[package]] name = "wasmtime-internal-cache" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab11f58f3b24a4a47d7d99b45ca22635c33cf26acdda1fe77612180e7285e2f" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", "base64", "directories-next", "log", "postcard", - "rustix 1.0.7", + "rustix 1.1.2", "serde", "serde_derive", "sha2", - "toml", + "toml 0.9.8", "windows-sys 0.60.2", "zstd", ] [[package]] name = "wasmtime-internal-component-macro" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c7d26d0c0d6da9f35dd56e3651a84bc4741646803e78886a3144aaf15c941e" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", "proc-macro2", @@ -3133,20 +3368,18 @@ dependencies = [ "syn", "wasmtime-internal-component-util", "wasmtime-internal-wit-bindgen", - "wit-parser 0.239.0", + "wit-parser 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmtime-internal-component-util" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb62647125f7a0833cefb9a8862bc7886d206fe4e7131201bddd8d3979abbb02" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" [[package]] name = "wasmtime-internal-cranelift" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d802c0a214c82fbf3e60ead87bf4d5dff7fc957c9a4449ceeae74bd89283ee57" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", "cfg-if", @@ -3155,15 +3388,15 @@ dependencies = [ "cranelift-entity", "cranelift-frontend", "cranelift-native", - "gimli 0.32.2", + "gimli", "itertools", "log", - "object 0.37.3", + "object", "pulley-interpreter", "smallvec", "target-lexicon", - "thiserror 2.0.12", - "wasmparser 0.239.0", + "thiserror 2.0.17", + "wasmparser 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmtime-environ", "wasmtime-internal-math", "wasmtime-internal-unwinder", @@ -3172,37 +3405,33 @@ dependencies = [ [[package]] name = "wasmtime-internal-fiber" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fdeb4c50dbbb3ebf01f7e6d270ba4239c8db5ff1e68725386abad2e5250be4" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", "cc", "cfg-if", "libc", - "rustix 1.0.7", - "wasmtime-internal-asm-macros", + "rustix 1.1.2", "wasmtime-internal-versioned-export-macros", "windows-sys 0.60.2", ] [[package]] name = "wasmtime-internal-jit-debug" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe059ecd3f572ec2f616f036e5f31a88649a5f16e4838147cb3af6c3b5cedd5" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "cc", - "object 0.37.3", - "rustix 1.0.7", + "object", + "rustix 1.1.2", "wasmtime-internal-versioned-export-macros", ] [[package]] name = "wasmtime-internal-jit-icache-coherence" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a03f55a9dbfa30f2ed269fa9735c2994b8423461d45c3ca08aa7a103daeff20" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", "cfg-if", @@ -3212,37 +3441,33 @@ dependencies = [ [[package]] name = "wasmtime-internal-math" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7f491d2c7f1be3f6e5485ab5a26f26f177860c8b5c16d3ab87df4b24f28e40" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "libm", ] [[package]] name = "wasmtime-internal-slab" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce79a933dd9c5fdcc1ce1f6b46e89e72ccb6767619557f0468d97fbfb0475db6" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" [[package]] name = "wasmtime-internal-unwinder" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0e12be7ff91e956c6e7cee17654982669493c4aaa861e3536f7b1c22999519d" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", "cfg-if", "cranelift-codegen", "log", - "object 0.37.3", + "object", ] [[package]] name = "wasmtime-internal-versioned-export-macros" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67d114c747409b6f76ddf86deb2c5976f809baed3eea8fdca493063ea182246b" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "proc-macro2", "quote", @@ -3251,17 +3476,16 @@ dependencies = [ [[package]] name = "wasmtime-internal-winch" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc9d9e984025efce72ec4b90b95790e857f2fef7601c317abedf61226e21585" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", "cranelift-codegen", - "gimli 0.32.2", + "gimli", "log", - "object 0.37.3", + "object", "target-lexicon", - "wasmparser 0.239.0", + "wasmparser 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmtime-environ", "wasmtime-internal-cranelift", "winch-codegen", @@ -3269,22 +3493,20 @@ dependencies = [ [[package]] name = "wasmtime-internal-wit-bindgen" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fb054d6661ef694d1d90e1c01233cb8ef0d7f84b73980709d3facef1373e189" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", "bitflags", - "heck", + "heck 0.5.0", "indexmap", - "wit-parser 0.239.0", + "wit-parser 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmtime-wasi" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6617d09fda0be82e11227418e5561c67a8469709215d7c05a9d9f4d2192dc7" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", "async-trait", @@ -3299,9 +3521,9 @@ dependencies = [ "futures", "io-extras", "io-lifetimes", - "rustix 1.0.7", + "rustix 1.1.2", "system-interface", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "url", @@ -3313,9 +3535,8 @@ dependencies = [ [[package]] name = "wasmtime-wasi-io" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdca5f5ac3e7814f184f44e49add8e99a3e92bc1ceb45c05ba013b2822b7b705" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", "async-trait", @@ -3335,31 +3556,31 @@ dependencies = [ [[package]] name = "wast" -version = "239.0.0" +version = "240.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9139176fe8a2590e0fb174cdcaf373b224cb93c3dde08e4297c1361d2ba1ea5d" +checksum = "b0efe1c93db4ac562b9733e3dca19ed7fc878dba29aef22245acf84f13da4a19" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width", - "wasm-encoder 0.239.0", + "wasm-encoder 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wat" -version = "1.239.0" +version = "1.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1c941927d34709f255558166f8901a2005f8ab4a9650432e9281b7cc6f3b75" +checksum = "4ec9b6eab7ecd4d639d78515e9ea491c9bacf494aa5eda10823bd35992cf8c1e" dependencies = [ - "wast 239.0.0", + "wast 240.0.0", ] [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", @@ -3377,32 +3598,21 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" -dependencies = [ - "webpki-roots 1.0.2", -] - -[[package]] -name = "webpki-roots" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" dependencies = [ "rustls-pki-types", ] [[package]] name = "wiggle" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e45c8d34846d01d20157c00cd61e207e4f4d8889cfc69b45d43e5114834e71e" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", - "async-trait", "bitflags", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "wasmtime", "wiggle-macro", @@ -3410,12 +3620,11 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772a63dfce3a1bce1cc019720e43603633c5a2cb84291bec77f438ec38b55bf2" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn", @@ -3424,9 +3633,8 @@ dependencies = [ [[package]] name = "wiggle-macro" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3636c0c0352f54b28f45d1327784176e134009db4230024be6509966c88adf74" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "proc-macro2", "quote", @@ -3452,11 +3660,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3467,19 +3675,18 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winch-codegen" -version = "37.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61aeea98b13a9d2f537b9aec71a168397aefd5a3faebdc6b3d96ff7df4592e59" +version = "39.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=efd56f68#efd56f682b6d1d37e43bf407947e8ee5406a837f" dependencies = [ "anyhow", "cranelift-assembler-x64", "cranelift-codegen", - "gimli 0.32.2", + "gimli", "regalloc2", "smallvec", "target-lexicon", - "thiserror 2.0.12", - "wasmparser 0.239.0", + "thiserror 2.0.17", + "wasmparser 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmtime-environ", "wasmtime-internal-cranelift", "wasmtime-internal-math", @@ -3487,44 +3694,59 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.52.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "windows-link" -version = "0.1.3" +name = "windows-implement" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "windows-registry" -version = "0.4.0" +name = "windows-interface" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ - "windows-result", - "windows-strings", - "windows-targets 0.53.3", + "proc-macro2", + "quote", + "syn", ] +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-result" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.3.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ "windows-link", ] @@ -3553,7 +3775,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", ] [[package]] @@ -3574,19 +3805,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -3597,9 +3828,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -3609,9 +3840,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -3621,9 +3852,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -3633,9 +3864,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -3645,9 +3876,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -3657,9 +3888,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -3669,9 +3900,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -3681,27 +3912,27 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.6.20" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] [[package]] name = "winx" -version = "0.36.3" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" +checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" dependencies = [ "bitflags", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3727,7 +3958,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "398c650cec1278cfb72e214ba26ef3440ab726e66401bd39c04f465ee3979e6b" dependencies = [ "anyhow", - "heck", + "heck 0.5.0", "wit-parser 0.227.1", ] @@ -3747,7 +3978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83903c8dcd8084a8a67ae08190122cf0e25dc37bdc239070a00f47e22d3f0aae" dependencies = [ "anyhow", - "heck", + "heck 0.5.0", "indexmap", "prettyplease", "syn", @@ -3793,7 +4024,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.240.0" -source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" +source = "git+https://github.com/dicej/wasm-tools?rev=b072b0ca#b072b0caa8307779558d96a62bc9522abda6a7fb" dependencies = [ "anyhow", "bitflags", @@ -3802,27 +4033,27 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.240.0", - "wasm-metadata 0.240.0", - "wasmparser 0.240.0", - "wit-parser 0.240.0", + "wasm-encoder 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", + "wasm-metadata 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", + "wasmparser 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", + "wit-parser 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", ] [[package]] name = "wit-dylib" version = "0.240.0" -source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" +source = "git+https://github.com/dicej/wasm-tools?rev=b072b0ca#b072b0caa8307779558d96a62bc9522abda6a7fb" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.240.0", - "wit-parser 0.240.0", + "wasm-encoder 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", + "wit-parser 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", ] [[package]] name = "wit-dylib-ffi" version = "0.1.0" -source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" +source = "git+https://github.com/dicej/wasm-tools?rev=b072b0ca#b072b0caa8307779558d96a62bc9522abda6a7fb" [[package]] name = "wit-parser" @@ -3844,9 +4075,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.239.0" +version = "0.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c92c939d667b7bf0c6bf2d1f67196529758f99a2a45a3355cc56964fd5315d" +checksum = "9875ea3fa272f57cc1fc50f225a7b94021a7878c484b33792bccad0d93223439" dependencies = [ "anyhow", "id-arena", @@ -3857,13 +4088,13 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.239.0", + "wasmparser 0.240.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wit-parser" version = "0.240.0" -source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" +source = "git+https://github.com/dicej/wasm-tools?rev=b072b0ca#b072b0caa8307779558d96a62bc9522abda6a7fb" dependencies = [ "anyhow", "id-arena", @@ -3874,7 +4105,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.240.0", + "wasmparser 0.240.0 (git+https://github.com/dicej/wasm-tools?rev=b072b0ca)", ] [[package]] @@ -3885,71 +4116,152 @@ checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" dependencies = [ "anyhow", "log", - "thiserror 1.0.65", + "thiserror 1.0.69", "wast 35.0.2", ] +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + [[package]] name = "xattr" -version = "1.3.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "linux-raw-sys 0.4.14", - "rustix 0.38.44", + "rustix 1.1.2", +] + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zstd" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.2.1" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 9ff0364..bc1561e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "componentize-py" -version = "0.18.0" +version = "0.19.0" edition = "2024" exclude = ["cpython"] @@ -14,12 +14,12 @@ clap = { version = "4.5.20", features = ["derive"] } tar = "0.4.42" tempfile = "3.13.0" zstd = "0.13.2" -# TODO: switch to wasm-tools 1.241.0 when available -wasm-encoder = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b1d8ff59" } -wit-dylib = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b1d8ff59" } -wit-parser = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b1d8ff59" } -wit-component = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b1d8ff59" } -wasmparser = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b1d8ff59" } +# TODO: switch to wasm-tools release once https://github.com/bytecodealliance/wasm-tools/pull/2367 has been merged and released +wasm-encoder = { git = "https://github.com/dicej/wasm-tools", rev = "b072b0ca" } +wit-dylib = { git = "https://github.com/dicej/wasm-tools", rev = "b072b0ca" } +wit-parser = { git = "https://github.com/dicej/wasm-tools", rev = "b072b0ca" } +wit-component = { git = "https://github.com/dicej/wasm-tools", rev = "b072b0ca" } +wasmparser = { git = "https://github.com/dicej/wasm-tools", rev = "b072b0ca" } indexmap = "2.6.0" bincode = "1.3.3" heck = "0.5.0" @@ -27,10 +27,11 @@ pyo3 = { version = "0.26.0", features = [ "abi3-py39", "extension-module", ], optional = true } -wasmtime = { version = "37.0.2", features = [ "component-model-async" ] } -wasmtime-wasi = "37.0.2" +# TODO: switch to wasmtime 39.0.x when available +wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "efd56f68", features = [ "component-model-async" ] } +wasmtime-wasi = { git = "https://github.com/bytecodealliance/wasmtime", rev = "efd56f68", features = [ "p3" ] } once_cell = "1.20.2" -component-init-transform = { git = "https://github.com/dicej/component-init", rev = "3b9680fe" } +component-init-transform = { git = "https://github.com/dicej/component-init", rev = "2d965957" } async-trait = "0.1.83" futures = "0.3.31" tokio = { version = "1.41.0", features = [ diff --git a/build.rs b/build.rs index 62b19ef..3b986f7 100644 --- a/build.rs +++ b/build.rs @@ -52,7 +52,8 @@ fn stubs_for_clippy(out_dir: &Path) -> Result<()> { ); let files = [ - "libcomponentize_py_runtime.so.zst", + "libcomponentize_py_runtime_sync.so.zst", + "libcomponentize_py_runtime_async.so.zst", "libpython3.14.so.zst", "libc.so.zst", "libwasi-emulated-mman.so.zst", @@ -103,62 +104,20 @@ fn package_all_the_things(out_dir: &Path) -> Result<()> { make_pyo3_config(&repo_dir)?; - let mut cmd = Command::new("rustup"); - cmd.current_dir("runtime") - .arg("run") - .arg("nightly") - .arg("cargo") - .arg("build") - .arg("-Z") - .arg("build-std=panic_abort,std") - .arg("--release") - .arg("--target=wasm32-wasip1"); - - for (key, _) in env::vars_os() { - if key - .to_str() - .map(|key| key.starts_with("RUST") || key.starts_with("CARGO")) - .unwrap_or(false) - { - cmd.env_remove(&key); - } - } - - cmd.env( - "RUSTFLAGS", - "-C relocation-model=pic --cfg pyo3_disable_reference_pool", - ) - .env("CARGO_TARGET_DIR", out_dir) - .env("PYO3_CONFIG_FILE", out_dir.join("pyo3-config.txt")); - - let status = cmd.status()?; - assert!(status.success()); - println!("cargo:rerun-if-changed=runtime"); - - let path = out_dir.join("wasm32-wasip1/release/libcomponentize_py_runtime.a"); - - if path.exists() { - let clang = wasi_sdk.join(format!("bin/{CLANG_EXECUTABLE}")); - if clang.exists() { - let name = "libcomponentize_py_runtime.so"; - - run(Command::new(clang) - .arg("-shared") - .arg("-o") - .arg(out_dir.join(name)) - .arg("-Wl,--whole-archive") - .arg(&path) - .arg("-Wl,--no-whole-archive") - .arg(format!("-L{}", cpython_wasi_dir.to_str().unwrap())) - .arg("-lpython3.14"))?; - - compress(out_dir, name, out_dir, false)?; - } else { - bail!("no such file: {}", clang.display()) - } - } else { - bail!("no such file: {}", path.display()) - } + make_runtime( + out_dir, + &wasi_sdk, + &cpython_wasi_dir, + false, + "libcomponentize_py_runtime_sync.so", + )?; + make_runtime( + out_dir, + &wasi_sdk, + &cpython_wasi_dir, + true, + "libcomponentize_py_runtime_async.so", + )?; let libraries = [ "libc.so", @@ -406,6 +365,79 @@ fn make_pyo3_config(repo_dir: &Path) -> Result<()> { Ok(()) } +fn make_runtime( + out_dir: &Path, + wasi_sdk: &Path, + cpython_wasi_dir: &Path, + async_: bool, + name: &str, +) -> Result<()> { + let mut cmd = Command::new("rustup"); + cmd.current_dir("runtime") + .arg("run") + .arg("nightly") + .arg("cargo") + .arg("build") + .arg("-Z") + .arg("build-std=panic_abort,std") + .arg("--release") + .arg("--target=wasm32-wasip1"); + + if async_ { + cmd.arg("--features=async"); + } + + for (key, _) in env::vars_os() { + if key + .to_str() + .map(|key| key.starts_with("RUST") || key.starts_with("CARGO")) + .unwrap_or(false) + { + cmd.env_remove(&key); + } + } + + let target = if async_ { "async" } else { "sync" }; + + cmd.env( + "RUSTFLAGS", + "-C relocation-model=pic --cfg pyo3_disable_reference_pool", + ) + .env("CARGO_TARGET_DIR", out_dir.join(target)) + .env("PYO3_CONFIG_FILE", out_dir.join("pyo3-config.txt")); + + let status = cmd.status()?; + assert!(status.success()); + println!("cargo:rerun-if-changed=runtime"); + + let path = out_dir + .join(target) + .join("wasm32-wasip1/release/libcomponentize_py_runtime.a"); + + if path.exists() { + let clang = wasi_sdk.join(format!("bin/{CLANG_EXECUTABLE}")); + if clang.exists() { + run(Command::new(clang) + .arg("-shared") + .arg("-o") + .arg(out_dir.join(name)) + .arg("-Wl,--whole-archive") + .arg(&path) + .arg("-Wl,--no-whole-archive") + .arg(format!("-L{}", cpython_wasi_dir.to_str().unwrap())) + .arg("-lpython3.14"))?; + + compress(out_dir, name, out_dir, false)?; + } else { + bail!("no such file: {}", clang.display()) + } + } else { + bail!("no such file: {}", path.display()) + } + + Ok(()) +} + fn fetch_extract(url: &str, out_dir: &Path) -> Result<()> { let response = reqwest::blocking::get(url)?; let decoder = flate2::read::GzDecoder::new(response); diff --git a/bundled/componentize_py_async_support/__init__.py b/bundled/componentize_py_async_support/__init__.py new file mode 100644 index 0000000..2d65b71 --- /dev/null +++ b/bundled/componentize_py_async_support/__init__.py @@ -0,0 +1,521 @@ +import asyncio +import componentize_py_runtime +import subprocess + +from os import PathLike +from socket import AddressFamily, AddressInfo, SocketKind, socket +from concurrent.futures import Executor +from componentize_py_types import Result, Ok, Err +from dataclasses import dataclass +from typing import Any, Optional, TypeVar, TypeVarTuple, Callable, IO, Literal, cast +from contextvars import ContextVar, Context +from collections.abc import Coroutine, Awaitable, Sequence +from asyncio.protocols import BaseProtocol +from asyncio.transports import Transport, WriteTransport, DatagramTransport, SubprocessTransport, ReadTransport +from asyncio.base_events import Server + +try: + from ssl import SSLContext +except: + pass + +@dataclass +class _FutureState: + waitable_set: int | None + futures: dict[int, Any] + handles: list[asyncio.Handle] + pending_count: int + +class _CallbackCode: + EXIT = 0 + YIELD = 1 + WAIT = 2 + POLL = 3 + +class _Event: + NONE = 0 + SUBTASK = 1 + STREAM_READ = 2 + STREAM_WRITE = 3 + FUTURE_READ = 4 + FUTURE_WRITE = 5 + CANCELLED = 6 + +class _Status: + STARTING = 0 + STARTED = 1 + RETURNED = 2 + START_CANCELLED = 3 + RETURN_CANCELLED = 4 + +_T = TypeVar("_T") +_Ts = TypeVarTuple("_Ts") +_ProtocolT = TypeVar("_ProtocolT", bound=BaseProtocol) + +async def _noop() -> None: + pass + +class _Loop(asyncio.AbstractEventLoop): + def __init__(self) -> None: + self.running: bool = False + self.exception: Optional[Any] = None + + def poll(self, future_state: _FutureState) -> None: + while True: + handles = future_state.handles + future_state.handles = [] + for handle in handles: + if not handle._cancelled: + handle._run() + + if self.exception is not None: + raise self.exception + + if len(handles) == 0 and len(future_state.handles) == 0: + return + + def get_debug(self) -> bool: + return False + + def is_running(self) -> bool: + return self.running + + def is_closed(self) -> bool: + return not self.running + + def stop(self) -> None: + self.running = False + + def close(self) -> None: + self.running = False + + def shutdown_asyncgens(self) -> Coroutine[Any, Any, None]: + return _noop() + + def call_exception_handler(self, context: dict[str, Any]) -> None: + self.exception = context.get('exception', None) + + def call_soon(self, + callback: Callable[[*_Ts], object], + *args: *_Ts, + context: Context | None = None) -> asyncio.Handle: + global _future_state + + if context is not None: + future_state = context[_future_state] + handle = asyncio.Handle(callback, args, self, context) + future_state.handles.append(handle) + return handle + else: + raise AssertionError + + def create_task(self, coroutine: Coroutine[Any, Any, _T], name: str | None = None, context: Context | None = None) -> asyncio.Task[_T]: + return asyncio.Task(coroutine, loop=self, context=context) + + def create_future(self) -> asyncio.Future[Any]: + return asyncio.Future(loop=self) + + # The remaining methods should be irrelevant for our purposes and thus unimplemented + + def run_until_complete(self, future: Awaitable[_T]) -> _T: + raise NotImplementedError + + def run_forever(self) -> None: + raise NotImplementedError + + async def shutdown_default_executor(self) -> None: + raise NotImplementedError + + def call_later(self, + delay: float, + callback: Callable[[*_Ts], object], + *args: *_Ts, + context: Context | None = None) -> asyncio.TimerHandle: + raise NotImplementedError + + def call_at(self, + when: float, + callback: Callable[[*_Ts], object], + *args: *_Ts, + context: Context | None = None) -> asyncio.TimerHandle: + raise NotImplementedError + + def time(self) -> float: + raise NotImplementedError + + def call_soon_threadsafe(self, + callback: Callable[[*_Ts], object], + *args: *_Ts, + context: Context | None = None) -> asyncio.Handle: + raise NotImplementedError + + def run_in_executor(self, + executor: Executor | None, + func: Callable[[*_Ts], _T], + *args: *_Ts) -> asyncio.Future[_T]: + raise NotImplementedError + + def set_default_executor(self, executor: Executor) -> None: + raise NotImplementedError + + async def getaddrinfo( + self, + host: bytes | str | None, + port: bytes | str | int | None, + *, + family: int = 0, + type: int = 0, + proto: int = 0, + flags: int = 0, + ) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int] | tuple[str, int, int, int]]]: + raise NotImplementedError + + async def getnameinfo(self, + sockaddr: tuple[str, int] | tuple[str, int, int, int], + flags: int = 0) -> tuple[str, str]: + raise NotImplementedError + + async def create_connection( + self, + protocol_factory: Callable[[], _ProtocolT], + host: str | None = ..., + port: int | None = ..., + *, + ssl: bool | SSLContext | None = None, + family: int = 0, + proto: int = 0, + flags: int = 0, + sock: socket | None = None, + local_addr: tuple[str, int] | None = None, + server_hostname: str | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + happy_eyeballs_delay: float | None = None, + interleave: int | None = None, + ) -> tuple[Transport, _ProtocolT]: + raise NotImplementedError + + async def create_server( + self, + protocol_factory: Callable[[], BaseProtocol], + host: str | Sequence[str] | None = None, + port: int | None = None, + *, + family: int = AddressFamily.AF_UNSPEC, + flags: int = AddressInfo.AI_PASSIVE, + sock: socket | None = ..., + backlog: int = 100, + ssl: bool | SSLContext | None = None, + reuse_address: bool | None = None, + reuse_port: bool | None = None, + keep_alive: bool | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + start_serving: bool = True, + ) -> Server: + raise NotImplementedError + + async def sendfile(self, + transport: WriteTransport, + file: IO[bytes], + offset: int = 0, + count: int | None = None, + *, + fallback: bool = True + ) -> int: + raise NotImplementedError + + async def start_tls( + self, + transport: WriteTransport, + protocol: BaseProtocol, + sslcontext: SSLContext, + *, + server_side: bool = False, + server_hostname: str | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + ) -> Transport | None: + raise NotImplementedError + + async def create_unix_connection( + self, + protocol_factory: Callable[[], _ProtocolT], + path: str | None = None, + *, + ssl: bool | SSLContext | None = None, + sock: socket | None = None, + server_hostname: str | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + ) -> tuple[Transport, _ProtocolT]: + raise NotImplementedError + + async def create_unix_server( + self, + protocol_factory: Callable[[], BaseProtocol], + path: str | PathLike[str] | None = None, + *, + sock: socket | None = None, + backlog: int = 100, + ssl: bool | SSLContext | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + start_serving: bool = True, + ) -> Server: + raise NotImplementedError + + async def connect_accepted_socket( + self, + protocol_factory: Callable[[], _ProtocolT], + sock: socket, + *, + ssl: bool | SSLContext | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + ) -> tuple[Transport, _ProtocolT]: + raise NotImplementedError + + async def create_datagram_endpoint( + self, + protocol_factory: Callable[[], _ProtocolT], + local_addr: tuple[str, int] | str | None = None, + remote_addr: tuple[str, int] | str | None = None, + *, + family: int = 0, + proto: int = 0, + flags: int = 0, + reuse_address: bool | None = None, + reuse_port: bool | None = None, + allow_broadcast: bool | None = None, + sock: socket | None = None, + ) -> tuple[DatagramTransport, _ProtocolT]: + raise NotImplementedError + + async def connect_read_pipe(self, + protocol_factory: Callable[[], _ProtocolT], + pipe: Any) -> tuple[ReadTransport, _ProtocolT]: + raise NotImplementedError + + async def connect_write_pipe(self, + protocol_factory: Callable[[], _ProtocolT], + pipe: Any) -> tuple[WriteTransport, _ProtocolT]: + raise NotImplementedError + + async def subprocess_shell( + self, + protocol_factory: Callable[[], _ProtocolT], + cmd: bytes | str, + *, + stdin: int | IO[Any] | None = -1, + stdout: int | IO[Any] | None = -1, + stderr: int | IO[Any] | None = -1, + universal_newlines: Literal[False] = False, + shell: Literal[True] = True, + bufsize: Literal[0] = 0, + encoding: None = None, + errors: None = None, + text: Literal[False] | None = None, + **kwargs: Any, + ) -> tuple[SubprocessTransport, _ProtocolT]: + raise NotImplementedError + + async def subprocess_exec( + self, + protocol_factory: Callable[[], _ProtocolT], + program: Any, + *args: Any, + stdin: int | IO[Any] | None = -1, + stdout: int | IO[Any] | None = -1, + stderr: int | IO[Any] | None = -1, + universal_newlines: Literal[False] = False, + shell: Literal[False] = False, + bufsize: Literal[0] = 0, + encoding: None = None, + errors: None = None, + **kwargs: Any, + ) -> tuple[SubprocessTransport, _ProtocolT]: + raise NotImplementedError + + def add_reader(self, fd: Any, callback: Callable[[*_Ts], Any], *args: *_Ts) -> None: + raise NotImplementedError + + def remove_reader(self, fd: Any) -> bool: + raise NotImplementedError + + def add_writer(self, fd: Any, callback: Callable[[*_Ts], Any], *args: *_Ts) -> None: + raise NotImplementedError + + def remove_writer(self, fd: Any) -> bool: + raise NotImplementedError + + async def sock_recv(self, sock: socket, nbytes: int) -> bytes: + raise NotImplementedError + + async def sock_recv_into(self, sock: socket, buf: Any) -> int: + raise NotImplementedError + + async def sock_recvfrom(self, sock: socket, bufsize: int) -> tuple[bytes, Any]: + raise NotImplementedError + + async def sock_recvfrom_into(self, sock: socket, buf: Any, nbytes: int = 0) -> tuple[int, Any]: + raise NotImplementedError + + async def sock_sendall(self, sock: socket, data: Any) -> None: + raise NotImplementedError + + async def sock_sendto(self, sock: socket, data: Any, address: Any) -> int: + raise NotImplementedError + + async def sock_connect(self, sock: socket, address: Any) -> None: + raise NotImplementedError + + async def sock_accept(self, sock: socket) -> tuple[socket, Any]: + raise NotImplementedError + + async def sock_sendfile( + self, + sock: socket, + file: IO[bytes], + offset: int = 0, + count: int | None = None, + *, + fallback: bool | None = None + ) -> int: + raise NotImplementedError + + def add_signal_handler(self, sig: int, callback: Callable[[*_Ts], object], *args: *_Ts) -> None: + raise NotImplementedError + + def remove_signal_handler(self, sig: int) -> bool: + raise NotImplementedError + + def set_task_factory(self, factory: Any | None) -> None: + raise NotImplementedError + + def get_task_factory(self) -> None: + raise NotImplementedError + + def get_exception_handler(self) -> None: + raise NotImplementedError + + def set_exception_handler(self, handler: Any | None) -> None: + raise NotImplementedError + + def default_exception_handler(self, context: dict[str, Any]) -> None: + raise NotImplementedError + + def set_debug(self, enabled: bool) -> None: + raise NotImplementedError + +_future_state: ContextVar[_FutureState] = ContextVar("_future_state") +_loop = _Loop() +asyncio.set_event_loop(_loop) +_loop.running = True +asyncio.events._set_running_loop(_loop) + +def _set_future_state(future_state: _FutureState) -> None: + global _future_state + + _future_state.set(future_state) + +async def _return_result(export_index: int, coroutine: Any) -> None: + global _future_state + + try: + try: + result: Result[Any, Any] = Ok(await coroutine) + except Err as e: + result = e + + componentize_py_runtime.call_task_return(export_index, result) + except Exception as e: + _loop.exception = e + + assert _future_state.get().pending_count > 0 + _future_state.get().pending_count -= 1 + +def first_poll(export_index: int, coroutine: Any) -> int: + context = Context() + future_state = _FutureState(None, {}, [], 1) + context.run(_set_future_state, future_state) + asyncio.create_task(_return_result(export_index, coroutine), context=context) + return _poll(future_state) + +def _poll(future_state: _FutureState) -> int: + global _loop + + _loop.poll(future_state) + + if future_state.pending_count == 0: + if future_state.waitable_set is not None: + componentize_py_runtime.waitable_set_drop(future_state.waitable_set) + + return _CallbackCode.EXIT + else: + waitable_set = future_state.waitable_set + assert waitable_set is not None + componentize_py_runtime.context_set(future_state) + return _CallbackCode.WAIT | (waitable_set << 4) + +def callback(event0: int, event1: int, event2: int) -> int: + future_state = componentize_py_runtime.context_get() + componentize_py_runtime.context_set(None) + + match event0: + case _Event.NONE: + pass + case _Event.SUBTASK: + match event2: + case _Status.STARTING: + raise AssertionError + case _Status.STARTED: + pass + case _Status.RETURNED: + componentize_py_runtime.waitable_join(event1, 0) + componentize_py_runtime.subtask_drop(event1) + future_state.futures.pop(event1).set_result(event2) + case _: + # todo + raise NotImplementedError + case _: + # todo + raise NotImplementedError + + return _poll(future_state) + +async def await_result[T](result: Result[T, tuple[int, int]]) -> T: + global _loop + global _future_state + + if isinstance(result, Ok): + return result.value + else: + future_state = _future_state.get() + waitable, promise = result.value + future = _loop.create_future() + future_state.futures[waitable] = future + + if future_state.waitable_set is None: + future_state.waitable_set = componentize_py_runtime.waitable_set_new() + componentize_py_runtime.waitable_join(waitable, future_state.waitable_set) + + return cast(T, componentize_py_runtime.promise_get_result(await future, promise)) + +async def _wrap_spawned(coroutine: Any) -> None: + global _future_state + + try: + await coroutine + except Exception as e: + _loop.exception = e + + assert _future_state.get().pending_count > 0 + _future_state.get().pending_count -= 1 + +def spawn(coroutine: Any) -> None: + global _future_state + + _future_state.get().pending_count += 1 + + asyncio.create_task(_wrap_spawned(coroutine)) diff --git a/bundled/componentize_py_async_support/futures.py b/bundled/componentize_py_async_support/futures.py new file mode 100644 index 0000000..c35ba0d --- /dev/null +++ b/bundled/componentize_py_async_support/futures.py @@ -0,0 +1,82 @@ +import componentize_py_runtime +import componentize_py_async_support +import weakref + +from typing import TypeVar, Generic, cast, Self, Any, Callable +from types import TracebackType + +T = TypeVar('T') + +class FutureReader(Generic[T]): + def __init__(self, type_: int, handle: int): + self.type_ = type_ + self.handle: int | None = handle + self.finalizer = weakref.finalize(self, componentize_py_runtime.future_drop_readable, type_, handle) + + async def read(self) -> T | None: + self.finalizer.detach() + handle = self.handle + self.handle = None + if handle is not None: + result = await componentize_py_async_support.await_result( + componentize_py_runtime.future_read(self.type_, handle) + ) + componentize_py_runtime.future_drop_readable(self.type_, handle) + return cast(T, result) + else: + raise AssertionError + + def __enter__(self) -> Self: + return self + + def __exit__(self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None) -> bool | None: + if self.handle is not None: + self.finalizer.detach() + handle = self.handle + self.handle = None + componentize_py_runtime.future_drop_readable(self.type_, handle) + + return None + +async def write(type_: int, handle: int, value: Any) -> None: + await componentize_py_async_support.await_result( + componentize_py_runtime.future_write(type_, handle, value) + ) + componentize_py_runtime.future_drop_writable(type_, handle) + +def write_default(type_: int, handle: int, default: Callable[[], Any]) -> None: + componentize_py_async_support.spawn(write(type_, handle, default())) + +class FutureWriter(Generic[T]): + def __init__(self, type_: int, handle: int, default: Callable[[], T]): + self.type_ = type_ + self.handle: int | None = handle + self.default = default + self.finalizer = weakref.finalize(self, write_default, type_, handle, default) + + async def write(self, value: T) -> None: + self.finalizer.detach() + handle = self.handle + self.handle = None + if handle is not None: + await componentize_py_async_support.await_result( + componentize_py_runtime.future_write(self.type_, handle, value) + ) + componentize_py_runtime.future_drop_writable(self.type_, handle) + else: + raise AssertionError + + def __enter__(self) -> Self: + return self + + def __exit__(self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None) -> bool | None: + if self.handle is not None: + componentize_py_async_support.spawn(self.write(self.default())) + + return None diff --git a/bundled/componentize_py_async_support/streams.py b/bundled/componentize_py_async_support/streams.py new file mode 100644 index 0000000..629c5a4 --- /dev/null +++ b/bundled/componentize_py_async_support/streams.py @@ -0,0 +1,193 @@ +import componentize_py_runtime +import componentize_py_async_support +import weakref + +from typing import TypeVar, Generic, Self, cast +from types import TracebackType + +class _ReturnCode: + COMPLETED = 0 + DROPPED = 1 + CANCELLED = 2 + +class ByteStreamReader: + def __init__(self, type_: int, handle: int): + self.writer_dropped = False + self.type_ = type_ + self.handle: int | None = handle + self.finalizer = weakref.finalize(self, componentize_py_runtime.stream_drop_readable, type_, handle) + + async def read(self, max_count: int) -> bytes: + if self.writer_dropped: + return bytes() + + code, values = await self._read(max_count) + + if code == _ReturnCode.DROPPED: + self.writer_dropped = True + + return values + + async def _read(self, max_count: int) -> tuple[int, bytes]: + if self.handle is not None: + return cast(tuple[int, bytes], await componentize_py_async_support.await_result( + componentize_py_runtime.stream_read(self.type_, self.handle, max_count) + )) + else: + raise AssertionError + + def __enter__(self) -> Self: + return self + + def __exit__(self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None) -> bool | None: + self.finalizer.detach() + handle = self.handle + self.handle = None + if handle is not None: + componentize_py_runtime.stream_drop_readable(self.type_, handle) + return None + +class ByteStreamWriter: + def __init__(self, type_: int, handle: int): + self.reader_dropped = False + self.type_ = type_ + self.handle: int | None = handle + self.finalizer = weakref.finalize(self, componentize_py_runtime.stream_drop_writable, type_, handle) + + async def write(self, source: bytes) -> int: + if self.reader_dropped: + return 0 + + code, count = await self._write(source) + + if code == _ReturnCode.DROPPED: + self.reader_dropped = True + + return count + + async def _write(self, source: bytes) -> tuple[int, int]: + if self.handle is not None: + return await componentize_py_async_support.await_result( + componentize_py_runtime.stream_write(self.type_, self.handle, source) + ) + else: + raise AssertionError + + async def write_all(self, source: bytes) -> int: + total = 0 + + while len(source) > 0 and not self.reader_dropped: + count = await self.write(source) + source = source[count:] + total += count + + return total + + def __enter__(self) -> Self: + return self + + def __exit__(self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None) -> bool | None: + self.finalizer.detach() + handle = self.handle + self.handle = None + if handle is not None: + componentize_py_runtime.stream_drop_writable(self.type_, handle) + return None + +T = TypeVar('T') + +class StreamReader(Generic[T]): + def __init__(self, type_: int, handle: int): + self.writer_dropped = False + self.type_ = type_ + self.handle: int | None = handle + self.finalizer = weakref.finalize(self, componentize_py_runtime.stream_drop_readable, type_, handle) + + async def read(self, max_count: int) -> list[T]: + if self.writer_dropped: + return [] + + code, values = await self._read(max_count) + + if code == _ReturnCode.DROPPED: + self.writer_dropped = True + + return values + + async def _read(self, max_count: int) -> tuple[int, list[T]]: + if self.handle is not None: + return cast(tuple[int, list[T]], await componentize_py_async_support.await_result( + componentize_py_runtime.stream_read(self.type_, self.handle, max_count) + )) + else: + raise AssertionError + + def __enter__(self) -> Self: + return self + + def __exit__(self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None) -> bool | None: + self.finalizer.detach() + handle = self.handle + self.handle = None + if handle is not None: + componentize_py_runtime.stream_drop_readable(self.type_, handle) + return None + +class StreamWriter(Generic[T]): + def __init__(self, type_: int, handle: int): + self.reader_dropped = False + self.type_ = type_ + self.handle: int | None = handle + self.finalizer = weakref.finalize(self, componentize_py_runtime.stream_drop_writable, type_, handle) + + async def write(self, source: list[T]) -> int: + if self.reader_dropped: + return 0 + + code, count = await self._write(source) + + if code == _ReturnCode.DROPPED: + self.reader_dropped = True + + return count + + async def _write(self, source: list[T]) -> tuple[int, int]: + if self.handle is not None: + return await componentize_py_async_support.await_result( + componentize_py_runtime.stream_write(self.type_, self.handle, source) + ) + else: + raise AssertionError + + async def write_all(self, source: list[T]) -> int: + total = 0 + + while len(source) > 0 and not self.reader_dropped: + count = await self.write(source) + source = source[count:] + total += count + + return total + + def __enter__(self) -> Self: + return self + + def __exit__(self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None) -> bool | None: + self.finalizer.detach() + handle = self.handle + self.handle = None + if handle is not None: + componentize_py_runtime.stream_drop_writable(self.type_, handle) + return None diff --git a/bundled/componentize_py_runtime.pyi b/bundled/componentize_py_runtime.pyi new file mode 100644 index 0000000..b670aad --- /dev/null +++ b/bundled/componentize_py_runtime.pyi @@ -0,0 +1,34 @@ +from typing import Any +from componentize_py_types import Result + +def call_task_return(index: int, result: Any) -> None: ... + +def waitable_set_drop(set: int) -> None: ... + +def context_set(value: Any) -> None: ... + +def context_get() -> Any: ... + +def waitable_join(waitable: int, set: int) -> None: ... + +def subtask_drop(task: int) -> None: ... + +def waitable_set_new() -> int: ... + +def promise_get_result(event: int, promise: int) -> Any: ... + +def future_read(ty: int, future: int) -> Result[Any, tuple[int, int]]: ... + +def future_write(ty: int, future: int, value: Any) -> Result[None, tuple[int, int]]: ... + +def future_drop_readable(ty: int, future: int) -> None: ... + +def future_drop_writable(ty: int, future: int) -> None: ... + +def stream_read(ty: int, stream: int, max_count: int) -> Result[tuple[int, bytes | list[Any]], tuple[int, int]]: ... + +def stream_write(ty: int, stream: int, values: bytes | list[Any]) -> Result[tuple[int, int], tuple[int, int]]: ... + +def stream_drop_readable(ty: int, stream: int) -> None: ... + +def stream_drop_writable(ty: int, stream: int) -> None: ... diff --git a/bundled/componentize_py_types.py b/bundled/componentize_py_types.py new file mode 100644 index 0000000..9867d14 --- /dev/null +++ b/bundled/componentize_py_types.py @@ -0,0 +1,19 @@ +from typing import TypeVar, Generic, Union +from dataclasses import dataclass + +S = TypeVar('S') +@dataclass +class Some(Generic[S]): + value: S + +T = TypeVar('T') +@dataclass +class Ok(Generic[T]): + value: T + +E = TypeVar('E') +@dataclass(frozen=True) +class Err(Generic[E], Exception): + value: E + +Result = Union[Ok[T], Err[E]] diff --git a/bundled/poll_loop.py b/bundled/poll_loop.py index c963cdb..c067a0f 100644 --- a/bundled/poll_loop.py +++ b/bundled/poll_loop.py @@ -11,7 +11,7 @@ import socket import subprocess -from wit_world.types import Ok, Err +from componentize_py_types import Ok, Err from wit_world.imports import types, streams, poll, outgoing_handler from wit_world.imports.types import ( IncomingBody, diff --git a/examples/cli/README.md b/examples/cli/README.md index 7d52f2d..03bf829 100644 --- a/examples/cli/README.md +++ b/examples/cli/README.md @@ -9,16 +9,16 @@ run a Python-based component targetting the [wasi-cli] `command` world. ## Prerequisites -* `Wasmtime` 37.0.0 or later -* `componentize-py` 0.18.0 +* `Wasmtime` 39.0.0 or later +* `componentize-py` 0.19.0 Below, we use [Rust](https://rustup.rs/)'s `cargo` to install `Wasmtime`. If you don't have `cargo`, you can download and install from -https://github.com/bytecodealliance/wasmtime/releases/tag/v37.0.0. +https://github.com/bytecodealliance/wasmtime/releases/tag/v39.0.0. ``` -cargo install --version 37.0.0 wasmtime-cli -pip install componentize-py==0.18.0 +cargo install --version 39.0.0 wasmtime-cli +pip install componentize-py==0.19.0 ``` ## Running the demo diff --git a/examples/http-p3/README.md b/examples/http-p3/README.md new file mode 100644 index 0000000..0ca960e --- /dev/null +++ b/examples/http-p3/README.md @@ -0,0 +1,59 @@ +# Example: `http-p3` + +This is an example of how to use [componentize-py] and [Wasmtime] to build and +run a Python-based component targetting version `0.3.0-rc-2025-09-16` of the +[wasi-http] `proxy` world. + +[componentize-py]: https://github.com/bytecodealliance/componentize-py +[Wasmtime]: https://github.com/bytecodealliance/wasmtime +[wasi-http]: https://github.com/WebAssembly/wasi-http + +## Prerequisites + +* `Wasmtime` 39.0.0 or later +* `componentize-py` 0.19.0 + +Below, we use [Rust](https://rustup.rs/)'s `cargo` to install `Wasmtime`. If +you don't have `cargo`, you can download and install from +https://github.com/bytecodealliance/wasmtime/releases/tag/v39.0.0. + +``` +cargo install --version 39.0.0 wasmtime-cli +pip install componentize-py==0.19.0 +``` + +## Running the demo + +First, build the app and run it: + +``` +componentize-py -d ../../wit -w wasi:http/proxy@0.3.0-rc-2025-09-16 componentize app -o http.wasm +wasmtime serve -Sp3,common -Wcomponent-model-async http.wasm +``` + +Then, in another terminal, use cURL to send a request to the app: + +``` +curl -i -H 'content-type: text/plain' --data-binary @- http://127.0.0.1:8080/echo < Response: + """Handle the specified `request`, returning a `Response`.""" + + method = request.get_method() + path = request.get_path_with_query() + headers = request.get_headers().copy_all() + + if isinstance(method, Method_Get) and path == "/hash-all": + # Collect one or more "url" headers, download their contents + # concurrently, compute their SHA-256 hashes incrementally (i.e. without + # buffering the response bodies), and stream the results back to the + # client as they become available. + + urls = list(map( + lambda pair: str(pair[1], "utf-8"), + filter(lambda pair: pair[0] == "url", headers), + )) + + tx, rx = wit_world.byte_stream() + componentize_py_async_support.spawn(hash_all(urls, tx)) + + return Response.new( + Fields.from_list([("content-type", b"text/plain")]), + rx, + trailers_future() + )[0] + + elif isinstance(method, Method_Post) and path == "/echo": + # Echo the request body back to the client without buffering. + + rx, trailers = Request.consume_body(request, unit_future()) + + return Response.new( + Fields.from_list( + list(filter(lambda pair: pair[0] == "content-type", headers)) + ), + rx, + trailers + )[0] + + else: + response = Response.new(Fields(), None, trailers_future())[0] + response.set_status_code(400) + return response + + +async def hash_all(urls: list[str], tx: ByteStreamWriter) -> None: + with tx: + for result in asyncio.as_completed(map(sha256, urls)): + url, sha = await result + await tx.write_all(bytes(f"{url}: {sha}\n", "utf-8")) + + +async def sha256(url: str) -> tuple[str, str]: + """Download the contents of the specified URL, computing the SHA-256 + incrementally as the response body arrives. + + This returns a tuple of the original URL and either the hex-encoded hash or + an error message. + """ + + url_parsed = parse.urlparse(url) + + match url_parsed.scheme: + case "http": + scheme: Scheme = Scheme_Http() + case "https": + scheme = Scheme_Https() + case _: + scheme = Scheme_Other(url_parsed.scheme) + + request = Request.new(Fields(), None, trailers_future(), None)[0] + request.set_scheme(scheme) + request.set_authority(url_parsed.netloc) + request.set_path_with_query(url_parsed.path) + + response = await handler.handle(request) + status = response.get_status_code() + if status < 200 or status > 299: + return url, f"unexpected status: {status}" + + rx = Response.consume_body(response, unit_future())[0] + + hasher = hashlib.sha256() + with rx: + while not rx.writer_dropped: + chunk = await rx.read(16 * 1024) + hasher.update(chunk) + + return url, hasher.hexdigest() + + +def trailers_future() -> FutureReader[Result[Optional[Fields], ErrorCode]]: + return wit_world.result_option_wasi_http_types_fields_wasi_http_types_error_code_future(lambda: Ok(None))[1] + + +def unit_future() -> FutureReader[Result[None, ErrorCode]]: + return wit_world.result_unit_wasi_http_types_error_code_future(lambda: Ok(None))[1] diff --git a/examples/http/README.md b/examples/http/README.md index 9b849ba..06e7938 100644 --- a/examples/http/README.md +++ b/examples/http/README.md @@ -9,16 +9,16 @@ run a Python-based component targetting the [wasi-http] `proxy` world. ## Prerequisites -* `Wasmtime` 37.0.0 or later -* `componentize-py` 0.18.0 +* `Wasmtime` 39.0.0 or later +* `componentize-py` 0.19.0 Below, we use [Rust](https://rustup.rs/)'s `cargo` to install `Wasmtime`. If you don't have `cargo`, you can download and install from -https://github.com/bytecodealliance/wasmtime/releases/tag/v37.0.0. +https://github.com/bytecodealliance/wasmtime/releases/tag/v39.0.0. ``` -cargo install --version 37.0.0 wasmtime-cli -pip install componentize-py==0.18.0 +cargo install --version 39.0.0 wasmtime-cli +pip install componentize-py==0.19.0 ``` ## Running the demo diff --git a/examples/http/app.py b/examples/http/app.py index 5043427..be720f3 100644 --- a/examples/http/app.py +++ b/examples/http/app.py @@ -9,8 +9,8 @@ import hashlib import poll_loop +from componentize_py_types import Ok from wit_world import exports -from wit_world.types import Ok from wit_world.imports import types from wit_world.imports.types import ( Method_Get, diff --git a/examples/matrix-math/README.md b/examples/matrix-math/README.md index f3eea06..36dbfb0 100644 --- a/examples/matrix-math/README.md +++ b/examples/matrix-math/README.md @@ -10,8 +10,8 @@ within a guest component. ## Prerequisites -* `wasmtime` 37.0.0 or later -* `componentize-py` 0.18.0 +* `wasmtime` 39.0.0 or later +* `componentize-py` 0.19.0 * `NumPy`, built for WASI Note that we use an unofficial build of NumPy since the upstream project does @@ -19,11 +19,11 @@ not yet publish WASI builds. Below, we use [Rust](https://rustup.rs/)'s `cargo` to install `Wasmtime`. If you don't have `cargo`, you can download and install from -https://github.com/bytecodealliance/wasmtime/releases/tag/v37.0.0. +https://github.com/bytecodealliance/wasmtime/releases/tag/v39.0.0. ``` -cargo install --version 37.0.0 wasmtime-cli -pip install componentize-py==0.18.0 +cargo install --version 39.0.0 wasmtime-cli +pip install componentize-py==0.19.0 curl -OL https://github.com/dicej/wasi-wheels/releases/download/v0.0.2/numpy-wasi.tar.gz tar xf numpy-wasi.tar.gz ``` diff --git a/examples/matrix-math/app.py b/examples/matrix-math/app.py index d1b09fd..c0824e6 100644 --- a/examples/matrix-math/app.py +++ b/examples/matrix-math/app.py @@ -5,7 +5,7 @@ import numpy import wit_world from wit_world import exports -from wit_world.types import Err +from componentize_py_types import Err class WitWorld(wit_world.WitWorld): diff --git a/examples/sandbox/README.md b/examples/sandbox/README.md index 3cf0303..3bd9e47 100644 --- a/examples/sandbox/README.md +++ b/examples/sandbox/README.md @@ -7,11 +7,11 @@ sandboxed Python code snippets from within a Python app. ## Prerequisites -* `wasmtime-py` 37.0.0 or later -* `componentize-py` 0.18.0 +* `wasmtime-py` 39.0.0 or later +* `componentize-py` 0.19.0 ``` -pip install componentize-py==0.18.0 wasmtime==37.0.0 +pip install componentize-py==0.19.0 wasmtime==39.0.0 ``` ## Running the demo diff --git a/examples/sandbox/guest.py b/examples/sandbox/guest.py index 37355d2..207925a 100644 --- a/examples/sandbox/guest.py +++ b/examples/sandbox/guest.py @@ -1,5 +1,5 @@ import wit_world -from wit_world.types import Err +from componentize_py_types import Err import json diff --git a/examples/tcp/README.md b/examples/tcp/README.md index 9f179cc..b9bb327 100644 --- a/examples/tcp/README.md +++ b/examples/tcp/README.md @@ -10,16 +10,16 @@ making an outbound TCP request using `wasi-sockets`. ## Prerequisites -* `Wasmtime` 37.0.0 or later -* `componentize-py` 0.18.0 +* `Wasmtime` 39.0.0 or later +* `componentize-py` 0.19.0 Below, we use [Rust](https://rustup.rs/)'s `cargo` to install `Wasmtime`. If you don't have `cargo`, you can download and install from -https://github.com/bytecodealliance/wasmtime/releases/tag/v37.0.0. +https://github.com/bytecodealliance/wasmtime/releases/tag/v39.0.0. ``` -cargo install --version 37.0.0 wasmtime-cli -pip install componentize-py==0.18.0 +cargo install --version 39.0.0 wasmtime-cli +pip install componentize-py==0.19.0 ``` ## Running the demo diff --git a/pyproject.toml b/pyproject.toml index 076f50e..216ce83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ features = ["pyo3/extension-module"] [project] name = "componentize-py" -version = "0.18.0" +version = "0.19.0" description = "Tool to package Python applications as WebAssembly components" readme = "README.md" license = { file = "LICENSE" } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 75f6464..bbb31af 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "componentize-py-runtime" version = "0.1.0" -edition = "2021" +edition = "2024" [lib] crate-type = ["staticlib"] @@ -14,4 +14,7 @@ num-bigint = "0.4.6" wit-bindgen = { version = "0.40.0", default-features = false, features = ["macros", "realloc"] } wit-bindgen-rt = { version = "0.40.0" } # TODO: switch to a release when available: -wit-dylib-ffi = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b1d8ff59", default-features = false } +wit-dylib-ffi = { git = "https://github.com/dicej/wasm-tools", rev = "b072b0ca", features = ["async-raw"] } + +[features] +async = [] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 9a02b7d..3d65fb4 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1,38 +1,32 @@ #![deny(warnings)] -#![allow( +#![expect( clippy::useless_conversion, reason = "some pyo3 macros produce code that does this" )] -#![allow( - static_mut_refs, - reason = "wit-bindgen::generate produces code that does this" -)] -#![allow(unknown_lints)] -#![allow( - unnecessary_transmutes, - reason = "nightly warning but not supported on stable" -)] -#![allow( +#![expect( clippy::not_unsafe_ptr_arg_deref, reason = "wit_dylib_ffi::export produces code that does this" )] use { anyhow::{Error, Result}, - exports::exports::{ - self as exp, Constructor, FunctionExportKind, Guest, OptionKind, ResultRecord, ReturnStyle, - Static, Symbols, + bindings::{ + exports::exports::{ + self as exp, Constructor, FunctionExportKind, Guest, OptionKind, ResultRecord, + ReturnStyle, Static, Symbols, + }, + wasi::cli0_2_0::environment, }, num_bigint::BigUint, once_cell::sync::OnceCell, pyo3::{ + Bound, IntoPyObject, Py, PyAny, PyErr, PyResult, Python, exceptions::PyAssertionError, intern, types::{ PyAnyMethods, PyBool, PyBytes, PyBytesMethods, PyDict, PyList, PyListMethods, PyMapping, PyMappingMethods, PyModule, PyModuleMethods, PyString, PyTuple, }, - Bound, IntoPyObject, Py, PyAny, PyErr, PyResult, Python, }, std::{ alloc::{self, Layout}, @@ -43,19 +37,26 @@ use { slice, str, sync::{Mutex, Once}, }, - wasi::cli::environment, wit_dylib_ffi::{ self as wit, Call, ExportFunction, Interpreter, List, Type, Wit, WitOption, WitResult, }, }; -wit_bindgen::generate!({ - world: "init", - path: "../wit", - generate_all, -}); +#[expect( + unsafe_op_in_unsafe_fn, + reason = "wit_bindgen::generate produces code that does this" +)] +mod bindings { + wit_bindgen::generate!({ + world: "init", + path: "../wit", + generate_all, + }); -export!(MyExports); + use super::MyExports; + + export!(MyExports); +} static WIT: OnceCell = OnceCell::new(); static STUB_WASI: OnceCell = OnceCell::new(); @@ -159,29 +160,86 @@ impl From for Anyhow { } } +fn release_borrows(py: Python) { + let borrows = mem::take(BORROWS.lock().unwrap().deref_mut()); + for Borrow { + value, + handle, + drop, + } in borrows + { + let value = value.bind(py); + + value.delattr(intern!(py, "handle")).unwrap(); + + value + .getattr(intern!(py, "finalizer")) + .unwrap() + .call_method0(intern!(py, "detach")) + .unwrap(); + + unsafe { + drop(handle); + } + } +} + #[pyo3::pyfunction] #[pyo3(pass_module)] fn call_import<'a>( module: Bound<'a, PyModule>, index: u32, params: Vec>, - _result_count: usize, -) -> PyResult>> { +) -> Bound<'a, PyAny> { + let py = module.py(); let func = WIT .get() .unwrap() .import_func(usize::try_from(index).unwrap()); + let mut call = MyCall::new(params.into_iter().rev().map(|v| v.unbind()).collect()); if func.is_async() { - todo!() + #[cfg(feature = "async")] + { + if let Some(pending) = unsafe { func.call_import_async(&mut call) } { + ERR_CONSTRUCTOR + .get() + .unwrap() + .call1( + py, + (PyTuple::new( + py, + [ + usize::try_from(pending.subtask).unwrap(), + Box::into_raw(Box::new(async_::Promise::ImportCall { + index, + call, + buffer: pending.buffer, + })) as usize, + ], + ) + .unwrap(),), + ) + .unwrap() + } else { + assert!(call.stack.len() < 2); + OK_CONSTRUCTOR + .get() + .unwrap() + .call1(py, (call.stack.pop().unwrap_or(py.None()),)) + .unwrap() + } + } + #[cfg(not(feature = "async"))] + { + panic!("async feature disabled") + } } else { - let mut call = MyCall::new(params.into_iter().rev().map(|v| v.unbind()).collect()); func.call_import_sync(&mut call); - Ok(mem::take(&mut call.stack) - .into_iter() - .map(|v| v.into_bound(module.py())) - .collect()) + assert!(call.stack.len() < 2); + call.stack.pop().unwrap_or(py.None()) } + .into_bound(py) } #[pyo3::pyfunction] @@ -193,11 +251,638 @@ fn drop_resource(_module: &Bound, index: usize, handle: u32) -> PyResu Ok(()) } +#[cfg(feature = "async")] +mod async_ { + use {super::*, pyo3::exceptions::PyMemoryError}; + + const RETURN_CODE_BLOCKED: u32 = 0xFFFF_FFFF; + const RETURN_CODE_COMPLETED: u32 = 0x0; + const RETURN_CODE_DROPPED: u32 = 0x1; + + pub static CALLBACK: OnceCell> = OnceCell::new(); + pub static BYTE_STREAM_READER_CONSTRUCTOR: OnceCell> = OnceCell::new(); + pub static STREAM_READER_CONSTRUCTOR: OnceCell> = OnceCell::new(); + pub static FUTURE_READER_CONSTRUCTOR: OnceCell> = OnceCell::new(); + + pub struct EmptyResource { + pub value: Py, + pub handle: u32, + pub finalizer_args: Py, + } + + impl EmptyResource { + fn restore(&self, py: Python) { + self.value + .setattr(py, intern!(py, "handle"), self.handle) + .unwrap(); + + let finalizer = FINALIZE + .get() + .unwrap() + .call1(py, self.finalizer_args.clone_ref(py)) + .unwrap(); + + self.value + .setattr(py, intern!(py, "finalizer"), finalizer) + .unwrap(); + } + } + + pub enum Promise { + ImportCall { + index: u32, + call: MyCall<'static>, + buffer: *mut u8, + }, + StreamRead { + call: MyCall<'static>, + ty: wit::Stream, + buffer: *mut u8, + }, + StreamWrite { + _values: Option>, + resources: Option>>, + }, + FutureRead { + call: MyCall<'static>, + ty: wit::Future, + buffer: *mut u8, + }, + FutureWrite { + _call: MyCall<'static>, + }, + } + + #[pyo3::pyfunction] + #[pyo3(pass_module)] + fn promise_get_result<'a>( + module: Bound<'a, PyModule>, + event: u32, + promise: usize, + ) -> Bound<'a, PyAny> { + let py = module.py(); + let mut promise = unsafe { Box::from_raw(promise as *mut Promise) }; + + match *promise.as_mut() { + Promise::ImportCall { + index, + ref mut call, + buffer, + } => { + let func = WIT + .get() + .unwrap() + .import_func(usize::try_from(index).unwrap()); + + unsafe { func.lift_import_async_result(call, buffer) }; + assert!(call.stack.len() < 2); + call.stack.pop().unwrap_or(py.None()).into_bound(py) + } + Promise::StreamRead { + ref mut call, + ty, + buffer, + } => { + let count = usize::try_from(event >> 4).unwrap(); + let code = event & 0xF; + PyTuple::new( + py, + [ + code.into_pyobject(py).unwrap().into_any(), + if let Some(Type::U8 | Type::S8) = ty.ty() { + unsafe { PyBytes::from_ptr(py, buffer, count) }.into_any() + } else { + let list = PyList::empty(py); + for offset in 0..count { + unsafe { + ty.lift(call, buffer.add(ty.abi_payload_size() * offset)) + }; + list.append(call.stack.pop().unwrap()).unwrap(); + } + list.into_any() + }, + ], + ) + .unwrap() + .into_any() + } + Promise::StreamWrite { ref resources, .. } => { + let read_count = event >> 4; + let code = event & 0xF; + + if let Some(resources) = resources { + for resources in &resources[usize::try_from(read_count).unwrap()..] { + for resource in resources { + resource.restore(py) + } + } + } + + PyTuple::new(py, [code, read_count]).unwrap().into_any() + } + Promise::FutureRead { + ref mut call, + ty, + buffer, + } => { + let code = event & 0xF; + if let RETURN_CODE_COMPLETED | RETURN_CODE_DROPPED = code { + unsafe { ty.lift(call, buffer) } + } + call.stack.pop().unwrap_or(py.None()).into_bound(py) + } + Promise::FutureWrite { .. } => { + let count = event >> 4; + let code = event & 0xF; + PyTuple::new(py, [code, count]).unwrap().into_any() + } + } + } + + #[pyo3::pyfunction] + fn waitable_set_new() -> u32 { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[waitable-set-new]"] + pub fn waitable_set_new() -> u32; + } + + unsafe { waitable_set_new() } + } + + #[pyo3::pyfunction] + fn waitable_join(waitable: u32, set: u32) { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[waitable-join]"] + pub fn waitable_join(waitable: u32, set: u32); + } + + unsafe { waitable_join(waitable, set) } + } + + #[pyo3::pyfunction] + fn context_set(value: Bound) { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[context-set-0]"] + pub fn context_set(value: u32); + } + + unsafe { + context_set(if value.is_none() { + 0 + } else { + u32::try_from(value.into_ptr() as usize).unwrap() + }) + } + } + + #[pyo3::pyfunction] + #[pyo3(pass_module)] + fn context_get<'a>(module: Bound<'a, PyModule>) -> Bound<'a, PyAny> { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[context-get-0]"] + pub fn context_get() -> u32; + } + unsafe { + let value = context_get(); + if value == 0 { + module.py().None().into_bound(module.py()) + } else { + Bound::from_owned_ptr( + module.py(), + usize::try_from(value).unwrap() as *mut pyo3::ffi::PyObject, + ) + } + } + } + + #[pyo3::pyfunction] + fn subtask_drop(task: u32) { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[subtask-drop]"] + pub fn subtask_drop(task: u32); + } + + unsafe { subtask_drop(task) } + } + + #[pyo3::pyfunction] + fn waitable_set_drop(set: u32) { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[waitable-set-drop]"] + pub fn waitable_set_drop(set: u32); + } + + unsafe { waitable_set_drop(set) } + } + + #[pyo3::pyfunction] + #[pyo3(pass_module)] + fn call_task_return(module: Bound, index: u32, result: Bound) { + let py = module.py(); + let index = usize::try_from(index).unwrap(); + let func = WIT.get().unwrap().export_func(index); + let export = &EXPORTS.get().unwrap()[index]; + let result = result.unbind(); + + let results = match export.return_style { + ReturnStyle::None => Vec::new(), + ReturnStyle::Normal => vec![result.getattr(py, intern!(py, "value")).unwrap()], + ReturnStyle::Result => vec![result], + }; + + let mut call = MyCall::new(results); + func.call_task_return(&mut call); + release_borrows(py); + } + + #[pyo3::pyfunction] + fn stream_new(ty: usize) -> u64 { + unsafe { WIT.get().unwrap().stream(ty).new()() } + } + + #[pyo3::pyfunction] + #[pyo3(pass_module)] + fn stream_read<'a>( + module: Bound<'a, PyModule>, + ty: usize, + handle: u32, + max_count: u32, + ) -> PyResult> { + let py = module.py(); + let ty = WIT.get().unwrap().stream(ty); + let mut call = MyCall::new(Vec::new()); + let max_count = usize::try_from(max_count).unwrap(); + let layout = + Layout::from_size_align(ty.abi_payload_size() * max_count, ty.abi_payload_align()) + .unwrap(); + let buffer = unsafe { std::alloc::alloc(layout) }; + if buffer.is_null() { + Err(PyMemoryError::new_err( + "`stream.read` buffer allocation failed", + )) + } else { + unsafe { call.defer_deallocate(buffer, layout) }; + + let code = unsafe { ty.read()(handle, buffer.cast(), max_count) }; + + Ok(if code == RETURN_CODE_BLOCKED { + ERR_CONSTRUCTOR + .get() + .unwrap() + .call1( + py, + (PyTuple::new( + py, + [ + usize::try_from(handle).unwrap(), + Box::into_raw(Box::new(async_::Promise::StreamRead { + call, + ty, + buffer, + })) as usize, + ], + ) + .unwrap(),), + ) + .unwrap() + } else { + let count = usize::try_from(code >> 4).unwrap(); + let code = code & 0xF; + OK_CONSTRUCTOR + .get() + .unwrap() + .call1( + py, + (PyTuple::new( + py, + [ + code.into_pyobject(py).unwrap().into_any(), + if let Some(Type::U8 | Type::S8) = ty.ty() { + unsafe { PyBytes::from_ptr(py, buffer, count) }.into_any() + } else { + let list = PyList::empty(py); + for offset in 0..count { + unsafe { + ty.lift( + &mut call, + buffer.add(ty.abi_payload_size() * offset), + ) + }; + list.append(call.stack.pop().unwrap()).unwrap(); + } + list.into_any() + }, + ], + ) + .unwrap(),), + ) + .unwrap() + } + .into_bound(py)) + } + } + + #[pyo3::pyfunction] + #[pyo3(pass_module)] + fn stream_write<'a>( + module: Bound<'a, PyModule>, + ty: usize, + handle: u32, + values: Bound<'a, PyAny>, + ) -> PyResult> { + let py = module.py(); + let ty = WIT.get().unwrap().stream(ty); + if let Some(Type::U8 | Type::S8) = ty.ty() { + let values = values.cast_into::().unwrap(); + let code = unsafe { + ty.write()( + handle, + values.as_bytes().as_ptr().cast(), + values.len().unwrap(), + ) + }; + + Ok(if code == RETURN_CODE_BLOCKED { + ERR_CONSTRUCTOR + .get() + .unwrap() + .call1( + py, + (PyTuple::new( + py, + [ + usize::try_from(handle).unwrap(), + Box::into_raw(Box::new(async_::Promise::StreamWrite { + _values: Some(values.unbind()), + resources: None, + })) as usize, + ], + ) + .unwrap(),), + ) + .unwrap() + } else { + let count = code >> 4; + let code = code & 0xF; + OK_CONSTRUCTOR + .get() + .unwrap() + .call1(py, (PyTuple::new(py, [code, count]).unwrap(),)) + .unwrap() + } + .into_bound(py)) + } else { + let values = values.cast_into::().unwrap(); + let write_count = values.len(); + let mut call = MyCall::new(Vec::new()); + let layout = Layout::from_size_align( + ty.abi_payload_size() * write_count, + ty.abi_payload_align(), + ) + .unwrap(); + let buffer = unsafe { std::alloc::alloc(layout) }; + if buffer.is_null() { + Err(PyMemoryError::new_err( + "`future.write` buffer allocation failed", + )) + } else { + unsafe { call.defer_deallocate(buffer, layout) }; + + let mut resources = Vec::with_capacity(write_count); + let mut need_restore_resources = false; + for offset in 0..write_count { + call.stack.push(values.get_item(offset).unwrap().unbind()); + call.resources = Some(Vec::new()); + unsafe { ty.lower(&mut call, buffer.add(ty.abi_payload_size() * offset)) }; + let res = call.resources.take().unwrap(); + if !res.is_empty() { + need_restore_resources = true; + } + resources.push(res); + } + + let code = unsafe { ty.write()(handle, buffer.cast(), write_count) }; + + Ok(if code == RETURN_CODE_BLOCKED { + ERR_CONSTRUCTOR + .get() + .unwrap() + .call1( + py, + (PyTuple::new( + py, + [ + usize::try_from(handle).unwrap(), + Box::into_raw(Box::new(async_::Promise::StreamWrite { + _values: None, + resources: need_restore_resources.then_some(resources), + })) as usize, + ], + ) + .unwrap(),), + ) + .unwrap() + } else { + let read_count = code >> 4; + let code = code & 0xF; + + if need_restore_resources { + for resources in &resources[usize::try_from(read_count).unwrap()..] { + for resource in resources { + resource.restore(py) + } + } + } + + OK_CONSTRUCTOR + .get() + .unwrap() + .call1(py, (PyTuple::new(py, [code, read_count]).unwrap(),)) + .unwrap() + } + .into_bound(py)) + } + } + } + + #[pyo3::pyfunction] + fn stream_drop_readable(ty: usize, handle: u32) { + unsafe { WIT.get().unwrap().stream(ty).drop_readable()(handle) }; + } + + #[pyo3::pyfunction] + fn stream_drop_writable(ty: usize, handle: u32) { + unsafe { WIT.get().unwrap().stream(ty).drop_writable()(handle) }; + } + + #[pyo3::pyfunction] + fn future_new(ty: usize) -> u64 { + unsafe { WIT.get().unwrap().future(ty).new()() } + } + + #[pyo3::pyfunction] + #[pyo3(pass_module)] + fn future_read<'a>( + module: Bound<'a, PyModule>, + ty: usize, + handle: u32, + ) -> PyResult> { + let py = module.py(); + let ty = WIT.get().unwrap().future(ty); + let mut call = MyCall::new(Vec::new()); + let layout = + Layout::from_size_align(ty.abi_payload_size(), ty.abi_payload_align()).unwrap(); + let buffer = unsafe { std::alloc::alloc(layout) }; + if buffer.is_null() { + Err(PyMemoryError::new_err( + "`future.read` buffer allocation failed", + )) + } else { + unsafe { call.defer_deallocate(buffer, layout) }; + + let code = unsafe { ty.read()(handle, buffer.cast()) }; + + Ok(if code == RETURN_CODE_BLOCKED { + ERR_CONSTRUCTOR + .get() + .unwrap() + .call1( + py, + (PyTuple::new( + py, + [ + usize::try_from(handle).unwrap(), + Box::into_raw(Box::new(async_::Promise::FutureRead { + call, + ty, + buffer, + })) as usize, + ], + ) + .unwrap(),), + ) + .unwrap() + } else { + let code = code & 0xF; + if let RETURN_CODE_COMPLETED | RETURN_CODE_DROPPED = code { + unsafe { ty.lift(&mut call, buffer) } + } + OK_CONSTRUCTOR + .get() + .unwrap() + .call1(py, (call.stack.pop().unwrap_or(py.None()),)) + .unwrap() + } + .into_bound(py)) + } + } + + #[pyo3::pyfunction] + #[pyo3(pass_module)] + fn future_write<'a>( + module: Bound<'a, PyModule>, + ty: usize, + handle: u32, + value: Bound<'a, PyAny>, + ) -> PyResult> { + let py = module.py(); + let ty = WIT.get().unwrap().future(ty); + let mut call = MyCall::new(vec![value.unbind()]); + let layout = + Layout::from_size_align(ty.abi_payload_size(), ty.abi_payload_align()).unwrap(); + let buffer = unsafe { std::alloc::alloc(layout) }; + if buffer.is_null() { + Err(PyMemoryError::new_err( + "`future.write` buffer allocation failed", + )) + } else { + unsafe { call.defer_deallocate(buffer, layout) }; + + let code = unsafe { + ty.lower(&mut call, buffer); + + ty.write()(handle, buffer.cast()) + }; + + Ok(if code == RETURN_CODE_BLOCKED { + ERR_CONSTRUCTOR + .get() + .unwrap() + .call1( + py, + (PyTuple::new( + py, + [ + usize::try_from(handle).unwrap(), + Box::into_raw(Box::new(async_::Promise::FutureWrite { + _call: call, + })) as usize, + ], + ) + .unwrap(),), + ) + .unwrap() + } else { + let count = code >> 4; + let code = code & 0xF; + OK_CONSTRUCTOR + .get() + .unwrap() + .call1(py, (PyTuple::new(py, [code, count]).unwrap(),)) + .unwrap() + } + .into_bound(py)) + } + } + + #[pyo3::pyfunction] + fn future_drop_readable(ty: usize, handle: u32) { + unsafe { WIT.get().unwrap().future(ty).drop_readable()(handle) }; + } + + #[pyo3::pyfunction] + fn future_drop_writable(ty: usize, handle: u32) { + unsafe { WIT.get().unwrap().future(ty).drop_writable()(handle) }; + } + + pub fn add_functions(module: &Bound) -> PyResult<()> { + module.add_function(pyo3::wrap_pyfunction!(promise_get_result, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(waitable_set_new, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(waitable_join, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(context_get, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(context_set, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(subtask_drop, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(waitable_set_drop, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(call_task_return, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(stream_new, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(stream_read, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(stream_write, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(stream_drop_readable, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(stream_drop_writable, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(future_new, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(future_read, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(future_write, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(future_drop_readable, module)?)?; + module.add_function(pyo3::wrap_pyfunction!(future_drop_writable, module)?) + } +} + #[pyo3::pymodule] #[pyo3(name = "componentize_py_runtime")] fn componentize_py_module(_py: Python<'_>, module: &Bound) -> PyResult<()> { module.add_function(pyo3::wrap_pyfunction!(call_import, module)?)?; - module.add_function(pyo3::wrap_pyfunction!(drop_resource, module)?) + module.add_function(pyo3::wrap_pyfunction!(drop_resource, module)?)?; + + #[cfg(feature = "async")] + async_::add_functions(module)?; + + Ok(()) } fn do_init(app_name: String, symbols: Symbols, stub_wasi: bool) -> Result<(), String> { @@ -206,13 +891,7 @@ fn do_init(app_name: String, symbols: Symbols, stub_wasi: bool) -> Result<(), St Python::initialize(); let init = |py: Python| { - let app = match py.import(app_name.as_str()) { - Ok(app) => app, - Err(e) => { - e.print(py); - return Err(e.into()); - } - }; + let app = py.import(app_name.as_str())?; STUB_WASI.set(stub_wasi).unwrap(); @@ -383,7 +1062,7 @@ fn do_init(app_name: String, symbols: Symbols, stub_wasi: bool) -> Result<(), St RESULTS.set(symbols.results).unwrap(); - let types = py.import(symbols.types_package.as_str())?; + let types = py.import("componentize_py_types")?; SOME_CONSTRUCTOR.set(types.getattr("Some")?.into()).unwrap(); OK_CONSTRUCTOR.set(types.getattr("Ok")?.into()).unwrap(); @@ -418,6 +1097,39 @@ fn do_init(app_name: String, symbols: Symbols, stub_wasi: bool) -> Result<(), St SEED.set(py.import("random")?.getattr("seed")?.into()) .unwrap(); + #[cfg(feature = "async")] + { + async_::CALLBACK + .set( + py.import("componentize_py_async_support") + .unwrap() + .getattr("callback") + .unwrap() + .into(), + ) + .unwrap(); + + let streams = py.import("componentize_py_async_support.streams").unwrap(); + + async_::BYTE_STREAM_READER_CONSTRUCTOR + .set(streams.getattr("ByteStreamReader").unwrap().into()) + .unwrap(); + + async_::STREAM_READER_CONSTRUCTOR + .set(streams.getattr("StreamReader").unwrap().into()) + .unwrap(); + + async_::FUTURE_READER_CONSTRUCTOR + .set( + py.import("componentize_py_async_support.futures") + .unwrap() + .getattr("FutureReader") + .unwrap() + .into(), + ) + .unwrap(); + } + let argv = py .import("sys")? .getattr("argv")? @@ -433,7 +1145,14 @@ fn do_init(app_name: String, symbols: Symbols, stub_wasi: bool) -> Result<(), St Ok::<_, Error>(()) }; - Python::attach(|py| init(py).map_err(|e| format!("{e:?}"))) + Python::attach(|py| { + init(py).map_err(|e| { + if let Some(e) = e.downcast_ref::() { + e.print(py); + } + format!("{e:?}") + }) + }) } struct MyExports; @@ -442,19 +1161,21 @@ impl Guest for MyExports { fn init(app_name: String, symbols: Symbols, stub_wasi: bool) -> Result<(), String> { let result = do_init(app_name, symbols, stub_wasi); - // This tells the WASI Preview 1 component adapter to reset its state. In particular, we want it to forget - // about any open handles and re-request the stdio handles at runtime since we'll be running under a brand - // new host. + // This tells the WASI Preview 1 component adapter to reset its state. + // In particular, we want it to forget about any open handles and + // re-request the stdio handles at runtime since we'll be running under + // a brand new host. #[link(wasm_import_module = "wasi_snapshot_preview1")] - extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "reset_adapter_state")] + unsafe extern "C" { + #[link_name = "reset_adapter_state"] fn reset_adapter_state(); } - // This tells wasi-libc to reset its preopen state, forcing re-initialization at runtime. + // This tells wasi-libc to reset its preopen state, forcing + // re-initialization at runtime. #[link(wasm_import_module = "env")] - extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "__wasilibc_reset_preopens")] + unsafe extern "C" { + #[link_name = "__wasilibc_reset_preopens"] fn wasilibc_reset_preopens(); } @@ -469,18 +1190,8 @@ impl Guest for MyExports { struct MyInterpreter; -impl Interpreter for MyInterpreter { - type CallCx<'a> = MyCall<'a>; - - fn initialize(wit: Wit) { - WIT.set(wit).map_err(drop).unwrap(); - } - - fn export_start<'a>(_: Wit, _: ExportFunction) -> Box> { - Box::new(MyCall::new(Vec::new())) - } - - fn export_call(_: Wit, func: ExportFunction, cx: &mut MyCall<'_>) { +impl MyInterpreter { + fn export_call_(func: ExportFunction, cx: &mut MyCall<'_>, async_: bool) -> u32 { Python::attach(|py| { if !*STUB_WASI.get().unwrap() { static ONCE: Once = Once::new(); @@ -524,63 +1235,98 @@ impl Interpreter for MyInterpreter { .and_then(|function| function.call1(py, PyTuple::new(py, params_py).unwrap())), }; - let result = match (result, export.return_style) { - (Ok(_), ReturnStyle::None) => None, - (Ok(result), ReturnStyle::Normal) => Some(result), - (Ok(result), ReturnStyle::Result) => { - Some(OK_CONSTRUCTOR.get().unwrap().call1(py, (result,)).unwrap()) - } - (Err(error), ReturnStyle::None | ReturnStyle::Normal) => { - error.print(py); - panic!("Python function threw an unexpected exception") + if async_ { + match result { + Ok(result) => result.extract(py).unwrap(), + Err(error) => { + error.print(py); + panic!("Python function threw an unexpected exception") + } } - (Err(error), ReturnStyle::Result) => { - if ERR_CONSTRUCTOR - .get() - .unwrap() - .bind(py) - .eq(error.get_type(py)) - .unwrap() - { - Some(error.into_value(py).into_any()) - } else { + } else { + let result = match (result, export.return_style) { + (Ok(_), ReturnStyle::None) => None, + (Ok(result), ReturnStyle::Normal) => Some(result), + (Ok(result), ReturnStyle::Result) => { + Some(OK_CONSTRUCTOR.get().unwrap().call1(py, (result,)).unwrap()) + } + (Err(error), ReturnStyle::None | ReturnStyle::Normal) => { error.print(py); panic!("Python function threw an unexpected exception") } + (Err(error), ReturnStyle::Result) => { + if ERR_CONSTRUCTOR + .get() + .unwrap() + .bind(py) + .eq(error.get_type(py)) + .unwrap() + { + Some(error.into_value(py).into_any()) + } else { + error.print(py); + panic!("Python function threw an unexpected exception") + } + } + }; + + if let Some(result) = result { + cx.stack.push(result); } - }; - if let Some(result) = result { - cx.stack.push(result); + release_borrows(py); + + 0 } + }) + } +} - let borrows = mem::take(BORROWS.lock().unwrap().deref_mut()); - for Borrow { - value, - handle, - drop, - } in borrows - { - let value = value.bind(py); +impl Interpreter for MyInterpreter { + type CallCx<'a> = MyCall<'a>; - value.delattr(intern!(py, "handle")).unwrap(); + fn initialize(wit: Wit) { + WIT.set(wit).map_err(drop).unwrap(); + } - value - .getattr(intern!(py, "finalizer")) - .unwrap() - .call_method0(intern!(py, "detach")) - .unwrap(); + fn export_start<'a>(_: Wit, _: ExportFunction) -> Box> { + Box::new(MyCall::new(Vec::new())) + } - unsafe { - drop(handle); - } - } - }); + fn export_call(_: Wit, func: ExportFunction, cx: &mut MyCall<'_>) { + Self::export_call_(func, cx, false); } - async fn export_call_async(_: Wit, func: ExportFunction, cx: Box>) { - _ = (func, cx); - todo!() + fn export_async_start(_: Wit, func: ExportFunction, mut cx: Box>) -> u32 { + #[cfg(feature = "async")] + { + Self::export_call_(func, &mut cx, true) + } + #[cfg(not(feature = "async"))] + { + _ = (func, &mut cx); + panic!("async feature disabled") + } + } + + fn export_async_callback(event0: u32, event1: u32, event2: u32) -> u32 { + #[cfg(feature = "async")] + { + Python::attach(|py| { + async_::CALLBACK + .get() + .unwrap() + .call1(py, (event0, event1, event2)) + .unwrap() + .extract(py) + .unwrap() + }) + } + #[cfg(not(feature = "async"))] + { + _ = (event0, event1, event2); + panic!("async feature disabled") + } } fn resource_dtor(ty: wit::Resource, handle: usize) { @@ -595,20 +1341,59 @@ struct MyCall<'a> { deferred_deallocations: Vec<(*mut u8, Layout)>, strings: Vec, stack: Vec>, + #[cfg(feature = "async")] + resources: Option>, } impl MyCall<'_> { fn new(stack: Vec>) -> Self { - // TODO: tell py03 to attach (and detach on drop) this thread to the - // interpreter. Self { _phantom: PhantomData, iter_stack: Vec::new(), deferred_deallocations: Vec::new(), strings: Vec::new(), stack, + #[cfg(feature = "async")] + resources: None, } } + + fn imported_resource_to_canon(&mut self, py: Python<'_>, value: Py, owned: bool) -> u32 { + let handle = value + .bind(py) + .getattr(intern!(py, "handle")) + .unwrap() + .extract() + .unwrap(); + + if owned { + value.bind(py).delattr(intern!(py, "handle")).unwrap(); + + let finalizer_args = value + .bind(py) + .getattr(intern!(py, "finalizer")) + .unwrap() + .call_method0(intern!(py, "detach")) + .unwrap(); + + #[cfg(feature = "async")] + { + if let Some(resources) = &mut self.resources { + resources.push(async_::EmptyResource { + value, + handle, + finalizer_args: finalizer_args.cast_into().unwrap().unbind(), + }); + } + } + #[cfg(not(feature = "async"))] + { + _ = finalizer_args; + } + } + + handle + } } impl Drop for MyCall<'_> { @@ -690,7 +1475,7 @@ impl Call for MyCall<'_> { exported_resource_to_canon(py, ty, new, value) } else { // imported resource type - imported_resource_to_canon(py, value) + self.imported_resource_to_canon(py, value, false) } }) } @@ -703,14 +1488,7 @@ impl Call for MyCall<'_> { exported_resource_to_canon(py, ty, new, value) } else { // imported resource type - value - .bind(py) - .getattr(intern!(py, "finalizer")) - .unwrap() - .call_method0(intern!(py, "detach")) - .unwrap(); - - imported_resource_to_canon(py, value) + self.imported_resource_to_canon(py, value, true) } }) } @@ -744,14 +1522,20 @@ impl Call for MyCall<'_> { }) } - fn pop_future(&mut self, ty: wit::Future) -> u32 { - _ = ty; - todo!() + fn pop_future(&mut self, _ty: wit::Future) -> u32 { + Python::attach(|py| { + let value = self.stack.pop().unwrap(); + + self.imported_resource_to_canon(py, value, true) + }) } - fn pop_stream(&mut self, ty: wit::Stream) -> u32 { - _ = ty; - todo!() + fn pop_stream(&mut self, _ty: wit::Stream) -> u32 { + Python::attach(|py| { + let value = self.stack.pop().unwrap(); + + self.imported_resource_to_canon(py, value, true) + }) } fn pop_option(&mut self, ty: WitOption) -> u32 { @@ -867,13 +1651,16 @@ impl Call for MyCall<'_> { } unsafe fn maybe_pop_list(&mut self, ty: List) -> Option<(*const u8, usize)> { - if let Type::U8 = ty.ty() { + if let Type::U8 | Type::S8 = ty.ty() { Python::attach(|py| { let src = self.stack.pop().unwrap(); let src = src.cast_bound::(py).unwrap(); let len = src.len().unwrap(); - let dst = alloc::alloc(Layout::from_size_align(len, 1).unwrap()); - slice::from_raw_parts_mut(dst, len).copy_from_slice(src.as_bytes()); + let dst = unsafe { + let dst = alloc::alloc(Layout::from_size_align(len, 1).unwrap()); + slice::from_raw_parts_mut(dst, len).copy_from_slice(src.as_bytes()); + dst + }; Some((dst as _, len)) }) } else { @@ -1123,13 +1910,46 @@ impl Call for MyCall<'_> { } fn push_future(&mut self, ty: wit::Future, handle: u32) { - _ = (ty, handle); - todo!() + #[cfg(feature = "async")] + { + let result = Python::attach(|py| { + async_::FUTURE_READER_CONSTRUCTOR + .get() + .unwrap() + .call1(py, (ty.index(), handle)) + .unwrap() + }); + + self.stack.push(result); + } + #[cfg(not(feature = "async"))] + { + _ = (ty, handle); + panic!("async feature disabled") + } } fn push_stream(&mut self, ty: wit::Stream, handle: u32) { - _ = (ty, handle); - todo!() + #[cfg(feature = "async")] + { + let result = Python::attach(|py| { + if let Some(Type::U8 | Type::S8) = ty.ty() { + async_::BYTE_STREAM_READER_CONSTRUCTOR.get() + } else { + async_::STREAM_READER_CONSTRUCTOR.get() + } + .unwrap() + .call1(py, (ty.index(), handle)) + .unwrap() + }); + + self.stack.push(result); + } + #[cfg(not(feature = "async"))] + { + _ = (ty, handle); + panic!("async feature disabled") + } } fn push_variant(&mut self, ty: wit::Variant, discriminant: u32) { @@ -1194,8 +2014,8 @@ impl Call for MyCall<'_> { } unsafe fn push_raw_list(&mut self, ty: List, src: *mut u8, len: usize) -> bool { - if let Type::U8 = ty.ty() { - self.stack.push(Python::attach(|py| { + if let Type::U8 | Type::S8 = ty.ty() { + self.stack.push(Python::attach(|py| unsafe { let value = PyBytes::new(py, slice::from_raw_parts(src, len)) .to_owned() .into_any() @@ -1307,33 +2127,27 @@ fn exported_resource_to_canon( } } -fn imported_resource_to_canon(py: Python<'_>, value: Py) -> u32 { - value - .bind(py) - .getattr(intern!(py, "handle")) - .unwrap() - .extract() - .unwrap() -} - wit_dylib_ffi::export!(MyInterpreter); -// As of this writing, recent Rust `nightly` builds include a version of the `libc` crate that expects `wasi-libc` -// to define the following global variables, but `wasi-libc` defines them as preprocessor constants which aren't -// visible at link time, so we need to define them somewhere. Ideally, we should fix this upstream, but for now we -// work around it: +// As of this writing, recent Rust `nightly` builds include a version of the +// `libc` crate that expects `wasi-libc` to define the following global +// variables, but `wasi-libc` defines them as preprocessor constants which +// aren't visible at link time, so we need to define them somewhere. Ideally, +// we should fix this upstream, but for now we work around it: -#[no_mangle] +#[unsafe(no_mangle)] static _CLOCK_PROCESS_CPUTIME_ID: u8 = 2; -#[no_mangle] +#[unsafe(no_mangle)] static _CLOCK_THREAD_CPUTIME_ID: u8 = 3; -// Traditionally, `wit-bindgen` would provide a `cabi_realloc` implementation, but recent versions use a weak -// symbol trick to avoid conflicts when more than one `wit-bindgen` version is used, and that trick does not -// currently play nice with how we build this library. So for now, we just define it ourselves here: +// Traditionally, `wit-bindgen` would provide a `cabi_realloc` implementation, +// but recent versions use a weak symbol trick to avoid conflicts when more than +// one `wit-bindgen` version is used, and that trick does not currently play +// nice with how we build this library. So for now, we just define it ourselves +// here: /// # Safety /// TODO -#[export_name = "cabi_realloc"] +#[unsafe(export_name = "cabi_realloc")] pub unsafe extern "C" fn cabi_realloc( old_ptr: *mut u8, old_len: usize, @@ -1343,5 +2157,5 @@ pub unsafe extern "C" fn cabi_realloc( assert!(old_ptr.is_null()); assert!(old_len == 0); - alloc::alloc(Layout::from_size_align(new_size, align).unwrap()) + unsafe { alloc::alloc(Layout::from_size_align(new_size, align).unwrap()) } } diff --git a/src/command.rs b/src/command.rs index a842359..980f3ff 100644 --- a/src/command.rs +++ b/src/command.rs @@ -52,21 +52,25 @@ pub struct Common { #[clap(long)] all_features: bool, - /// Specify names to use for imported interfaces. May be specified more than once. + /// Specify names to use for imported interfaces. May be specified more + /// than once. /// - /// By default, the python module name generated for a given interface will be the snake-case form of the WIT - /// interface name, possibly qualified with the package name and namespace and/or version if that name would - /// otherwise clash with another interface. With this option, you may override that name with your own, unique - /// name. + /// By default, the python module name generated for a given interface will + /// be the snake-case form of the WIT interface name, possibly qualified + /// with the package name and namespace and/or version if that name would + /// otherwise clash with another interface. With this option, you may + /// override that name with your own, unique name. #[arg(long, value_parser = parse_key_value)] pub import_interface_name: Vec<(String, String)>, - /// Specify names to use for exported interfaces. May be specified more than once. + /// Specify names to use for exported interfaces. May be specified more + /// than once. /// - /// By default, the python module name generated for a given interface will be the snake-case form of the WIT - /// interface name, possibly qualified with the package name and namespace and/or version if that name would - /// otherwise clash with another interface. With this option, you may override that name with your own, unique - /// name. + /// By default, the python module name generated for a given interface will + /// be the snake-case form of the WIT interface name, possibly qualified + /// with the package name and namespace and/or version if that name would + /// otherwise clash with another interface. With this option, you may + /// override that name with your own, unique name. #[arg(long, value_parser = parse_key_value)] pub export_interface_name: Vec<(String, String)>, @@ -82,7 +86,8 @@ pub enum Command { /// Generate a component from the specified Python app and its dependencies. Componentize(Componentize), - /// Generate Python bindings for the world and write them to the specified directory. + /// Generate Python bindings for the world and write them to the specified + /// directory. Bindings(Bindings), } @@ -90,29 +95,37 @@ pub enum Command { pub struct Componentize { /// The name of a Python module containing the app to wrap. /// - /// Note that this should not match (any of) the world name(s) you are targeting since `componentize-py` will - /// generate code using those name(s), and Python doesn't know how to load two top-level modules with the same - /// name. + /// Note that this should not match (any of) the world name(s) you are + /// targeting since `componentize-py` will generate code using those + /// name(s), and Python doesn't know how to load two top-level modules with + /// the same name. pub app_name: String, - /// Specify a directory containing the app and/or its dependencies. May be specified more than once. + /// Specify a directory containing the app and/or its dependencies. May be + /// specified more than once. /// - /// If a `VIRTUAL_ENV` environment variable is set, it will be interpreted as a directory name, and that - /// directory will be searched for a `site-packages` subdirectory, which will be appended to the path as a - /// convenience for `venv` users. Alternatively, if `pipenv` is in `$PATH` and `pipenv --venv` produces a - /// non-empty result, it will be searched for a `site-packages` subdirectory, which will likewise be appended. - /// If the previous options fail, the `site` module in python will be used to get the `site-packages` + /// If a `VIRTUAL_ENV` environment variable is set, it will be interpreted + /// as a directory name, and that directory will be searched for a + /// `site-packages` subdirectory, which will be appended to the path as a + /// convenience for `venv` users. Alternatively, if `pipenv` is in `$PATH` + /// and `pipenv --venv` produces a non-empty result, it will be searched for + /// a `site-packages` subdirectory, which will likewise be appended. If the + /// previous options fail, the `site` module in python will be used to get + /// the `site-packages` #[arg(short = 'p', long, default_value = ".")] pub python_path: Vec, - /// Specify which world to use with which Python module. May be specified more than once. + /// Specify which world to use with which Python module. May be specified + /// more than once. /// - /// Some Python modules (e.g. SDK wrappers around WIT APIs) may contain `componentize-py.toml` files which - /// point to embedded WIT files, and those may define multiple WIT worlds. In this case, it may be necessary + /// Some Python modules (e.g. SDK wrappers around WIT APIs) may contain + /// `componentize-py.toml` files which point to embedded WIT files, and + /// those may define multiple WIT worlds. In this case, it may be necessary /// to specify which world on a module-by-module basis. /// - /// Note that these must be specified in topological order (i.e. if a module containing WIT files depends on - /// other modules containing WIT files, it must be listed after all its dependencies). + /// Note that these must be specified in topological order (i.e. if a module + /// containing WIT files depends on other modules containing WIT files, it + /// must be listed after all its dependencies). #[arg(short = 'm', long, value_parser = parse_key_value)] pub module_worlds: Vec<(String, String)>, @@ -122,9 +135,11 @@ pub struct Componentize { /// If set, replace all WASI imports with trapping stubs. /// - /// PLEASE NOTE: This has the effect of baking whatever PRNG seed is generated at build time into the - /// component, meaning Python's `random` module will return the exact same sequence each time the component is - /// run. Do *not* use this option in situations where a secure source of randomness is required. + /// PLEASE NOTE: This has the effect of baking whatever PRNG seed is + /// generated at build time into the component, meaning Python's `random` + /// module will return the exact same sequence each time the component is + /// run. Do *not* use this option in situations where a secure source of + /// randomness is required. #[arg(short = 's', long)] pub stub_wasi: bool, } @@ -423,7 +438,8 @@ mod tests { #[test] fn unstable_features_used_in_componentize() -> Result<()> { - // Given bindings to a WIT file with gated features and a Python file that uses them + // Given bindings to a WIT file with gated features and a Python file + // that uses them let wit = gated_x_wit_file()?; let out_dir = tempfile::tempdir()?; let common = Common { diff --git a/src/lib.rs b/src/lib.rs index babbf8f..9fd85c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,6 +46,8 @@ mod summary; mod test; mod util; +const DEBUG_PYTHON_BINDINGS: bool = false; + /// The default name of the Python module containing code generated from the /// specified WIT world. This may be overriden programatically or via the CLI /// using the `--world-module` option. @@ -189,17 +191,22 @@ pub fn generate_bindings( import_interface_names: &HashMap<&str, &str>, export_interface_names: &HashMap<&str, &str>, ) -> Result<()> { - // TODO: Split out and reuse the code responsible for finding and using componentize-py.toml files in the - // `componentize` function below, since that can affect the bindings we should be generating. + // TODO: Split out and reuse the code responsible for finding and using + // componentize-py.toml files in the `componentize` function below, since + // that can affect the bindings we should be generating. let (resolve, world) = parse_wit(wit_path, world, features, all_features)?; let import_function_indexes = &HashMap::new(); + let export_function_indexes = &HashMap::new(); + let stream_and_future_indexes = &HashMap::new(); let summary = Summary::try_new( &resolve, &iter::once(world).collect(), import_interface_names, export_interface_names, import_function_indexes, + export_function_indexes, + stream_and_future_indexes, )?; let world_module = world_module.unwrap_or(DEFAULT_WORLD_MODULE); let world_dir = output_dir.join(world_module.replace('.', "/")); @@ -231,7 +238,8 @@ pub async fn componentize( import_interface_names: &HashMap<&str, &str>, export_interface_names: &HashMap<&str, &str>, ) -> Result<()> { - // Remove non-existent elements from `python_path` so we don't choke on them later: + // Remove non-existent elements from `python_path` so we don't choke on them + // later: let python_path = &python_path .iter() .filter_map(|&s| Path::new(s).exists().then_some(s)) @@ -240,11 +248,11 @@ pub async fn componentize( let embedded_python_standard_lib = prelink::embedded_python_standard_library()?; let embedded_helper_utils = prelink::embedded_helper_utils()?; - let (configs, mut libraries) = + let (configs, libraries) = prelink::search_for_libraries_and_configs(python_path, module_worlds, world)?; - // Next, iterate over all the WIT directories, merging them into a single `Resolve`, and matching Python - // packages to `WorldId`s. + // Next, iterate over all the WIT directories, merging them into a single + // `Resolve`, and matching Python packages to `WorldId`s. let (mut resolve, mut main_world) = match wit_path { [] => (None, None), paths => { @@ -305,8 +313,8 @@ pub async fn componentize( let mut resolve = if let Some(resolve) = resolve { resolve } else { - // If no WIT directory was provided as a parameter and none were referenced by Python packages, use - // the default values. + // If no WIT directory was provided as a parameter and none were + // referenced by Python packages, use the default values. let paths: &[&Path] = &[]; let (my_resolve, world) = parse_wit(paths, world, features, all_features).context( "no WIT files found; please specify the directory or file \ @@ -316,8 +324,8 @@ pub async fn componentize( my_resolve }; - // Extract relevant metadata from the `Resolve` into a `Summary` instance, which we'll use to generate Wasm- - // and Python-level bindings. + // Extract relevant metadata from the `Resolve` into a `Summary` instance, + // which we'll use to generate Wasm- and Python-level bindings. let worlds = configs .values() @@ -390,7 +398,41 @@ pub async fn componentize( .iter() .enumerate() .map(|(index, func)| ((func.interface.as_deref(), func.name.as_str()), index)) - .collect::>(); + .collect(); + + let exported_function_indexes = metadata + .export_funcs + .iter() + .enumerate() + .map(|(index, func)| ((func.interface.as_deref(), func.name.as_str()), index)) + .collect(); + + let mut reverse_cloned_types = HashMap::new(); + for (&original, &clone) in clone_maps.types() { + assert!(reverse_cloned_types.insert(clone, original).is_none()); + } + + let original = |ty| { + if let Some(&original) = reverse_cloned_types.get(&ty) { + original + } else { + ty + } + }; + + let stream_and_future_indexes = metadata + .streams + .iter() + .enumerate() + .map(|(index, stream)| (original(stream.id), index)) + .chain( + metadata + .futures + .iter() + .enumerate() + .map(|(index, future)| (original(future.id), index)), + ) + .collect(); let summary = Summary::try_new( &resolve, @@ -398,8 +440,32 @@ pub async fn componentize( &import_interface_names, &export_interface_names, &imported_function_indexes, + &exported_function_indexes, + &stream_and_future_indexes, )?; + let need_async = summary.need_async(); + + // Now that we know whether to use the sync or async version of + // `libcomponentize_py_runtime.so`, update `libraries` accordingly. + // + // Note that we have two separate versions because older runtimes don't + // understand the new async ABI, so we only use the async version if it's + // actually needed. + let mut libraries = libraries + .into_iter() + .filter_map(|library| match (need_async, library.name.as_str()) { + (true, "libcomponentize_py_runtime_sync.so") + | (false, "libcomponentize_py_runtime_async.so") => None, + (true, "libcomponentize_py_runtime_async.so") + | (false, "libcomponentize_py_runtime_sync.so") => Some(Library { + name: "libcomponentize_py_runtime.so".into(), + ..library + }), + _ => Some(library), + }) + .collect::>(); + libraries.push(Library { name: "libcomponentize_py_bindings.so".into(), module: bindings, @@ -414,10 +480,11 @@ pub async fn componentize( None }; - // Pre-initialize the component by running it through `component_init_transform::initialize`. - // Currently, this is the application's first and only chance to load any standard or - // third-party modules since we do not yet include a virtual filesystem in the component to - // make those modules available at runtime. + // Pre-initialize the component by running it through + // `component_init_transform::initialize`. Currently, this is the + // application's first and only chance to load any standard or third-party + // modules since we do not yet include a virtual filesystem in the component + // to make those modules available at runtime. let stdout = MemoryOutputPipe::new(10000); let stderr = MemoryOutputPipe::new(10000); @@ -446,8 +513,9 @@ pub async fn componentize( wasi.preopened_dir(path, index.to_string(), DirPerms::all(), FilePerms::all())?; } - // For each Python package with a `componentize-py.toml` file that specifies where generated bindings for that - // package should be placed, generate the bindings and place them as indicated. + // For each Python package with a `componentize-py.toml` file that specifies + // where generated bindings for that package should be placed, generate the + // bindings and place them as indicated. let mut world_dir_mounts = Vec::new(); let mut locations = Locations::default(); @@ -505,7 +573,8 @@ pub async fn componentize( )); } - // If the caller specified a world and we haven't already generated bindings for it above, do so now. + // If the caller specified a world and we haven't already generated bindings + // for it above, do so now. if let (Some(world), false) = (main_world, saw_main_world) { let module = world_module.unwrap_or(DEFAULT_WORLD_MODULE); let world_dir = tempfile::tempdir()?; @@ -514,8 +583,9 @@ pub async fn componentize( summary.generate_code(&module_path, world, module, &mut locations, false)?; world_dir_mounts.push((vec!["world".to_owned()], world_dir)); - // The helper utilities are hard-coded to assume the world module is named `wit_world`. Here we replace - // that with the actual world module name. + // The helper utilities are hard-coded to assume the world module is + // named `wit_world`. Here we replace that with the actual world module + // name. fn replace(path: &Path, pattern: &str, replacement: &str) -> Result<()> { if path.is_dir() { for entry in fs::read_dir(path)? { @@ -537,15 +607,25 @@ pub async fn componentize( for (mounts, world_dir) in world_dir_mounts.iter() { for mount in mounts { + if DEBUG_PYTHON_BINDINGS { + eprintln!("world dir path: {}", world_dir.path().display()); + } wasi.preopened_dir(world_dir.path(), mount, DirPerms::all(), FilePerms::all())?; } } - // Generate a `Symbols` object containing metadata to be passed to the pre-init function. The runtime library - // will use this to look up types and functions that will later be referenced by the generated Wasm code. + if DEBUG_PYTHON_BINDINGS { + // Prevent temporary directories from being deleted: + std::mem::forget(world_dir_mounts); + } + + // Generate a `Symbols` object containing metadata to be passed to the + // pre-init function. The runtime library will use this to look up types + // and functions that will later be referenced by the generated Wasm code. let symbols = summary.collect_symbols(&locations, &metadata, &clone_maps); - // Finally, pre-initialize the component, writing the result to `output_path`. + // Finally, pre-initialize the component, writing the result to + // `output_path`. let python_path = (0..python_path.len()) .map(|index| format!("/{index}")) @@ -562,6 +642,7 @@ pub async fn componentize( let mut config = Config::new(); config.wasm_component_model(true); + config.wasm_component_model_async(true); config.async_support(true); let engine = Engine::new(&config)?; @@ -623,8 +704,8 @@ fn parse_wit( features: &[String], all_features: bool, ) -> Result<(Resolve, WorldId)> { - // If no WIT directory was provided as a parameter and none were referenced by Python packages, use ./wit - // by default. + // If no WIT directory was provided as a parameter and none were referenced + // by Python packages, use ./wit by default. if paths.is_empty() { let paths = &[Path::new("wit")]; return parse_wit(paths, world, features, all_features); @@ -722,9 +803,10 @@ fn add_wasi_and_stubs( for (interface_name, stubs) in stubs { if let Some(interface_name) = interface_name { - // Note that we do _not_ stub interfaces which appear to be part of WASIp2 since those should be - // provided by the `wasmtime_wasi::add_to_linker_async` call above, and adding stubs to those same - // interfaces would just cause trouble. + // Note that we do _not_ stub interfaces which appear to be part of + // WASIp2 since those should be provided by the + // `wasmtime_wasi::add_to_linker_async` call above, and adding stubs + // to those same interfaces would just cause trouble. if !is_wasip2_cli(&interface_name) && let Ok(mut instance) = linker.instance(&interface_name) { @@ -733,7 +815,7 @@ fn add_wasi_and_stubs( match stub { Stub::Function(name) => instance.func_new(name, { let name = name.clone(); - move |_, _, _| { + move |_, _, _, _| { Err(anyhow!("called trapping stub: {interface_name}#{name}")) } }), @@ -754,7 +836,7 @@ fn add_wasi_and_stubs( match stub { Stub::Function(name) => instance.func_new(name, { let name = name.clone(); - move |_, _, _| Err(anyhow!("called trapping stub: {name}")) + move |_, _, _, _| Err(anyhow!("called trapping stub: {name}")) }), Stub::Resource(name) => instance .resource(name, ResourceType::host::<()>(), { diff --git a/src/prelink.rs b/src/prelink.rs index a197112..0f176c9 100644 --- a/src/prelink.rs +++ b/src/prelink.rs @@ -22,7 +22,8 @@ type ConfigsMatchedWorlds<'a> = IndexMap, Option<&'a str>)>; pub fn embedded_python_standard_library() -> Result { - // Untar the embedded copy of the Python standard library into a temporary directory + // Untar the embedded copy of the Python standard library into a temporary + // directory let stdlib = tempfile::tempdir()?; Archive::new(Decoder::new(Cursor::new(include_bytes!(concat!( @@ -52,10 +53,18 @@ pub fn embedded_helper_utils() -> Result { pub fn bundle_libraries(library_path: Vec<(&str, Vec)>) -> Result> { let mut libraries = vec![ Library { - name: "libcomponentize_py_runtime.so".into(), + name: "libcomponentize_py_runtime_sync.so".into(), module: zstd::decode_all(Cursor::new(include_bytes!(concat!( env!("OUT_DIR"), - "/libcomponentize_py_runtime.so.zst" + "/libcomponentize_py_runtime_sync.so.zst" + ))))?, + dl_openable: false, + }, + Library { + name: "libcomponentize_py_runtime_async.so".into(), + module: zstd::decode_all(Cursor::new(include_bytes!(concat!( + env!("OUT_DIR"), + "/libcomponentize_py_runtime_async.so.zst" ))))?, dl_openable: false, }, @@ -167,12 +176,15 @@ pub fn search_for_libraries_and_configs<'a>( let libraries = bundle_libraries(library_path)?; - // Validate the paths parsed from any componentize-py.toml files discovered above and match them up with - // `module_worlds` entries. Note that we use an `IndexMap` to preserve the order specified in `module_worlds`, - // which is required to be topologically sorted with respect to package dependencies. + // Validate the paths parsed from any componentize-py.toml files discovered + // above and match them up with `module_worlds` entries. Note that we use + // an `IndexMap` to preserve the order specified in `module_worlds`, which + // is required to be topologically sorted with respect to package + // dependencies. // - // For any packages which contain componentize-py.toml files but no corresponding `module_worlds` entry, we use - // the `world` parameter as a default. + // For any packages which contain componentize-py.toml files but no + // corresponding `module_worlds` entry, we use the `world` parameter as a + // default. let configs: IndexMap, Option<&str>)> = { let mut configs = raw_configs .into_iter() @@ -239,9 +251,11 @@ fn search_directory( let mut push = true; for existing in &mut *configs { if path == existing.path.join("componentize-py.toml") { - // When one directory in `PYTHON_PATH` is a subdirectory of the other, we consider the - // subdirectory to be the true owner of the file. This is important later, when we derive a - // package name by stripping the root directory from the file path. + // When one directory in `PYTHON_PATH` is a subdirectory of + // the other, we consider the subdirectory to be the true + // owner of the file. This is important later, when we + // derive a package name by stripping the root directory + // from the file path. if root > existing.root { module.clone_into(&mut existing.module); root.clone_into(&mut existing.root); @@ -250,13 +264,15 @@ fn search_directory( push = false; break; } else { - // If we find a componentize-py.toml file under a Python module which will not be used because - // we already found a version of that module in an earlier `PYTHON_PATH` directory, we'll - // ignore the latest one. + // If we find a componentize-py.toml file under a Python + // module which will not be used because we already found a + // version of that module in an earlier `PYTHON_PATH` + // directory, we'll ignore the latest one. // - // For example, if the module `foo_sdk` appears twice in `PYTHON_PATH`, and both versions have - // a componentize-py.toml file, we'll ignore the second one just as Python will ignore the - // second module. + // For example, if the module `foo_sdk` appears twice in + // `PYTHON_PATH`, and both versions have a + // componentize-py.toml file, we'll ignore the second one + // just as Python will ignore the second module. if modules_seen.contains(&module) { bail!("multiple `componentize-py.toml` files found in module `{module}`"); diff --git a/src/stubwasi.rs b/src/stubwasi.rs index 1eff2b8..106336b 100644 --- a/src/stubwasi.rs +++ b/src/stubwasi.rs @@ -33,14 +33,17 @@ pub fn link_stub_modules(libraries: Vec) -> Result, } @@ -121,7 +122,6 @@ struct TypeLocation { pub struct Locations { types: HashMap, keys: HashMap, - types_module: Option, } pub struct Summary<'a> { @@ -142,6 +142,9 @@ pub struct Summary<'a> { imported_interface_names: HashMap, exported_interface_names: HashMap, imported_function_indexes: &'a HashMap<(Option<&'a str>, &'a str), usize>, + exported_function_indexes: &'a HashMap<(Option<&'a str>, &'a str), usize>, + stream_and_future_indexes: &'a HashMap, + need_async: bool, } impl<'a> Summary<'a> { @@ -151,6 +154,8 @@ impl<'a> Summary<'a> { import_interface_names: &HashMap<&str, &str>, export_interface_names: &HashMap<&str, &str>, imported_function_indexes: &'a HashMap<(Option<&'a str>, &'a str), usize>, + exported_function_indexes: &'a HashMap<(Option<&'a str>, &'a str), usize>, + stream_and_future_indexes: &'a HashMap, ) -> Result { let mut me = Self { resolve, @@ -170,6 +175,9 @@ impl<'a> Summary<'a> { imported_interface_names: HashMap::new(), exported_interface_names: HashMap::new(), imported_function_indexes, + exported_function_indexes, + stream_and_future_indexes, + need_async: false, }; let mut import_keys_seen = HashSet::new(); @@ -203,6 +211,10 @@ impl<'a> Summary<'a> { Ok(me) } + pub fn need_async(&self) -> bool { + self.need_async + } + fn push_function(&mut self, function: MyFunction<'a>) { self.functions.push(function); } @@ -281,8 +293,9 @@ impl<'a> Summary<'a> { self.visit_type(*ty, world); } TypeDefKind::Type(ty) => { - // When visiting a type alias, we must use the state already stored for any `use`d - // resources rather than overwrite it. + // When visiting a type alias, we must use the state + // already stored for any `use`d resources rather than + // overwrite it. let resource_state = self.resource_state.take(); self.visit_type(*ty, world); self.resource_state = resource_state; @@ -304,6 +317,13 @@ impl<'a> Summary<'a> { } self.types.insert(id); } + TypeDefKind::Stream(ty) | TypeDefKind::Future(ty) => { + self.need_async = true; + if let Some(ty) = ty { + self.visit_type(*ty, world); + } + self.types.insert(id); + } kind => todo!("{kind:?}"), } } @@ -340,6 +360,13 @@ impl<'a> Summary<'a> { wit_kind: wit_kind.clone(), }; + if let wit_parser::FunctionKind::AsyncFreestanding + | wit_parser::FunctionKind::AsyncMethod(_) + | wit_parser::FunctionKind::AsyncStatic(_) = wit_kind + { + self.need_async = true; + } + match direction { Direction::Import => { self.push_function(make(FunctionKind::Import)); @@ -640,10 +667,11 @@ impl<'a> Summary<'a> { ), FunctionExport { kind: match function.wit_kind { - wit_parser::FunctionKind::Freestanding => { + wit_parser::FunctionKind::Freestanding + | wit_parser::FunctionKind::AsyncFreestanding => { FunctionExportKind::Freestanding(Function { protocol: scope.to_upper_camel_case().escape(), - name: self.function_name(function), + name: self.function_name_for_call(function), }) } wit_parser::FunctionKind::Constructor(id) => { @@ -657,10 +685,12 @@ impl<'a> Summary<'a> { .escape(), }) } - wit_parser::FunctionKind::Method(_) => { - FunctionExportKind::Method(self.function_name(function)) + wit_parser::FunctionKind::Method(_) + | wit_parser::FunctionKind::AsyncMethod(_) => { + FunctionExportKind::Method(self.function_name_for_call(function)) } - wit_parser::FunctionKind::Static(id) => { + wit_parser::FunctionKind::Static(id) + | wit_parser::FunctionKind::AsyncStatic(id) => { FunctionExportKind::Static(Static { module: scope.to_snake_case().escape(), protocol: self.resolve.types[id] @@ -669,10 +699,9 @@ impl<'a> Summary<'a> { .unwrap() .to_upper_camel_case() .escape(), - name: self.function_name(function), + name: self.function_name_for_call(function), }) } - _ => todo!("handle async functions"), }, return_style: match function.result { None => ReturnStyle::None, @@ -779,7 +808,6 @@ impl<'a> Summary<'a> { .collect(); Symbols { - types_package: format!("{}.types", locations.types_module.as_ref().unwrap()), exports, resources, records, @@ -806,13 +834,43 @@ impl<'a> Summary<'a> { .unwrap() } + fn exported_function_index(&self, function: &MyFunction) -> usize { + *self + .exported_function_indexes + .get(&( + function + .interface + .as_ref() + .map(|v| self.resolve.name_world_key(v.key)) + .as_deref(), + function.name, + )) + .unwrap() + } + fn function_name(&self, function: &MyFunction) -> String { - self.function_name_with(&function.wit_kind, function.name) + self.function_name_with(&function.wit_kind, function.name, "") + } + + fn function_name_for_call(&self, function: &MyFunction) -> String { + self.function_name_with(&function.wit_kind, function.name, ASYNC_START_PREFIX) } - fn function_name_with(&self, kind: &wit_parser::FunctionKind, name: &str) -> String { + fn function_name_with( + &self, + kind: &wit_parser::FunctionKind, + name: &str, + async_start_prefix: &str, + ) -> String { match kind { wit_parser::FunctionKind::Freestanding => name.to_snake_case().escape(), + wit_parser::FunctionKind::AsyncFreestanding => format!( + "{async_start_prefix}{}", + name.strip_prefix("[async]") + .unwrap() + .to_snake_case() + .escape() + ), wit_parser::FunctionKind::Constructor(_) => "__init__".into(), wit_parser::FunctionKind::Method(id) => name .strip_prefix(&format!( @@ -822,6 +880,16 @@ impl<'a> Summary<'a> { .unwrap() .to_snake_case() .escape(), + wit_parser::FunctionKind::AsyncMethod(id) => format!( + "{async_start_prefix}{}", + name.strip_prefix(&format!( + "[async method]{}.", + self.resolve.types[*id].name.as_deref().unwrap() + )) + .unwrap() + .to_snake_case() + .escape() + ), wit_parser::FunctionKind::Static(id) => name .strip_prefix(&format!( "[static]{}.", @@ -830,7 +898,16 @@ impl<'a> Summary<'a> { .unwrap() .to_snake_case() .escape(), - _ => todo!("support async functions"), + wit_parser::FunctionKind::AsyncStatic(id) => format!( + "{async_start_prefix}{}", + name.strip_prefix(&format!( + "[async static]{}.", + self.resolve.types[*id].name.as_deref().unwrap() + )) + .unwrap() + .to_snake_case() + .escape() + ), } } @@ -863,11 +940,15 @@ impl<'a> Summary<'a> { let snake = self.function_name(function); let (skip_count, self_) = match function.wit_kind { - wit_parser::FunctionKind::Freestanding => (0, None), + wit_parser::FunctionKind::Freestanding + | wit_parser::FunctionKind::AsyncFreestanding => (0, None), wit_parser::FunctionKind::Constructor(_) => (0, Some("self")), - wit_parser::FunctionKind::Method(_) => (1, Some("self")), - wit_parser::FunctionKind::Static(_) => (0, Some("cls")), - _ => todo!("support async functions"), + wit_parser::FunctionKind::Method(_) | wit_parser::FunctionKind::AsyncMethod(_) => { + (1, Some("self")) + } + wit_parser::FunctionKind::Static(_) | wit_parser::FunctionKind::AsyncStatic(_) => { + (0, Some("cls")) + } }; let mut type_name = |ty| names.type_name(ty, seen, resource); @@ -916,7 +997,9 @@ impl<'a> Summary<'a> { if let wit_parser::FunctionKind::Constructor(_) = function.wit_kind { ("return".to_owned(), "None".to_owned(), None) } else { - let indent = if let wit_parser::FunctionKind::Freestanding = function.wit_kind { + let indent = if let wit_parser::FunctionKind::Freestanding + | wit_parser::FunctionKind::AsyncFreestanding = function.wit_kind + { "" } else { " " @@ -934,37 +1017,24 @@ impl<'a> Summary<'a> { ( format!( - "if isinstance(result[0], Err): -{indent} raise result[0] + "if isinstance(result, Err): +{indent} raise result {indent} else: -{indent} return result[0].value" +{indent} return result.value" ), result.ok.map(type_name).unwrap_or_else(|| "None".into()), error, ) } - SpecialReturn::None => { - ("return result[0]".to_owned(), type_name(*ty), None) - } + SpecialReturn::None => ("return result".to_owned(), type_name(*ty), None), }, - _ => ( - "return result".to_owned(), - format!( - "({})", - result_types - .iter() - .map(|ty| type_name(*ty)) - .collect::>() - .join(", ") - ), - None, - ), + _ => unreachable!(), } }; - let result_count = result_types.len(); - - let class_method = if let wit_parser::FunctionKind::Static(_) = function.wit_kind { + let class_method = if let wit_parser::FunctionKind::Static(_) + | wit_parser::FunctionKind::AsyncStatic(_) = function.wit_kind + { "\n @classmethod" } else { "" @@ -977,7 +1047,6 @@ impl<'a> Summary<'a> { return_statement, class_method, return_type: format!(" -> {return_type}"), - result_count, error, } } @@ -1082,6 +1151,12 @@ impl<'a> Summary<'a> { sorted.insert(id); } } + TypeDefKind::Stream(ty) | TypeDefKind::Future(ty) => { + if let Some(ty) = ty { + self.sort(*ty, sorted, visited); + } + sorted.insert(id); + } kind => todo!("{kind:?}"), } } @@ -1181,6 +1256,120 @@ impl<'a> Summary<'a> { names } + #[allow(clippy::too_many_arguments)] + fn generate_export_code( + &self, + stub_runtime_calls: bool, + function: &MyFunction, + class_method: &str, + snake: &str, + params: &str, + return_type: &str, + docs: &str, + ) -> String { + let (skip_count, async_prefix) = match function.wit_kind { + wit_parser::FunctionKind::Freestanding => (0, None), + wit_parser::FunctionKind::AsyncFreestanding => (0, Some("self.")), + wit_parser::FunctionKind::Constructor(_) => (0, None), + wit_parser::FunctionKind::Method(_) => (1, None), + wit_parser::FunctionKind::AsyncMethod(_) => (1, Some("self.")), + wit_parser::FunctionKind::Static(_) => (0, None), + wit_parser::FunctionKind::AsyncStatic(_) => (0, Some("cls.")), + }; + + let (async_, body) = if let Some(prefix) = async_prefix { + let args = function + .params + .iter() + .skip(skip_count) + .map(|(name, _)| name.to_snake_case().escape()) + .collect::>() + .join(", "); + + ( + "async ", + if stub_runtime_calls { + NOT_IMPLEMENTED.into() + } else { + let index = self.exported_function_index(function); + + format!( + "{NOT_IMPLEMENTED} + +{class_method} + def {ASYNC_START_PREFIX}{snake}({params}) -> int: + return componentize_py_async_support.first_poll({index}, {prefix}{snake}({args}))" + ) + }, + ) + } else { + ("", NOT_IMPLEMENTED.into()) + }; + + format!( + "{class_method} + @abstractmethod + {async_}def {snake}({params}){return_type}: + {docs}{body} +" + ) + } + + #[allow(clippy::too_many_arguments)] + fn generate_import_code( + &self, + indent_level: usize, + stub_runtime_calls: bool, + function: &MyFunction, + class_method: &str, + snake: &str, + params: &str, + return_type: &str, + docs: &str, + args: &str, + return_statement: &str, + ) -> String { + let index = if stub_runtime_calls { + 0 + } else { + self.imported_function_index(function) + }; + + let indent = (0..indent_level) + .map(|_| " ") + .collect::>() + .concat(); + + let (async_, await_) = if let wit_parser::FunctionKind::AsyncFreestanding + | wit_parser::FunctionKind::AsyncMethod(_) + | wit_parser::FunctionKind::AsyncStatic(_) = function.wit_kind + { + ( + "async ", + format!( + "result = await componentize_py_async_support.await_result(result)\n{indent} " + ), + ) + } else { + ("", String::new()) + }; + + if stub_runtime_calls { + format!( + "{class_method} +{indent}{async_}def {snake}({params}){return_type}: +{indent} {docs}{NOT_IMPLEMENTED}" + ) + } else { + format!( + "{class_method} +{indent}{async_}def {snake}({params}){return_type}: +{indent} {docs}result = componentize_py_runtime.call_import({index}, [{args}]) +{indent} {await_}{return_statement}" + ) + } + } + pub fn generate_code( &self, path: &Path, @@ -1208,6 +1397,8 @@ impl<'a> Summary<'a> { let mut world_imports = Definitions::default(); let mut world_exports = Definitions::default(); let mut seen = HashSet::new(); + let mut stream_payloads = HashSet::new(); + let mut future_payloads = HashSet::new(); for (index, id) in self.types.iter().copied().enumerate() { if !self .world_types @@ -1392,7 +1583,6 @@ class {camel}(Flag): return_type, return_statement, class_method, - result_count, error, } = self.function_code( Direction::Import, @@ -1420,26 +1610,25 @@ class {camel}(Flag): format!( " def {snake}({params}){return_type}: - {docs}tmp = componentize_py_runtime.call_import({index}, [{args}], {result_count})[0] + {docs}tmp = componentize_py_runtime.call_import({index}, [{args}]) (_, func, args, _) = tmp.finalizer.detach() self.handle = tmp.handle self.finalizer = weakref.finalize(self, func, args[0], args[1]) " ) } - } else if stub_runtime_calls { - format!( - "{class_method} - def {snake}({params}){return_type}: - {docs}{NOT_IMPLEMENTED}" - ) } else { - let index = self.imported_function_index(function); - format!( - "{class_method} - def {snake}({params}){return_type}: - {docs}result = componentize_py_runtime.call_import({index}, [{args}], {result_count}) - {return_statement}" + self.generate_import_code( + 1, + stub_runtime_calls, + function, + class_method, + &snake, + ¶ms, + &return_type, + &docs, + &args, + &return_statement, ) } }; @@ -1512,12 +1701,14 @@ class {camel}: let docs = docstring(world_module, function.docs, 2, error.as_deref()); - format!( - "{class_method} - @abstractmethod - def {snake}({params}){return_type}: - {docs}{NOT_IMPLEMENTED} -" + self.generate_export_code( + stub_runtime_calls, + function, + class_method, + &snake, + ¶ms, + &return_type, + &docs, ) }; @@ -1548,6 +1739,92 @@ class {camel}(Protocol): | TypeDefKind::Option(_) | TypeDefKind::Result(_) | TypeDefKind::Handle(_) => (None, Vec::new()), + TypeDefKind::Stream(ty) => { + let code = if stream_payloads.contains(ty) { + None + } else { + stream_payloads.insert(ty); + + Some(if let Some(Type::U8 | Type::S8) = ty { + if stub_runtime_calls { + format!( + " +def byte_stream() -> tuple[ByteStreamWriter, ByteStreamReader]: + {NOT_IMPLEMENTED} +" + ) + } else { + let index = *self.stream_and_future_indexes.get(&id).unwrap(); + format!( + " +def byte_stream() -> tuple[ByteStreamWriter, ByteStreamReader]: + pair = componentize_py_runtime.stream_new({index}) + return (ByteStreamWriter({index}, pair >> 32), ByteStreamReader({index}, pair & 0xFFFFFFFF)) +" + ) + } + } else { + let snake = ty + .map(|ty| names.mangle_name(ty)) + .unwrap_or_else(|| "unit".into()); + let camel = ty + .map(|ty| names.type_name(ty, &seen, None)) + .unwrap_or_else(|| "None".into()); + if stub_runtime_calls { + format!( + " +def {snake}_stream() -> tuple[StreamWriter[{camel}], StreamReader[{camel}]]: + {NOT_IMPLEMENTED} +" + ) + } else { + let index = *self.stream_and_future_indexes.get(&id).unwrap(); + format!( + " +def {snake}_stream() -> tuple[StreamWriter[{camel}], StreamReader[{camel}]]: + pair = componentize_py_runtime.stream_new({index}) + return (StreamWriter({index}, pair >> 32), StreamReader({index}, pair & 0xFFFFFFFF)) +" + ) + } + }) + }; + + (code.map(Code::Shared), Vec::new()) + } + TypeDefKind::Future(ty) => { + let code = if future_payloads.contains(ty) { + None + } else { + future_payloads.insert(ty); + + let snake = ty + .map(|ty| names.mangle_name(ty)) + .unwrap_or_else(|| "unit".into()); + let camel = ty + .map(|ty| names.type_name(ty, &seen, None)) + .unwrap_or_else(|| "None".into()); + Some(if stub_runtime_calls { + format!( + " +def {snake}_future(default: Callable[[], {camel}]) -> tuple[FutureWriter[{camel}], FutureReader[{camel}]]: + {NOT_IMPLEMENTED} +" + ) + } else { + let index = *self.stream_and_future_indexes.get(&id).unwrap(); + format!( + " +def {snake}_future(default: Callable[[], {camel}]) -> tuple[FutureWriter[{camel}], FutureReader[{camel}]]: + pair = componentize_py_runtime.future_new({index}) + return (FutureWriter({index}, pair >> 32, default), FutureReader({index}, pair & 0xFFFFFFFF)) +" + ) + }) + }; + + (code.map(Code::Shared), Vec::new()) + } kind => todo!("{kind:?}"), }; @@ -1623,7 +1900,7 @@ class {camel}(Protocol): .collect(), }, - TypeOwner::World(_) => { + TypeOwner::World(_) | TypeOwner::None => { let docs = self.resolve.worlds[world].docs.contents.as_deref(); match code { Code::Shared(code) => vec![(code, (&mut world_exports, docs))], @@ -1634,8 +1911,6 @@ class {camel}(Protocol): .collect(), } } - - TypeOwner::None => unreachable!(), }; for (code, (definitions, docs)) in tuples { @@ -1667,7 +1942,8 @@ class {camel}(Protocol): ) { ( FunctionKind::Import | FunctionKind::Export, - wit_parser::FunctionKind::Freestanding, + wit_parser::FunctionKind::Freestanding + | wit_parser::FunctionKind::AsyncFreestanding, true, ) => { let mut names = TypeNames::new( @@ -1687,7 +1963,6 @@ class {camel}(Protocol): args, return_type, return_statement, - result_count, error, .. } = self.function_code( @@ -1703,23 +1978,18 @@ class {camel}(Protocol): FunctionKind::Import => { let docs = docstring(world_module, function.docs, 1, error.as_deref()); - let code = if stub_runtime_calls { - format!( - " -def {snake}({params}){return_type}: - {docs}{NOT_IMPLEMENTED} -" - ) - } else { - let index = self.imported_function_index(function); - format!( - " -def {snake}({params}){return_type}: - {docs}result = componentize_py_runtime.call_import({index}, [{args}], {result_count}) - {return_statement} -" - ) - }; + let code = self.generate_import_code( + 0, + stub_runtime_calls, + function, + "", + &snake, + ¶ms, + &return_type, + &docs, + &args, + &return_statement, + ); let (definitions, docs) = if let Some(interface) = &function.interface { ( @@ -1765,12 +2035,14 @@ def {snake}({params}){return_type}: let function_docs = docstring(world_module, function.docs, 2, error.as_deref()); - let code = format!( - " - @abstractmethod - def {snake}({params}){return_type}: - {function_docs}{NOT_IMPLEMENTED} -" + let code = self.generate_export_code( + stub_runtime_calls, + function, + "", + &snake, + ¶ms, + &return_type, + &function_docs, ); definitions.functions.push(code); @@ -1787,7 +2059,7 @@ def {snake}({params}){return_type}: } let python_imports = - "from typing import TypeVar, Generic, Union, Optional, Protocol, Tuple, List, Any, Self + "from typing import TypeVar, Generic, Union, Optional, Protocol, Tuple, List, Any, Self, Callable from types import TracebackType from enum import Flag, Enum, auto from dataclasses import dataclass @@ -1795,45 +2067,9 @@ from abc import abstractmethod import weakref "; - { - let mut file = File::create(path.join("types.py"))?; - if let Some(module) = locations.types_module.as_ref() { - writeln!(file, "{}", world_module_import(module, "peer"))?; - write!( - file, - "Some = peer.types.Some -Ok = peer.types.Ok -Err = peer.types.Err -Result = peer.types.Result -" - )?; - } else { - locations.types_module = Some(world_module.to_owned()); - - write!( - file, - "{file_header}{python_imports} - -S = TypeVar('S') -@dataclass -class Some(Generic[S]): - value: S - -T = TypeVar('T') -@dataclass -class Ok(Generic[T]): - value: T - -E = TypeVar('E') -@dataclass(frozen=True) -class Err(Generic[E], Exception): - value: E - -Result = Union[Ok[T], Err[E]] -" - )?; - } - } + let async_imports = "import componentize_py_async_support +from componentize_py_async_support.streams import StreamReader, StreamWriter, ByteStreamReader, ByteStreamWriter +from componentize_py_async_support.futures import FutureReader, FutureWriter"; let import = |prefix, interface| { let (module, package) = self.interface_package(interface); @@ -1854,20 +2090,16 @@ Result = Union[Ok[T], Err[E]] .type_imports .union(&code.function_imports) .map(|&interface| import("..", interface)) + .chain(self.need_async.then(|| async_imports.into())) + .chain((!stub_runtime_calls).then(|| "import componentize_py_runtime".into())) .collect::>() .join("\n"); let docs = docstring(world_module, code.docs, 0, None); - let imports = if stub_runtime_calls { - imports - } else { - format!("import componentize_py_runtime\n{imports}") - }; - write!( file, "{file_header}{docs}{python_imports} -from ..types import Result, Ok, Err, Some +from componentize_py_types import Result, Ok, Err, Some {imports} {types} {functions} @@ -1891,6 +2123,7 @@ from ..types import Result, Ok, Err, Some .type_imports .into_iter() .map(|interface| import("..", interface)) + .chain(self.need_async.then(|| async_imports.into())) .collect::>() .join("\n"); let docs = docstring(world_module, code.docs, 0, None); @@ -1898,7 +2131,7 @@ from ..types import Result, Ok, Err, Some write!( file, "{file_header}{docs}{python_imports} -from ..types import Result, Ok, Err, Some +from componentize_py_types import Result, Ok, Err, Some {imports} {types} " @@ -1939,13 +2172,14 @@ class {camel}(Protocol): let imports = protocol_imports .into_iter() .map(|interface| import("..", interface)) + .chain(self.need_async.then(|| async_imports.into())) .collect::>() .join("\n"); write!( init, "{file_header}{python_imports} -from ..types import Result, Ok, Err, Some +from componentize_py_types import Result, Ok, Err, Some {imports} {protocols} " @@ -1983,21 +2217,17 @@ from ..types import Result, Ok, Err, Some .collect(), ) .map(|&interface| import(".", interface)) + .chain(self.need_async.then(|| async_imports.into())) + .chain((!stub_runtime_calls).then(|| "import componentize_py_runtime".into())) .collect::>() .join("\n"); let docs = docstring(world_module, world_exports.docs, 0, None); - let imports = if stub_runtime_calls { - imports - } else { - format!("import componentize_py_runtime\n{imports}") - }; - write!( file, "{file_header}{docs}{python_imports} -from .types import Result, Ok, Err, Some +from componentize_py_types import Result, Ok, Err, Some {imports} {type_exports} {function_imports} @@ -2093,6 +2323,9 @@ from .types import Result, Ok, Err, Some let info = self.resource_info.get(&id).unwrap_or(empty); info.local && info.remote } + TypeDefKind::Stream(ty) | TypeDefKind::Future(ty) => ty + .map(|ty| self.has_imported_and_exported_resource(ty)) + .unwrap_or(false), kind => todo!("{kind:?}"), }, } @@ -2145,7 +2378,8 @@ impl<'a> TypeNames<'a> { self.imports.insert(interface); format!("{}.", self.summary.interface_package(interface).1) } - // todo: place anonymous types in types.py and import them from there + // todo: place anonymous types in types.py + // and import them from there _ => String::new(), } }; @@ -2161,8 +2395,10 @@ impl<'a> TypeNames<'a> { format!("{package}{name}") } else { - // As of this writing, there's no concept of forward declaration in Python, so we must - // either use `Any` or `Self` for types which have not yet been fully declared. + // As of this writing, there's no concept of forward + // declaration in Python, so we must either use + // `Any` or `Self` for types which have not yet been + // fully declared. if Some(id) == resource { "Self" } else { "Any" }.to_owned() } } @@ -2198,17 +2434,126 @@ impl<'a> TypeNames<'a> { .map(|ty| self.type_name(*ty, seen, resource)) .collect::>() .join(", "); - let types = if types.is_empty() { - "()".to_owned() - } else { - types - }; format!("Tuple[{types}]") } TypeDefKind::Handle(Handle::Own(ty) | Handle::Borrow(ty)) => { self.type_name(Type::Id(*ty), seen, resource) } TypeDefKind::Type(ty) => self.type_name(*ty, seen, resource), + TypeDefKind::Stream(ty) => { + if let Some(Type::U8 | Type::S8) = ty { + "ByteStreamReader".into() + } else { + format!( + "StreamReader[{}]", + ty.map(|ty| self.type_name(ty, seen, resource)) + .unwrap_or_else(|| "None".into()) + ) + } + } + TypeDefKind::Future(ty) => { + format!( + "FutureReader[{}]", + ty.map(|ty| self.type_name(ty, seen, resource)) + .unwrap_or_else(|| "None".into()) + ) + } + kind => todo!("{kind:?}"), + } + } + } + } + + fn mangle_name(&mut self, ty: Type) -> String { + // TODO: Ensure the returned name is always distinct for distinct types + // (e.g. by incorporating interface version numbers and/or additional + // mangling as needed). + match ty { + Type::Bool => "bool".into(), + Type::U8 => "u8".into(), + Type::U16 => "u16".into(), + Type::U32 => "u32".into(), + Type::U64 => "u64".into(), + Type::S8 => "s8".into(), + Type::S16 => "s16".into(), + Type::S32 => "s32".into(), + Type::S64 => "s64".into(), + Type::ErrorContext => "error_context".into(), + Type::F32 => "f32".into(), + Type::F64 => "f64".into(), + Type::Char => "char".into(), + Type::String => "string".into(), + Type::Id(id) => { + let ty = &self.summary.resolve.types[id]; + match &ty.kind { + TypeDefKind::Record(_) + | TypeDefKind::Variant(_) + | TypeDefKind::Enum(_) + | TypeDefKind::Flags(_) + | TypeDefKind::Resource => { + let package = if ty.owner == self.owner { + String::new() + } else { + match ty.owner { + TypeOwner::Interface(interface) => { + format!("{}_", self.summary.interface_package(interface).1) + } + _ => String::new(), + } + }; + + let name = if let Some(name) = &ty.name { + name.to_snake_case().escape() + } else { + format!("anon{}", self.summary.types.get_index_of(&id).unwrap()) + }; + + format!("{package}{name}") + } + TypeDefKind::Option(some) => { + format!("option_{}", self.mangle_name(*some)) + } + TypeDefKind::Result(result) => format!( + "result_{}_{}", + result + .ok + .map(|ty| self.mangle_name(ty)) + .unwrap_or_else(|| "unit".into()), + result + .err + .map(|ty| self.mangle_name(ty)) + .unwrap_or_else(|| "unit".into()) + ), + TypeDefKind::List(ty) => { + format!("list_{}", self.mangle_name(*ty)) + } + TypeDefKind::Tuple(tuple) => { + let types = tuple + .types + .iter() + .map(|ty| self.mangle_name(*ty)) + .collect::>() + .join("_"); + format!("tuple{}_{types}", tuple.types.len()) + } + TypeDefKind::Handle(Handle::Own(ty) | Handle::Borrow(ty)) => { + self.mangle_name(Type::Id(*ty)) + } + TypeDefKind::Type(ty) => self.mangle_name(*ty), + TypeDefKind::Stream(ty) => { + format!( + "stream_{}", + ty.map(|ty| self.mangle_name(ty)) + .unwrap_or_else(|| "unit".into()) + ) + } + TypeDefKind::Future(ty) => { + format!( + "stream_{}", + ty.map(|ty| self.mangle_name(ty)) + .unwrap_or_else(|| "unit".into()) + ) + } kind => todo!("{kind:?}"), } } @@ -2222,8 +2567,8 @@ pub trait Escape { impl Escape for String { fn escape(self) -> Self { - // Escape Python keywords - // Source: https://docs.python.org/3/reference/lexical_analysis.html#keywords + // Escape Python keywords; source: + // https://docs.python.org/3/reference/lexical_analysis.html#keywords match self.as_str() { "False" | "None" | "True" | "and" | "as" | "assert" | "async" | "await" | "break" | "class" | "continue" | "def" | "del" | "elif" | "else" | "except" | "finally" @@ -2240,11 +2585,13 @@ fn matches_resource(function: &MyFunction, resource: TypeId, direction: Directio match (direction, &function.kind) { (Direction::Import, FunctionKind::Import) | (Direction::Export, FunctionKind::Export) => { match &function.wit_kind { - wit_parser::FunctionKind::Freestanding => false, + wit_parser::FunctionKind::Freestanding + | wit_parser::FunctionKind::AsyncFreestanding => false, wit_parser::FunctionKind::Method(id) + | wit_parser::FunctionKind::AsyncMethod(id) | wit_parser::FunctionKind::Static(id) + | wit_parser::FunctionKind::AsyncStatic(id) | wit_parser::FunctionKind::Constructor(id) => *id == resource, - _ => todo!("support async functions"), } } _ => false, diff --git a/src/test.rs b/src/test.rs index 21efb19..d7e548e 100644 --- a/src/test.rs +++ b/src/test.rs @@ -38,6 +38,7 @@ static ENGINE: Lazy = Lazy::new(|| { let mut config = Config::new(); config.async_support(true); config.wasm_component_model(true); + config.wasm_component_model_async(true); Engine::new(&config).unwrap() }); @@ -127,9 +128,11 @@ impl Tester { module_worlds: &[(&str, &str)], seed: [u8; 32], ) -> Result { - // TODO: create two versions of the component -- one with and one without an `add_to_linker` -- and run - // each test on each component in the `test` method (but probably not in the `proptest` method, since that - // would slow it down a lot). This will help exercise the stub mechanism when pre-initializing. + // TODO: create two versions of the component -- one with and one + // without an `add_to_linker` -- and run each test on each component in + // the `test` method (but probably not in the `proptest` method, since + // that would slow it down a lot). This will help exercise the stub + // mechanism when pre-initializing. let component = &Runtime::new()?.block_on(make_component( wit, world_module, diff --git a/src/test/python_source/app.py b/src/test/python_source/app.py index 364477b..7160d79 100644 --- a/src/test/python_source/app.py +++ b/src/test/python_source/app.py @@ -4,11 +4,15 @@ import resource_aggregates import resource_alias1 import resource_borrow_in_record +import componentize_py_async_support + +from componentize_py_types import Result, Ok, Err from tests import exports, imports from tests.imports import resource_borrow_import from tests.imports import simple_import_and_export +from tests.imports import simple_async_import_and_export from tests.exports import resource_alias2 -from tests.types import Result, Ok, Err +from tests.exports import streams_and_futures from typing import Tuple, List, Optional from foo_sdk.wit import exports as foo_exports from foo_sdk.wit.imports.foo_interface import test as foo_test @@ -23,6 +27,14 @@ class SimpleImportAndExport(exports.SimpleImportAndExport): def foo(self, v: int) -> int: return simple_import_and_export.foo(v) + 3 +class SimpleAsyncExport(exports.SimpleAsyncExport): + async def foo(self, v: int) -> int: + return v + 3 + +class SimpleAsyncImportAndExport(exports.SimpleAsyncImportAndExport): + async def foo(self, v: int) -> int: + return (await simple_async_import_and_export.foo(v)) + 3 + class ResourceImportAndExport(exports.ResourceImportAndExport): pass @@ -118,6 +130,42 @@ def test(self, a: List[exports.resource_borrow_in_record.Foo]) -> List[resource_ ) ) +async def pipe_bytes(rx: ByteStreamReader, tx: ByteStreamWriter): + while not (rx.writer_dropped or tx.reader_dropped): + await tx.write_all(await rx.read(1024)) + +async def pipe_strings(rx: FutureReader[str], tx: StreamReader[str]): + await tx.write(await rx.read()) + +async def pipe_things(rx: StreamReader[streams_and_futures.Thing], tx: StreamWriter[streams_and_futures.Thing]): + # Read the things one at a time, forcing the host to re-take ownership of + # any unwritten items between writes. + things = [] + while not rx.writer_dropped: + things += await rx.read(1) + + # Write the things all at once. The host will read them only one at a time, + # forcing us to re-take ownership of any unwritten items between writes. + await tx.write_all(things) + +class StreamsAndFutures(exports.StreamsAndFutures): + async def echo_stream_u8(self, stream: ByteStreamReader) -> ByteStreamReader: + tx, rx = tests.byte_stream() + componentize_py_async_support.spawn(pipe_bytes(stream, tx)) + return rx + + async def echo_future_string(self, future: FutureReader[str]) -> FutureReader[str]: + def unreachable() -> str: + raise AssertionError + tx, rx = tests.string_future(unreachable) + componentize_py_async_support.spawn(pipe_strings(future, tx)) + return rx + + async def short_reads(self, stream: StreamReader[streams_and_futures.Thing]) -> StreamReader[streams_and_futures.Thing]: + tx, rx = tests.streams_and_futures_thing_stream() + componentize_py_async_support.spawn(pipe_things(stream, tx)) + return rx + class Tests(tests.Tests): def test_resource_borrow_import(self, v: int) -> int: return resource_borrow_import.foo(resource_borrow_import.Thing(v + 1)) + 4 diff --git a/src/test/python_source/streams_and_futures.py b/src/test/python_source/streams_and_futures.py new file mode 100644 index 0000000..58281cc --- /dev/null +++ b/src/test/python_source/streams_and_futures.py @@ -0,0 +1,8 @@ +from tests.exports import streams_and_futures + +class Thing(streams_and_futures.Thing): + def __init__(self, v: str): + self.value = v + + async def get(self) -> str: + return self.value diff --git a/src/test/tests.rs b/src/test/tests.rs index 77e1249..13a7944 100644 --- a/src/test/tests.rs +++ b/src/test/tests.rs @@ -3,11 +3,25 @@ use { super::{Ctx, SEED, Tester}, anyhow::{Error, Result, anyhow}, + exports::componentize_py::test::streams_and_futures, + futures::FutureExt, once_cell::sync::Lazy, - std::str, + std::{ + mem, + ops::DerefMut, + pin::Pin, + str, + sync::{Arc, Mutex}, + task::{self, Context, Poll}, + time::Duration, + }, wasmtime::{ - Store, - component::{HasSelf, InstancePre, Linker, Resource, ResourceAny}, + Store, StoreContextMut, + component::{ + Accessor, Destination, FutureConsumer, FutureProducer, FutureReader, HasSelf, + InstancePre, Lift, Linker, Resource, ResourceAny, Source, StreamConsumer, + StreamProducer, StreamReader, StreamResult, VecBuffer, + }, }, wasmtime_wasi::{DirPerms, FilePerms, WasiCtxBuilder, WasiView}, }; @@ -16,16 +30,16 @@ wasmtime::component::bindgen!({ path: "src/test/wit", world: "tests", imports: { default: async | trappable }, - exports: { default: async }, + exports: { default: async | task_exit }, with: { - "componentize-py:test/resource-import-and-export/thing": ThingU32, - "componentize-py:test/resource-borrow-import/thing": ThingU32, - "componentize-py:test/resource-with-lists/thing": ThingList, - "componentize-py:test/resource-aggregates/thing": ThingU32, - "componentize-py:test/resource-alias1/thing": ThingString, - "componentize-py:test/resource-floats/float": MyFloat, - "resource-floats-imports/float": MyFloat, - "componentize-py:test/resource-borrow-in-record/thing": ThingString, + "componentize-py:test/resource-import-and-export.thing": ThingU32, + "componentize-py:test/resource-borrow-import.thing": ThingU32, + "componentize-py:test/resource-with-lists.thing": ThingList, + "componentize-py:test/resource-aggregates.thing": ThingU32, + "componentize-py:test/resource-alias1.thing": ThingString, + "componentize-py:test/resource-floats.float": MyFloat, + "resource-floats-imports.float": MyFloat, + "componentize-py:test/resource-borrow-in-record.thing": ThingString, }, }); @@ -83,7 +97,8 @@ const GUEST_CODE: &[(&str, &str)] = load_guest_code!( "resource_aggregates.py", "resource_alias1.py", "resource_floats_exports.py", - "resource_borrow_in_record.py" + "resource_borrow_in_record.py", + "streams_and_futures.py" ); struct Host; @@ -163,6 +178,29 @@ fn simple_export() -> Result<()> { }) } +#[test] +fn simple_async_export() -> Result<()> { + TESTER.test(|world, store, runtime| { + assert_eq!( + 42 + 3, + runtime + .block_on(async { + store + .run_concurrent(async |store| { + world + .componentize_py_test_simple_async_export() + .call_foo(store, 42) + .await + }) + .await? + })? + .0 + ); + + Ok(()) + }) +} + #[test] fn simple_import_and_export() -> Result<()> { impl componentize_py::test::simple_import_and_export::Host for Ctx { @@ -185,6 +223,38 @@ fn simple_import_and_export() -> Result<()> { }) } +#[test] +fn simple_async_import_and_export() -> Result<()> { + impl componentize_py::test::simple_async_import_and_export::Host for Ctx {} + + impl componentize_py::test::simple_async_import_and_export::HostWithStore for HasSelf { + async fn foo(_: &Accessor, v: u32) -> Result { + tokio::time::sleep(Duration::from_millis(10)).await; + Ok(v + 2) + } + } + + TESTER.test(|world, store, runtime| { + assert_eq!( + 42 + 2 + 3, + runtime + .block_on(async { + store + .run_concurrent(async |store| { + world + .componentize_py_test_simple_async_import_and_export() + .call_foo(store, 42) + .await + }) + .await? + })? + .0 + ); + + Ok(()) + }) +} + #[test] fn resource_import_and_export() -> Result<()> { use componentize_py::test::resource_import_and_export::{Host, HostThing}; @@ -816,3 +886,346 @@ fn filesystem() -> Result<()> { }) }) } + +struct VecProducer { + source: Vec, + sleep: Pin + Send>>, +} + +impl VecProducer { + fn new(source: Vec, delay: bool) -> Self { + Self { + source, + sleep: if delay { + tokio::time::sleep(Duration::from_millis(10)).boxed() + } else { + async {}.boxed() + }, + } + } +} + +impl StreamProducer for VecProducer { + type Item = T; + type Buffer = VecBuffer; + + fn poll_produce( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + _: StoreContextMut, + mut destination: Destination, + _: bool, + ) -> Poll> { + let sleep = &mut self.as_mut().get_mut().sleep; + task::ready!(sleep.as_mut().poll(cx)); + *sleep = async {}.boxed(); + + destination.set_buffer(mem::take(&mut self.get_mut().source).into()); + Poll::Ready(Ok(StreamResult::Dropped)) + } +} + +struct VecConsumer { + destination: Arc>>, + sleep: Pin + Send>>, +} + +impl VecConsumer { + fn new(destination: Arc>>, delay: bool) -> Self { + Self { + destination, + sleep: if delay { + tokio::time::sleep(Duration::from_millis(10)).boxed() + } else { + async {}.boxed() + }, + } + } +} + +impl StreamConsumer for VecConsumer { + type Item = T; + + fn poll_consume( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + store: StoreContextMut, + mut source: Source, + _: bool, + ) -> Poll> { + let sleep = &mut self.as_mut().get_mut().sleep; + task::ready!(sleep.as_mut().poll(cx)); + *sleep = async {}.boxed(); + + source.read(store, self.destination.lock().unwrap().deref_mut())?; + Poll::Ready(Ok(StreamResult::Completed)) + } +} + +#[test] +fn echo_stream_u8() -> Result<()> { + test_echo_stream_u8(false) +} + +#[test] +fn echo_stream_u8_with_delay() -> Result<()> { + test_echo_stream_u8(true) +} + +fn test_echo_stream_u8(delay: bool) -> Result<()> { + TESTER.test(|world, store, runtime| { + runtime.block_on(async { + store + .run_concurrent(async |store| { + let expected = + b"Beware the Jubjub bird, and shun\n\tThe frumious Bandersnatch!"; + let stream = store.with(|store| { + StreamReader::new(store, VecProducer::new(expected.to_vec(), delay)) + }); + + let (stream, task) = world + .componentize_py_test_streams_and_futures() + .call_echo_stream_u8(store, stream) + .await?; + + let received = Arc::new(Mutex::new(Vec::with_capacity(expected.len()))); + store.with(|store| { + stream.pipe(store, VecConsumer::new(received.clone(), delay)) + }); + + task.block(store).await; + + assert_eq!(expected, &received.lock().unwrap()[..]); + + anyhow::Ok(()) + }) + .await? + })?; + + Ok(()) + }) +} + +struct OptionProducer { + source: Option, + sleep: Pin + Send>>, +} + +impl OptionProducer { + fn new(source: Option, delay: bool) -> Self { + Self { + source, + sleep: if delay { + tokio::time::sleep(Duration::from_millis(10)).boxed() + } else { + async {}.boxed() + }, + } + } +} + +impl FutureProducer for OptionProducer { + type Item = T; + + fn poll_produce( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + _: StoreContextMut, + _: bool, + ) -> Poll>> { + let sleep = &mut self.as_mut().get_mut().sleep; + task::ready!(sleep.as_mut().poll(cx)); + *sleep = async {}.boxed(); + + Poll::Ready(Ok(self.get_mut().source.take())) + } +} + +struct OptionConsumer { + destination: Arc>>, + sleep: Pin + Send>>, +} + +impl OptionConsumer { + fn new(destination: Arc>>, delay: bool) -> Self { + Self { + destination, + sleep: if delay { + tokio::time::sleep(Duration::from_millis(10)).boxed() + } else { + async {}.boxed() + }, + } + } +} + +impl FutureConsumer for OptionConsumer { + type Item = T; + + fn poll_consume( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + store: StoreContextMut, + mut source: Source, + _: bool, + ) -> Poll> { + let sleep = &mut self.as_mut().get_mut().sleep; + task::ready!(sleep.as_mut().poll(cx)); + *sleep = async {}.boxed(); + + source.read(store, self.destination.lock().unwrap().deref_mut())?; + Poll::Ready(Ok(())) + } +} + +#[test] +fn echo_future_string() -> Result<()> { + test_echo_future_string(false) +} + +#[test] +fn echo_future_string_with_delay() -> Result<()> { + test_echo_future_string(true) +} + +fn test_echo_future_string(delay: bool) -> Result<()> { + TESTER.test(|world, store, runtime| { + runtime.block_on(async { + store + .run_concurrent(async |store| { + let expected = "Beware the Jubjub bird, and shun\n\tThe frumious Bandersnatch!"; + let future = store.with(|store| { + FutureReader::new( + store, + OptionProducer::new(Some(expected.to_string()), delay), + ) + }); + + let (future, task) = world + .componentize_py_test_streams_and_futures() + .call_echo_future_string(store, future) + .await?; + + let received = Arc::new(Mutex::new(None::)); + store.with(|store| { + future.pipe(store, OptionConsumer::new(received.clone(), delay)) + }); + + task.block(store).await; + + assert_eq!( + expected, + received.lock().unwrap().as_ref().unwrap().as_str() + ); + + anyhow::Ok(()) + }) + .await? + })?; + + Ok(()) + }) +} + +struct OneAtATime { + destination: Arc>>, + sleep: Pin + Send>>, +} + +impl OneAtATime { + fn new(destination: Arc>>, delay: bool) -> Self { + Self { + destination, + sleep: if delay { + tokio::time::sleep(Duration::from_millis(10)).boxed() + } else { + async {}.boxed() + }, + } + } +} + +impl StreamConsumer for OneAtATime { + type Item = T; + + fn poll_consume( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + store: StoreContextMut, + mut source: Source, + _: bool, + ) -> Poll> { + let sleep = &mut self.as_mut().get_mut().sleep; + task::ready!(sleep.as_mut().poll(cx)); + *sleep = async {}.boxed(); + + let value = &mut None; + source.read(store, value)?; + self.destination.lock().unwrap().push(value.take().unwrap()); + Poll::Ready(Ok(StreamResult::Completed)) + } +} + +#[test] +fn short_reads() -> Result<()> { + test_short_reads(false) +} + +#[test] +fn short_reads_with_delay() -> Result<()> { + test_short_reads(true) +} + +fn test_short_reads(delay: bool) -> Result<()> { + TESTER.test(|world, mut store, runtime| { + runtime.block_on(async { + let instance = world.componentize_py_test_streams_and_futures(); + let thing = instance.thing(); + + let strings = ["a", "b", "c", "d", "e"]; + let mut things = Vec::with_capacity(strings.len()); + for string in strings { + things.push(thing.call_constructor(&mut store, string).await?); + } + + store + .run_concurrent(async |store| { + let count = things.len(); + let stream = store + .with(|store| StreamReader::new(store, VecProducer::new(things, delay))); + + let (stream, task) = instance.call_short_reads(store, stream).await?; + + let received_things = Arc::new(Mutex::new( + Vec::::with_capacity(count), + )); + store.with(|store| { + stream.pipe(store, OneAtATime::new(received_things.clone(), delay)) + }); + + task.block(store).await; + + assert_eq!(count, received_things.lock().unwrap().len()); + + let mut received_strings = Vec::with_capacity(strings.len()); + let received_things = mem::take(received_things.lock().unwrap().deref_mut()); + for it in received_things { + received_strings.push(thing.call_get(store, it).await?.0); + } + + assert_eq!( + &strings[..], + &received_strings + .iter() + .map(|s| s.as_str()) + .collect::>() + ); + + anyhow::Ok(()) + }) + .await? + })?; + + Ok(()) + }) +} diff --git a/src/test/wit/tests.wit b/src/test/wit/tests.wit index 48aa3cf..cf9ca8b 100644 --- a/src/test/wit/tests.wit +++ b/src/test/wit/tests.wit @@ -4,10 +4,18 @@ interface simple-export { foo: func(v: u32) -> u32; } +interface simple-async-export { + foo: async func(v: u32) -> u32; +} + interface simple-import-and-export { foo: func(v: u32) -> u32; } +interface simple-async-import-and-export { + foo: async func(v: u32) -> u32; +} + interface resource-import-and-export { resource thing { constructor(v: u32); @@ -134,6 +142,17 @@ interface resource-borrow-in-record { test: func(a: list) -> list; } +interface streams-and-futures { + resource thing { + constructor(s: string); + get: async func() -> string; + } + + echo-stream-u8: async func(s: stream) -> stream; + echo-future-string: async func(f: future) -> future; + short-reads: async func(s: stream) -> stream; +} + world tests { use resource-alias1.{thing}; use resource-floats.{float}; @@ -141,6 +160,9 @@ world tests { export simple-export; import simple-import-and-export; export simple-import-and-export; + export simple-async-export; + import simple-async-import-and-export; + export simple-async-import-and-export; import resource-import-and-export; export resource-import-and-export; import resource-borrow-import; @@ -155,6 +177,7 @@ world tests { export resource-alias2; import resource-borrow-in-record; export resource-borrow-in-record; + export streams-and-futures; export resource-floats-exports: interface { resource float { diff --git a/test-generator/Cargo.toml b/test-generator/Cargo.toml index 2ec71ff..c4a710d 100644 --- a/test-generator/Cargo.toml +++ b/test-generator/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "test-generator" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] anyhow = "1.0.91" diff --git a/test-generator/src/lib.rs b/test-generator/src/lib.rs index 8e1a8b2..a38d4ef 100644 --- a/test-generator/src/lib.rs +++ b/test-generator/src/lib.rs @@ -1,7 +1,7 @@ #![deny(warnings)] use { - anyhow::{anyhow, Result}, + anyhow::{Result, anyhow}, proptest::{ strategy::{Just, Strategy, ValueTree}, test_runner::{Config, TestRng, TestRunner}, @@ -11,8 +11,9 @@ use { const DEFAULT_TEST_COUNT: usize = 10; const MAX_LIST_SIZE: usize = 100; -// As of this writing, neither `Debug` nor `Strategy` are implemented for tuples of more than twelve elements. -// Technically we could work around this, but it's probably more trouble than it's worth. +// As of this writing, neither `Debug` nor `Strategy` are implemented for tuples +// of more than twelve elements. Technically we could work around this, but +// it's probably more trouble than it's worth. const MAX_TUPLE_SIZE: usize = 12; // See note above about `MAX_TUPLE_SIZE` const MAX_PARAM_COUNT: usize = 12; diff --git a/tests/bindings.rs b/tests/bindings.rs index 1ef1de0..f1b6c48 100644 --- a/tests/bindings.rs +++ b/tests/bindings.rs @@ -1,9 +1,10 @@ use std::{ ffi::OsStr, + fs, path::{Path, PathBuf}, }; -use assert_cmd::{Command, assert::Assert}; +use assert_cmd::{Command, assert::Assert, cargo}; use flate2::bufread::GzDecoder; use fs_extra::dir::CopyOptions; use predicates::{Predicate, prelude::predicate}; @@ -13,7 +14,7 @@ use tar::Archive; fn lint_cli_bindings() -> anyhow::Result<()> { let dir = tempfile::tempdir()?; fs_extra::copy_items( - &["./examples/cli", "./wit"], + &["./examples/cli", "./wit", "./bundled"], dir.path(), &CopyOptions::new(), )?; @@ -32,10 +33,12 @@ fn lint_cli_bindings() -> anyhow::Result<()> { fn lint_http_bindings() -> anyhow::Result<()> { let dir = tempfile::tempdir()?; fs_extra::copy_items( - &["./examples/http", "./wit"], + &["./examples/http", "./wit", "./bundled"], dir.path(), &CopyOptions::new(), )?; + // poll_loop.py has many errors that might not be worth adjusting at the moment, so ignore for now + fs::remove_file(dir.path().join("bundled/poll_loop.py")).unwrap(); let path = dir.path().join("http"); generate_bindings(&path, "wasi:http/proxy@0.2.0")?; @@ -58,11 +61,32 @@ fn lint_http_bindings() -> anyhow::Result<()> { Ok(()) } +#[test] +fn lint_http_p3_bindings() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + fs_extra::copy_items( + &["./examples/http-p3", "./wit", "./bundled"], + dir.path(), + &CopyOptions::new(), + )?; + let path = dir.path().join("http-p3"); + + generate_bindings(&path, "wasi:http/proxy@0.3.0-rc-2025-09-16")?; + + assert!(predicate::path::is_dir().eval(&path.join("wit_world"))); + + _ = dir.keep(); + + mypy_check(&path, ["--strict", "-m", "app", "-p", "wit_world"]); + + Ok(()) +} + #[test] fn lint_matrix_math_bindings() -> anyhow::Result<()> { let dir = tempfile::tempdir()?; fs_extra::copy_items( - &["./examples/matrix-math", "./wit"], + &["./examples/matrix-math", "./wit", "./bundled"], dir.path(), &CopyOptions::new(), )?; @@ -94,10 +118,14 @@ fn lint_matrix_math_bindings() -> anyhow::Result<()> { #[test] fn lint_sandbox_bindings() -> anyhow::Result<()> { let dir = tempfile::tempdir()?; - fs_extra::copy_items(&["./examples/sandbox"], dir.path(), &CopyOptions::new())?; + fs_extra::copy_items( + &["./examples/sandbox", "./bundled"], + dir.path(), + &CopyOptions::new(), + )?; let path = dir.path().join("sandbox"); - Command::cargo_bin("componentize-py")? + cargo::cargo_bin_cmd!("componentize-py") .current_dir(&path) .args(["-d", "sandbox.wit", "bindings", "."]) .assert() @@ -114,7 +142,7 @@ fn lint_sandbox_bindings() -> anyhow::Result<()> { fn lint_tcp_bindings() -> anyhow::Result<()> { let dir = tempfile::tempdir()?; fs_extra::copy_items( - &["./examples/tcp", "./wit"], + &["./examples/tcp", "./wit", "./bundled"], dir.path(), &CopyOptions::new(), )?; @@ -130,7 +158,7 @@ fn lint_tcp_bindings() -> anyhow::Result<()> { } fn generate_bindings(path: &Path, world: &str) -> Result { - Ok(Command::cargo_bin("componentize-py")? + Ok(cargo::cargo_bin_cmd!("componentize-py") .current_dir(path) .args(["-d", "../wit", "-w", world, "bindings", "."]) .assert() @@ -156,6 +184,14 @@ where Command::new(venv_path(path).join("mypy")) .current_dir(path) + .env( + "MYPYPATH", + ["bundled"] + .into_iter() + .map(|v| path.parent().unwrap().join(v).to_str().unwrap().to_string()) + .collect::>() + .join(":"), + ) .args(args) .assert() .success() diff --git a/tests/componentize.rs b/tests/componentize.rs index 5fb305d..675e143 100644 --- a/tests/componentize.rs +++ b/tests/componentize.rs @@ -6,7 +6,7 @@ use std::{ time::Duration, }; -use assert_cmd::Command; +use assert_cmd::{Command, cargo}; use flate2::bufread::GzDecoder; use fs_extra::dir::CopyOptions; use predicates::prelude::predicate; @@ -22,7 +22,7 @@ fn cli_example() -> anyhow::Result<()> { )?; let path = dir.path().join("cli"); - Command::cargo_bin("componentize-py")? + cargo::cargo_bin_cmd!("componentize-py") .current_dir(&path) .args([ "-d", @@ -50,21 +50,30 @@ fn cli_example() -> anyhow::Result<()> { #[test] fn http_example() -> anyhow::Result<()> { + test_http_example("http", "wasi:http/proxy@0.2.0", 8080) +} + +#[test] +fn http_p3_example() -> anyhow::Result<()> { + test_http_example("http-p3", "wasi:http/proxy@0.3.0-rc-2025-09-16", 8081) +} + +fn test_http_example(name: &str, world: &str, port: u16) -> anyhow::Result<()> { let dir = tempfile::tempdir()?; fs_extra::copy_items( - &["./examples/http", "./wit"], + &[format!("./examples/{name}").as_str(), "./wit"], dir.path(), &CopyOptions::new(), )?; - let path = dir.path().join("http"); + let path = dir.path().join(name); - Command::cargo_bin("componentize-py")? + cargo::cargo_bin_cmd!("componentize-py") .current_dir(&path) .args([ "-d", "../wit", "-w", - "wasi:http/proxy@0.2.0", + world, "componentize", "app", "-o", @@ -76,7 +85,13 @@ fn http_example() -> anyhow::Result<()> { let mut handle = std::process::Command::new("wasmtime") .current_dir(&path) - .args(["serve", "-Scli", "http.wasm"]) + .args([ + "serve", + &format!("--addr=0.0.0.0:{port}"), + "-Sp3,common", + "-Wcomponent-model-async", + "http.wasm", + ]) .spawn()?; let content = "’Twas brillig, and the slithy toves @@ -89,7 +104,7 @@ All mimsy were the borogoves, let text = retry(|| { Ok(client - .post("http://127.0.0.1:8080/echo") + .post(format!("http://127.0.0.1:{port}/echo")) .header("content-type", "text/plain") .body(content) .send()? @@ -100,7 +115,7 @@ All mimsy were the borogoves, let text = retry(|| { Ok(client - .get("http://127.0.0.1:8080/hash-all") + .get(format!("http://127.0.0.1:{port}/hash-all")) .header("url", "https://webassembly.github.io/spec/core/") .header("url", "https://www.w3.org/groups/wg/wasm/") .header("url", "https://bytecodealliance.org/") @@ -129,7 +144,7 @@ fn matrix_math_example() -> anyhow::Result<()> { install_numpy(&path)?; - Command::cargo_bin("componentize-py")? + cargo::cargo_bin_cmd!("componentize-py") .current_dir(&path) .args([ "-d", @@ -166,7 +181,7 @@ fn sandbox_example() -> anyhow::Result<()> { fs_extra::copy_items(&["./examples/sandbox"], dir.path(), &CopyOptions::new())?; let path = dir.path().join("sandbox"); - Command::cargo_bin("componentize-py")? + cargo::cargo_bin_cmd!("componentize-py") .current_dir(&path) .args([ "-d", @@ -225,7 +240,7 @@ fn tcp_example() -> anyhow::Result<()> { )?; let path = dir.path().join("tcp"); - Command::cargo_bin("componentize-py")? + cargo::cargo_bin_cmd!("componentize-py") .current_dir(&path) .args([ "-d", diff --git a/wit/deps/cli-0.3.0-rc-2025-09-16/command.wit b/wit/deps/cli-0.3.0-rc-2025-09-16/command.wit new file mode 100644 index 0000000..f2f613e --- /dev/null +++ b/wit/deps/cli-0.3.0-rc-2025-09-16/command.wit @@ -0,0 +1,10 @@ +package wasi:cli@0.3.0-rc-2025-09-16; + +@since(version = 0.3.0-rc-2025-09-16) +world command { + @since(version = 0.3.0-rc-2025-09-16) + include imports; + + @since(version = 0.3.0-rc-2025-09-16) + export run; +} diff --git a/wit/deps/cli-0.3.0-rc-2025-09-16/environment.wit b/wit/deps/cli-0.3.0-rc-2025-09-16/environment.wit new file mode 100644 index 0000000..3763f2f --- /dev/null +++ b/wit/deps/cli-0.3.0-rc-2025-09-16/environment.wit @@ -0,0 +1,22 @@ +@since(version = 0.3.0-rc-2025-09-16) +interface environment { + /// Get the POSIX-style environment variables. + /// + /// Each environment variable is provided as a pair of string variable names + /// and string value. + /// + /// Morally, these are a value import, but until value imports are available + /// in the component model, this import function should return the same + /// values each time it is called. + @since(version = 0.3.0-rc-2025-09-16) + get-environment: func() -> list>; + + /// Get the POSIX-style arguments to the program. + @since(version = 0.3.0-rc-2025-09-16) + get-arguments: func() -> list; + + /// Return a path that programs should use as their initial current working + /// directory, interpreting `.` as shorthand for this. + @since(version = 0.3.0-rc-2025-09-16) + get-initial-cwd: func() -> option; +} diff --git a/wit/deps/cli-0.3.0-rc-2025-09-16/exit.wit b/wit/deps/cli-0.3.0-rc-2025-09-16/exit.wit new file mode 100644 index 0000000..1efba7d --- /dev/null +++ b/wit/deps/cli-0.3.0-rc-2025-09-16/exit.wit @@ -0,0 +1,17 @@ +@since(version = 0.3.0-rc-2025-09-16) +interface exit { + /// Exit the current instance and any linked instances. + @since(version = 0.3.0-rc-2025-09-16) + exit: func(status: result); + + /// Exit the current instance and any linked instances, reporting the + /// specified status code to the host. + /// + /// The meaning of the code depends on the context, with 0 usually meaning + /// "success", and other values indicating various types of failure. + /// + /// This function does not return; the effect is analogous to a trap, but + /// without the connotation that something bad has happened. + @unstable(feature = cli-exit-with-code) + exit-with-code: func(status-code: u8); +} diff --git a/wit/deps/cli-0.3.0-rc-2025-09-16/imports.wit b/wit/deps/cli-0.3.0-rc-2025-09-16/imports.wit new file mode 100644 index 0000000..660a2dd --- /dev/null +++ b/wit/deps/cli-0.3.0-rc-2025-09-16/imports.wit @@ -0,0 +1,34 @@ +package wasi:cli@0.3.0-rc-2025-09-16; + +@since(version = 0.3.0-rc-2025-09-16) +world imports { + @since(version = 0.3.0-rc-2025-09-16) + include wasi:clocks/imports@0.3.0-rc-2025-09-16; + @since(version = 0.3.0-rc-2025-09-16) + include wasi:filesystem/imports@0.3.0-rc-2025-09-16; + @since(version = 0.3.0-rc-2025-09-16) + include wasi:sockets/imports@0.3.0-rc-2025-09-16; + @since(version = 0.3.0-rc-2025-09-16) + include wasi:random/imports@0.3.0-rc-2025-09-16; + + @since(version = 0.3.0-rc-2025-09-16) + import environment; + @since(version = 0.3.0-rc-2025-09-16) + import exit; + @since(version = 0.3.0-rc-2025-09-16) + import stdin; + @since(version = 0.3.0-rc-2025-09-16) + import stdout; + @since(version = 0.3.0-rc-2025-09-16) + import stderr; + @since(version = 0.3.0-rc-2025-09-16) + import terminal-input; + @since(version = 0.3.0-rc-2025-09-16) + import terminal-output; + @since(version = 0.3.0-rc-2025-09-16) + import terminal-stdin; + @since(version = 0.3.0-rc-2025-09-16) + import terminal-stdout; + @since(version = 0.3.0-rc-2025-09-16) + import terminal-stderr; +} diff --git a/wit/deps/cli-0.3.0-rc-2025-09-16/run.wit b/wit/deps/cli-0.3.0-rc-2025-09-16/run.wit new file mode 100644 index 0000000..631441a --- /dev/null +++ b/wit/deps/cli-0.3.0-rc-2025-09-16/run.wit @@ -0,0 +1,6 @@ +@since(version = 0.3.0-rc-2025-09-16) +interface run { + /// Run the program. + @since(version = 0.3.0-rc-2025-09-16) + run: async func() -> result; +} diff --git a/wit/deps/cli-0.3.0-rc-2025-09-16/stdio.wit b/wit/deps/cli-0.3.0-rc-2025-09-16/stdio.wit new file mode 100644 index 0000000..51e5ae4 --- /dev/null +++ b/wit/deps/cli-0.3.0-rc-2025-09-16/stdio.wit @@ -0,0 +1,65 @@ +@since(version = 0.3.0-rc-2025-09-16) +interface types { + @since(version = 0.3.0-rc-2025-09-16) + enum error-code { + /// Input/output error + io, + /// Invalid or incomplete multibyte or wide character + illegal-byte-sequence, + /// Broken pipe + pipe, + } +} + +@since(version = 0.3.0-rc-2025-09-16) +interface stdin { + use types.{error-code}; + + /// Return a stream for reading from stdin. + /// + /// This function returns a stream which provides data read from stdin, + /// and a future to signal read results. + /// + /// If the stream's readable end is dropped the future will resolve to success. + /// + /// If the stream's writable end is dropped the future will either resolve to + /// success if stdin was closed by the writer or to an error-code if reading + /// failed for some other reason. + /// + /// Multiple streams may be active at the same time. The behavior of concurrent + /// reads is implementation-specific. + @since(version = 0.3.0-rc-2025-09-16) + read-via-stream: func() -> tuple, future>>; +} + +@since(version = 0.3.0-rc-2025-09-16) +interface stdout { + use types.{error-code}; + + /// Write the given stream to stdout. + /// + /// If the stream's writable end is dropped this function will either return + /// success once the entire contents of the stream have been written or an + /// error-code representing a failure. + /// + /// Otherwise if there is an error the readable end of the stream will be + /// dropped and this function will return an error-code. + @since(version = 0.3.0-rc-2025-09-16) + write-via-stream: async func(data: stream) -> result<_, error-code>; +} + +@since(version = 0.3.0-rc-2025-09-16) +interface stderr { + use types.{error-code}; + + /// Write the given stream to stderr. + /// + /// If the stream's writable end is dropped this function will either return + /// success once the entire contents of the stream have been written or an + /// error-code representing a failure. + /// + /// Otherwise if there is an error the readable end of the stream will be + /// dropped and this function will return an error-code. + @since(version = 0.3.0-rc-2025-09-16) + write-via-stream: async func(data: stream) -> result<_, error-code>; +} diff --git a/wit/deps/cli-0.3.0-rc-2025-09-16/terminal.wit b/wit/deps/cli-0.3.0-rc-2025-09-16/terminal.wit new file mode 100644 index 0000000..74c1769 --- /dev/null +++ b/wit/deps/cli-0.3.0-rc-2025-09-16/terminal.wit @@ -0,0 +1,62 @@ +/// Terminal input. +/// +/// In the future, this may include functions for disabling echoing, +/// disabling input buffering so that keyboard events are sent through +/// immediately, querying supported features, and so on. +@since(version = 0.3.0-rc-2025-09-16) +interface terminal-input { + /// The input side of a terminal. + @since(version = 0.3.0-rc-2025-09-16) + resource terminal-input; +} + +/// Terminal output. +/// +/// In the future, this may include functions for querying the terminal +/// size, being notified of terminal size changes, querying supported +/// features, and so on. +@since(version = 0.3.0-rc-2025-09-16) +interface terminal-output { + /// The output side of a terminal. + @since(version = 0.3.0-rc-2025-09-16) + resource terminal-output; +} + +/// An interface providing an optional `terminal-input` for stdin as a +/// link-time authority. +@since(version = 0.3.0-rc-2025-09-16) +interface terminal-stdin { + @since(version = 0.3.0-rc-2025-09-16) + use terminal-input.{terminal-input}; + + /// If stdin is connected to a terminal, return a `terminal-input` handle + /// allowing further interaction with it. + @since(version = 0.3.0-rc-2025-09-16) + get-terminal-stdin: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stdout as a +/// link-time authority. +@since(version = 0.3.0-rc-2025-09-16) +interface terminal-stdout { + @since(version = 0.3.0-rc-2025-09-16) + use terminal-output.{terminal-output}; + + /// If stdout is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + @since(version = 0.3.0-rc-2025-09-16) + get-terminal-stdout: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stderr as a +/// link-time authority. +@since(version = 0.3.0-rc-2025-09-16) +interface terminal-stderr { + @since(version = 0.3.0-rc-2025-09-16) + use terminal-output.{terminal-output}; + + /// If stderr is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + @since(version = 0.3.0-rc-2025-09-16) + get-terminal-stderr: func() -> option; +} diff --git a/wit/deps/clocks-0.3.0-rc-2025-09-16/monotonic-clock.wit b/wit/deps/clocks-0.3.0-rc-2025-09-16/monotonic-clock.wit new file mode 100644 index 0000000..a91d495 --- /dev/null +++ b/wit/deps/clocks-0.3.0-rc-2025-09-16/monotonic-clock.wit @@ -0,0 +1,48 @@ +package wasi:clocks@0.3.0-rc-2025-09-16; +/// WASI Monotonic Clock is a clock API intended to let users measure elapsed +/// time. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A monotonic clock is a clock which has an unspecified initial value, and +/// successive reads of the clock will produce non-decreasing values. +@since(version = 0.3.0-rc-2025-09-16) +interface monotonic-clock { + use types.{duration}; + + /// An instant in time, in nanoseconds. An instant is relative to an + /// unspecified initial value, and can only be compared to instances from + /// the same monotonic-clock. + @since(version = 0.3.0-rc-2025-09-16) + type instant = u64; + + /// Read the current value of the clock. + /// + /// The clock is monotonic, therefore calling this function repeatedly will + /// produce a sequence of non-decreasing values. + /// + /// For completeness, this function traps if it's not possible to represent + /// the value of the clock in an `instant`. Consequently, implementations + /// should ensure that the starting time is low enough to avoid the + /// possibility of overflow in practice. + @since(version = 0.3.0-rc-2025-09-16) + now: func() -> instant; + + /// Query the resolution of the clock. Returns the duration of time + /// corresponding to a clock tick. + @since(version = 0.3.0-rc-2025-09-16) + get-resolution: func() -> duration; + + /// Wait until the specified instant has occurred. + @since(version = 0.3.0-rc-2025-09-16) + wait-until: async func( + when: instant, + ); + + /// Wait for the specified duration to elapse. + @since(version = 0.3.0-rc-2025-09-16) + wait-for: async func( + how-long: duration, + ); +} diff --git a/wit/deps/clocks-0.3.0-rc-2025-09-16/timezone.wit b/wit/deps/clocks-0.3.0-rc-2025-09-16/timezone.wit new file mode 100644 index 0000000..ab8f5c0 --- /dev/null +++ b/wit/deps/clocks-0.3.0-rc-2025-09-16/timezone.wit @@ -0,0 +1,55 @@ +package wasi:clocks@0.3.0-rc-2025-09-16; + +@unstable(feature = clocks-timezone) +interface timezone { + @unstable(feature = clocks-timezone) + use wall-clock.{datetime}; + + /// Return information needed to display the given `datetime`. This includes + /// the UTC offset, the time zone name, and a flag indicating whether + /// daylight saving time is active. + /// + /// If the timezone cannot be determined for the given `datetime`, return a + /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight + /// saving time. + @unstable(feature = clocks-timezone) + display: func(when: datetime) -> timezone-display; + + /// The same as `display`, but only return the UTC offset. + @unstable(feature = clocks-timezone) + utc-offset: func(when: datetime) -> s32; + + /// Information useful for displaying the timezone of a specific `datetime`. + /// + /// This information may vary within a single `timezone` to reflect daylight + /// saving time adjustments. + @unstable(feature = clocks-timezone) + record timezone-display { + /// The number of seconds difference between UTC time and the local + /// time of the timezone. + /// + /// The returned value will always be less than 86400 which is the + /// number of seconds in a day (24*60*60). + /// + /// In implementations that do not expose an actual time zone, this + /// should return 0. + utc-offset: s32, + + /// The abbreviated name of the timezone to display to a user. The name + /// `UTC` indicates Coordinated Universal Time. Otherwise, this should + /// reference local standards for the name of the time zone. + /// + /// In implementations that do not expose an actual time zone, this + /// should be the string `UTC`. + /// + /// In time zones that do not have an applicable name, a formatted + /// representation of the UTC offset may be returned, such as `-04:00`. + name: string, + + /// Whether daylight saving time is active. + /// + /// In implementations that do not expose an actual time zone, this + /// should return false. + in-daylight-saving-time: bool, + } +} diff --git a/wit/deps/clocks-0.3.0-rc-2025-09-16/types.wit b/wit/deps/clocks-0.3.0-rc-2025-09-16/types.wit new file mode 100644 index 0000000..aff7c2a --- /dev/null +++ b/wit/deps/clocks-0.3.0-rc-2025-09-16/types.wit @@ -0,0 +1,8 @@ +package wasi:clocks@0.3.0-rc-2025-09-16; +/// This interface common types used throughout wasi:clocks. +@since(version = 0.3.0-rc-2025-09-16) +interface types { + /// A duration of time, in nanoseconds. + @since(version = 0.3.0-rc-2025-09-16) + type duration = u64; +} diff --git a/wit/deps/clocks-0.3.0-rc-2025-09-16/wall-clock.wit b/wit/deps/clocks-0.3.0-rc-2025-09-16/wall-clock.wit new file mode 100644 index 0000000..ea94050 --- /dev/null +++ b/wit/deps/clocks-0.3.0-rc-2025-09-16/wall-clock.wit @@ -0,0 +1,46 @@ +package wasi:clocks@0.3.0-rc-2025-09-16; +/// WASI Wall Clock is a clock API intended to let users query the current +/// time. The name "wall" makes an analogy to a "clock on the wall", which +/// is not necessarily monotonic as it may be reset. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A wall clock is a clock which measures the date and time according to +/// some external reference. +/// +/// External references may be reset, so this clock is not necessarily +/// monotonic, making it unsuitable for measuring elapsed time. +/// +/// It is intended for reporting the current date and time for humans. +@since(version = 0.3.0-rc-2025-09-16) +interface wall-clock { + /// A time and date in seconds plus nanoseconds. + @since(version = 0.3.0-rc-2025-09-16) + record datetime { + seconds: u64, + nanoseconds: u32, + } + + /// Read the current value of the clock. + /// + /// This clock is not monotonic, therefore calling this function repeatedly + /// will not necessarily produce a sequence of non-decreasing values. + /// + /// The returned timestamps represent the number of seconds since + /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], + /// also known as [Unix Time]. + /// + /// The nanoseconds field of the output is always less than 1000000000. + /// + /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 + /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time + @since(version = 0.3.0-rc-2025-09-16) + now: func() -> datetime; + + /// Query the resolution of the clock. + /// + /// The nanoseconds field of the output is always less than 1000000000. + @since(version = 0.3.0-rc-2025-09-16) + get-resolution: func() -> datetime; +} diff --git a/wit/deps/clocks-0.3.0-rc-2025-09-16/world.wit b/wit/deps/clocks-0.3.0-rc-2025-09-16/world.wit new file mode 100644 index 0000000..a6b885f --- /dev/null +++ b/wit/deps/clocks-0.3.0-rc-2025-09-16/world.wit @@ -0,0 +1,11 @@ +package wasi:clocks@0.3.0-rc-2025-09-16; + +@since(version = 0.3.0-rc-2025-09-16) +world imports { + @since(version = 0.3.0-rc-2025-09-16) + import monotonic-clock; + @since(version = 0.3.0-rc-2025-09-16) + import wall-clock; + @unstable(feature = clocks-timezone) + import timezone; +} diff --git a/wit/deps/filesystem-0.3.0-rc-2025-09-16/preopens.wit b/wit/deps/filesystem-0.3.0-rc-2025-09-16/preopens.wit new file mode 100644 index 0000000..9036e90 --- /dev/null +++ b/wit/deps/filesystem-0.3.0-rc-2025-09-16/preopens.wit @@ -0,0 +1,11 @@ +package wasi:filesystem@0.3.0-rc-2025-09-16; + +@since(version = 0.3.0-rc-2025-09-16) +interface preopens { + @since(version = 0.3.0-rc-2025-09-16) + use types.{descriptor}; + + /// Return the set of preopened directories, and their paths. + @since(version = 0.3.0-rc-2025-09-16) + get-directories: func() -> list>; +} diff --git a/wit/deps/filesystem-0.3.0-rc-2025-09-16/types.wit b/wit/deps/filesystem-0.3.0-rc-2025-09-16/types.wit new file mode 100644 index 0000000..41d91be --- /dev/null +++ b/wit/deps/filesystem-0.3.0-rc-2025-09-16/types.wit @@ -0,0 +1,636 @@ +package wasi:filesystem@0.3.0-rc-2025-09-16; +/// WASI filesystem is a filesystem API primarily intended to let users run WASI +/// programs that access their files on their existing filesystems, without +/// significant overhead. +/// +/// It is intended to be roughly portable between Unix-family platforms and +/// Windows, though it does not hide many of the major differences. +/// +/// Paths are passed as interface-type `string`s, meaning they must consist of +/// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain +/// paths which are not accessible by this API. +/// +/// The directory separator in WASI is always the forward-slash (`/`). +/// +/// All paths in WASI are relative paths, and are interpreted relative to a +/// `descriptor` referring to a base directory. If a `path` argument to any WASI +/// function starts with `/`, or if any step of resolving a `path`, including +/// `..` and symbolic link steps, reaches a directory outside of the base +/// directory, or reaches a symlink to an absolute or rooted path in the +/// underlying filesystem, the function fails with `error-code::not-permitted`. +/// +/// For more information about WASI path resolution and sandboxing, see +/// [WASI filesystem path resolution]. +/// +/// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md +@since(version = 0.3.0-rc-2025-09-16) +interface types { + @since(version = 0.3.0-rc-2025-09-16) + use wasi:clocks/wall-clock@0.3.0-rc-2025-09-16.{datetime}; + + /// File size or length of a region within a file. + @since(version = 0.3.0-rc-2025-09-16) + type filesize = u64; + + /// The type of a filesystem object referenced by a descriptor. + /// + /// Note: This was called `filetype` in earlier versions of WASI. + @since(version = 0.3.0-rc-2025-09-16) + enum descriptor-type { + /// The type of the descriptor or file is unknown or is different from + /// any of the other types specified. + unknown, + /// The descriptor refers to a block device inode. + block-device, + /// The descriptor refers to a character device inode. + character-device, + /// The descriptor refers to a directory inode. + directory, + /// The descriptor refers to a named pipe. + fifo, + /// The file refers to a symbolic link inode. + symbolic-link, + /// The descriptor refers to a regular file inode. + regular-file, + /// The descriptor refers to a socket. + socket, + } + + /// Descriptor flags. + /// + /// Note: This was called `fdflags` in earlier versions of WASI. + @since(version = 0.3.0-rc-2025-09-16) + flags descriptor-flags { + /// Read mode: Data can be read. + read, + /// Write mode: Data can be written to. + write, + /// Request that writes be performed according to synchronized I/O file + /// integrity completion. The data stored in the file and the file's + /// metadata are synchronized. This is similar to `O_SYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + file-integrity-sync, + /// Request that writes be performed according to synchronized I/O data + /// integrity completion. Only the data stored in the file is + /// synchronized. This is similar to `O_DSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + data-integrity-sync, + /// Requests that reads be performed at the same level of integrity + /// requested for writes. This is similar to `O_RSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + requested-write-sync, + /// Mutating directories mode: Directory contents may be mutated. + /// + /// When this flag is unset on a descriptor, operations using the + /// descriptor which would create, rename, delete, modify the data or + /// metadata of filesystem objects, or obtain another handle which + /// would permit any of those, shall fail with `error-code::read-only` if + /// they would otherwise succeed. + /// + /// This may only be set on directories. + mutate-directory, + } + + /// File attributes. + /// + /// Note: This was called `filestat` in earlier versions of WASI. + @since(version = 0.3.0-rc-2025-09-16) + record descriptor-stat { + /// File type. + %type: descriptor-type, + /// Number of hard links to the file. + link-count: link-count, + /// For regular files, the file size in bytes. For symbolic links, the + /// length in bytes of the pathname contained in the symbolic link. + size: filesize, + /// Last data access timestamp. + /// + /// If the `option` is none, the platform doesn't maintain an access + /// timestamp for this file. + data-access-timestamp: option, + /// Last data modification timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// modification timestamp for this file. + data-modification-timestamp: option, + /// Last file status-change timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// status-change timestamp for this file. + status-change-timestamp: option, + } + + /// Flags determining the method of how paths are resolved. + @since(version = 0.3.0-rc-2025-09-16) + flags path-flags { + /// As long as the resolved path corresponds to a symbolic link, it is + /// expanded. + symlink-follow, + } + + /// Open flags used by `open-at`. + @since(version = 0.3.0-rc-2025-09-16) + flags open-flags { + /// Create file if it does not exist, similar to `O_CREAT` in POSIX. + create, + /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX. + directory, + /// Fail if file already exists, similar to `O_EXCL` in POSIX. + exclusive, + /// Truncate file to size 0, similar to `O_TRUNC` in POSIX. + truncate, + } + + /// Number of hard links to an inode. + @since(version = 0.3.0-rc-2025-09-16) + type link-count = u64; + + /// When setting a timestamp, this gives the value to set it to. + @since(version = 0.3.0-rc-2025-09-16) + variant new-timestamp { + /// Leave the timestamp set to its previous value. + no-change, + /// Set the timestamp to the current time of the system clock associated + /// with the filesystem. + now, + /// Set the timestamp to the given value. + timestamp(datetime), + } + + /// A directory entry. + record directory-entry { + /// The type of the file referred to by this directory entry. + %type: descriptor-type, + + /// The name of the object. + name: string, + } + + /// Error codes returned by functions, similar to `errno` in POSIX. + /// Not all of these error codes are returned by the functions provided by this + /// API; some are used in higher-level library layers, and others are provided + /// merely for alignment with POSIX. + enum error-code { + /// Permission denied, similar to `EACCES` in POSIX. + access, + /// Connection already in progress, similar to `EALREADY` in POSIX. + already, + /// Bad descriptor, similar to `EBADF` in POSIX. + bad-descriptor, + /// Device or resource busy, similar to `EBUSY` in POSIX. + busy, + /// Resource deadlock would occur, similar to `EDEADLK` in POSIX. + deadlock, + /// Storage quota exceeded, similar to `EDQUOT` in POSIX. + quota, + /// File exists, similar to `EEXIST` in POSIX. + exist, + /// File too large, similar to `EFBIG` in POSIX. + file-too-large, + /// Illegal byte sequence, similar to `EILSEQ` in POSIX. + illegal-byte-sequence, + /// Operation in progress, similar to `EINPROGRESS` in POSIX. + in-progress, + /// Interrupted function, similar to `EINTR` in POSIX. + interrupted, + /// Invalid argument, similar to `EINVAL` in POSIX. + invalid, + /// I/O error, similar to `EIO` in POSIX. + io, + /// Is a directory, similar to `EISDIR` in POSIX. + is-directory, + /// Too many levels of symbolic links, similar to `ELOOP` in POSIX. + loop, + /// Too many links, similar to `EMLINK` in POSIX. + too-many-links, + /// Message too large, similar to `EMSGSIZE` in POSIX. + message-size, + /// Filename too long, similar to `ENAMETOOLONG` in POSIX. + name-too-long, + /// No such device, similar to `ENODEV` in POSIX. + no-device, + /// No such file or directory, similar to `ENOENT` in POSIX. + no-entry, + /// No locks available, similar to `ENOLCK` in POSIX. + no-lock, + /// Not enough space, similar to `ENOMEM` in POSIX. + insufficient-memory, + /// No space left on device, similar to `ENOSPC` in POSIX. + insufficient-space, + /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX. + not-directory, + /// Directory not empty, similar to `ENOTEMPTY` in POSIX. + not-empty, + /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX. + not-recoverable, + /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX. + unsupported, + /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX. + no-tty, + /// No such device or address, similar to `ENXIO` in POSIX. + no-such-device, + /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX. + overflow, + /// Operation not permitted, similar to `EPERM` in POSIX. + not-permitted, + /// Broken pipe, similar to `EPIPE` in POSIX. + pipe, + /// Read-only file system, similar to `EROFS` in POSIX. + read-only, + /// Invalid seek, similar to `ESPIPE` in POSIX. + invalid-seek, + /// Text file busy, similar to `ETXTBSY` in POSIX. + text-file-busy, + /// Cross-device link, similar to `EXDEV` in POSIX. + cross-device, + } + + /// File or memory access pattern advisory information. + @since(version = 0.3.0-rc-2025-09-16) + enum advice { + /// The application has no advice to give on its behavior with respect + /// to the specified data. + normal, + /// The application expects to access the specified data sequentially + /// from lower offsets to higher offsets. + sequential, + /// The application expects to access the specified data in a random + /// order. + random, + /// The application expects to access the specified data in the near + /// future. + will-need, + /// The application expects that it will not access the specified data + /// in the near future. + dont-need, + /// The application expects to access the specified data once and then + /// not reuse it thereafter. + no-reuse, + } + + /// A 128-bit hash value, split into parts because wasm doesn't have a + /// 128-bit integer type. + @since(version = 0.3.0-rc-2025-09-16) + record metadata-hash-value { + /// 64 bits of a 128-bit hash value. + lower: u64, + /// Another 64 bits of a 128-bit hash value. + upper: u64, + } + + /// A descriptor is a reference to a filesystem object, which may be a file, + /// directory, named pipe, special file, or other object on which filesystem + /// calls may be made. + @since(version = 0.3.0-rc-2025-09-16) + resource descriptor { + /// Return a stream for reading from a file. + /// + /// Multiple read, write, and append streams may be active on the same open + /// file and they do not interfere with each other. + /// + /// This function returns a `stream` which provides the data received from the + /// file, and a `future` providing additional error information in case an + /// error is encountered. + /// + /// If no error is encountered, `stream.read` on the `stream` will return + /// `read-status::closed` with no `error-context` and the future resolves to + /// the value `ok`. If an error is encountered, `stream.read` on the + /// `stream` returns `read-status::closed` with an `error-context` and the future + /// resolves to `err` with an `error-code`. + /// + /// Note: This is similar to `pread` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + read-via-stream: func( + /// The offset within the file at which to start reading. + offset: filesize, + ) -> tuple, future>>; + + /// Return a stream for writing to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be written. + /// + /// It is valid to write past the end of a file; the file is extended to the + /// extent of the write, with bytes between the previous end and the start of + /// the write set to zero. + /// + /// This function returns once either full contents of the stream are + /// written or an error is encountered. + /// + /// Note: This is similar to `pwrite` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + write-via-stream: async func( + /// Data to write + data: stream, + /// The offset within the file at which to start writing. + offset: filesize, + ) -> result<_, error-code>; + + /// Return a stream for appending to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be appended. + /// + /// This function returns once either full contents of the stream are + /// written or an error is encountered. + /// + /// Note: This is similar to `write` with `O_APPEND` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + append-via-stream: async func(data: stream) -> result<_, error-code>; + + /// Provide file advisory information on a descriptor. + /// + /// This is similar to `posix_fadvise` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + advise: async func( + /// The offset within the file to which the advisory applies. + offset: filesize, + /// The length of the region to which the advisory applies. + length: filesize, + /// The advice. + advice: advice + ) -> result<_, error-code>; + + /// Synchronize the data of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fdatasync` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + sync-data: async func() -> result<_, error-code>; + + /// Get flags associated with a descriptor. + /// + /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. + /// + /// Note: This returns the value that was the `fs_flags` value returned + /// from `fdstat_get` in earlier versions of WASI. + @since(version = 0.3.0-rc-2025-09-16) + get-flags: async func() -> result; + + /// Get the dynamic type of a descriptor. + /// + /// Note: This returns the same value as the `type` field of the `fd-stat` + /// returned by `stat`, `stat-at` and similar. + /// + /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided + /// by `fstat` in POSIX. + /// + /// Note: This returns the value that was the `fs_filetype` value returned + /// from `fdstat_get` in earlier versions of WASI. + @since(version = 0.3.0-rc-2025-09-16) + get-type: async func() -> result; + + /// Adjust the size of an open file. If this increases the file's size, the + /// extra bytes are filled with zeros. + /// + /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. + @since(version = 0.3.0-rc-2025-09-16) + set-size: async func(size: filesize) -> result<_, error-code>; + + /// Adjust the timestamps of an open file or directory. + /// + /// Note: This is similar to `futimens` in POSIX. + /// + /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. + @since(version = 0.3.0-rc-2025-09-16) + set-times: async func( + /// The desired values of the data access timestamp. + data-access-timestamp: new-timestamp, + /// The desired values of the data modification timestamp. + data-modification-timestamp: new-timestamp, + ) -> result<_, error-code>; + + /// Read directory entries from a directory. + /// + /// On filesystems where directories contain entries referring to themselves + /// and their parents, often named `.` and `..` respectively, these entries + /// are omitted. + /// + /// This always returns a new stream which starts at the beginning of the + /// directory. Multiple streams may be active on the same directory, and they + /// do not interfere with each other. + /// + /// This function returns a future, which will resolve to an error code if + /// reading full contents of the directory fails. + @since(version = 0.3.0-rc-2025-09-16) + read-directory: async func() -> tuple, future>>; + + /// Synchronize the data and metadata of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fsync` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + sync: async func() -> result<_, error-code>; + + /// Create a directory. + /// + /// Note: This is similar to `mkdirat` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + create-directory-at: async func( + /// The relative path at which to create the directory. + path: string, + ) -> result<_, error-code>; + + /// Return the attributes of an open file or directory. + /// + /// Note: This is similar to `fstat` in POSIX, except that it does not return + /// device and inode information. For testing whether two descriptors refer to + /// the same underlying filesystem object, use `is-same-object`. To obtain + /// additional data that can be used do determine whether a file has been + /// modified, use `metadata-hash`. + /// + /// Note: This was called `fd_filestat_get` in earlier versions of WASI. + @since(version = 0.3.0-rc-2025-09-16) + stat: async func() -> result; + + /// Return the attributes of a file or directory. + /// + /// Note: This is similar to `fstatat` in POSIX, except that it does not + /// return device and inode information. See the `stat` description for a + /// discussion of alternatives. + /// + /// Note: This was called `path_filestat_get` in earlier versions of WASI. + @since(version = 0.3.0-rc-2025-09-16) + stat-at: async func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to inspect. + path: string, + ) -> result; + + /// Adjust the timestamps of a file or directory. + /// + /// Note: This is similar to `utimensat` in POSIX. + /// + /// Note: This was called `path_filestat_set_times` in earlier versions of + /// WASI. + @since(version = 0.3.0-rc-2025-09-16) + set-times-at: async func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to operate on. + path: string, + /// The desired values of the data access timestamp. + data-access-timestamp: new-timestamp, + /// The desired values of the data modification timestamp. + data-modification-timestamp: new-timestamp, + ) -> result<_, error-code>; + + /// Create a hard link. + /// + /// Fails with `error-code::no-entry` if the old path does not exist, + /// with `error-code::exist` if the new path already exists, and + /// `error-code::not-permitted` if the old path is not a file. + /// + /// Note: This is similar to `linkat` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + link-at: async func( + /// Flags determining the method of how the path is resolved. + old-path-flags: path-flags, + /// The relative source path from which to link. + old-path: string, + /// The base directory for `new-path`. + new-descriptor: borrow, + /// The relative destination path at which to create the hard link. + new-path: string, + ) -> result<_, error-code>; + + /// Open a file or directory. + /// + /// If `flags` contains `descriptor-flags::mutate-directory`, and the base + /// descriptor doesn't have `descriptor-flags::mutate-directory` set, + /// `open-at` fails with `error-code::read-only`. + /// + /// If `flags` contains `write` or `mutate-directory`, or `open-flags` + /// contains `truncate` or `create`, and the base descriptor doesn't have + /// `descriptor-flags::mutate-directory` set, `open-at` fails with + /// `error-code::read-only`. + /// + /// Note: This is similar to `openat` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + open-at: async func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the object to open. + path: string, + /// The method by which to open the file. + open-flags: open-flags, + /// Flags to use for the resulting descriptor. + %flags: descriptor-flags, + ) -> result; + + /// Read the contents of a symbolic link. + /// + /// If the contents contain an absolute or rooted path in the underlying + /// filesystem, this function fails with `error-code::not-permitted`. + /// + /// Note: This is similar to `readlinkat` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + readlink-at: async func( + /// The relative path of the symbolic link from which to read. + path: string, + ) -> result; + + /// Remove a directory. + /// + /// Return `error-code::not-empty` if the directory is not empty. + /// + /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + remove-directory-at: async func( + /// The relative path to a directory to remove. + path: string, + ) -> result<_, error-code>; + + /// Rename a filesystem object. + /// + /// Note: This is similar to `renameat` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + rename-at: async func( + /// The relative source path of the file or directory to rename. + old-path: string, + /// The base directory for `new-path`. + new-descriptor: borrow, + /// The relative destination path to which to rename the file or directory. + new-path: string, + ) -> result<_, error-code>; + + /// Create a symbolic link (also known as a "symlink"). + /// + /// If `old-path` starts with `/`, the function fails with + /// `error-code::not-permitted`. + /// + /// Note: This is similar to `symlinkat` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + symlink-at: async func( + /// The contents of the symbolic link. + old-path: string, + /// The relative destination path at which to create the symbolic link. + new-path: string, + ) -> result<_, error-code>; + + /// Unlink a filesystem object that is not a directory. + /// + /// Return `error-code::is-directory` if the path refers to a directory. + /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + @since(version = 0.3.0-rc-2025-09-16) + unlink-file-at: async func( + /// The relative path to a file to unlink. + path: string, + ) -> result<_, error-code>; + + /// Test whether two descriptors refer to the same filesystem object. + /// + /// In POSIX, this corresponds to testing whether the two descriptors have the + /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. + /// wasi-filesystem does not expose device and inode numbers, so this function + /// may be used instead. + @since(version = 0.3.0-rc-2025-09-16) + is-same-object: async func(other: borrow) -> bool; + + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a descriptor. + /// + /// This returns a hash of the last-modification timestamp and file size, and + /// may also include the inode number, device number, birth timestamp, and + /// other metadata fields that may change when the file is modified or + /// replaced. It may also include a secret value chosen by the + /// implementation and not otherwise exposed. + /// + /// Implementations are encouraged to provide the following properties: + /// + /// - If the file is not modified or replaced, the computed hash value should + /// usually not change. + /// - If the object is modified or replaced, the computed hash value should + /// usually change. + /// - The inputs to the hash should not be easily computable from the + /// computed hash. + /// + /// However, none of these is required. + @since(version = 0.3.0-rc-2025-09-16) + metadata-hash: async func() -> result; + + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a directory descriptor and a relative path. + /// + /// This performs the same hash computation as `metadata-hash`. + @since(version = 0.3.0-rc-2025-09-16) + metadata-hash-at: async func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to inspect. + path: string, + ) -> result; + } +} diff --git a/wit/deps/filesystem-0.3.0-rc-2025-09-16/world.wit b/wit/deps/filesystem-0.3.0-rc-2025-09-16/world.wit new file mode 100644 index 0000000..87fc727 --- /dev/null +++ b/wit/deps/filesystem-0.3.0-rc-2025-09-16/world.wit @@ -0,0 +1,9 @@ +package wasi:filesystem@0.3.0-rc-2025-09-16; + +@since(version = 0.3.0-rc-2025-09-16) +world imports { + @since(version = 0.3.0-rc-2025-09-16) + import types; + @since(version = 0.3.0-rc-2025-09-16) + import preopens; +} diff --git a/wit/deps/http-0.3.0-rc-2025-09-16/handler.wit b/wit/deps/http-0.3.0-rc-2025-09-16/handler.wit new file mode 100644 index 0000000..e4446cb --- /dev/null +++ b/wit/deps/http-0.3.0-rc-2025-09-16/handler.wit @@ -0,0 +1,17 @@ +/// This interface defines a handler of HTTP Requests. It may be imported by +/// components which wish to send HTTP Requests and also exported by components +/// which can respond to HTTP Requests. In addition, it may be used to pass +/// a request from one component to another without any use of a network. +interface handler { + use types.{request, response, error-code}; + + /// When exported, this function may be called with either an incoming + /// request read from the network or a request synthesized or forwarded by + /// another component. + /// + /// When imported, this function may be used to either send an outgoing + /// request over the network or pass it to another component. + handle: async func( + request: request, + ) -> result; +} diff --git a/wit/deps/http-0.3.0-rc-2025-09-16/proxy.wit b/wit/deps/http-0.3.0-rc-2025-09-16/proxy.wit new file mode 100644 index 0000000..223083e --- /dev/null +++ b/wit/deps/http-0.3.0-rc-2025-09-16/proxy.wit @@ -0,0 +1,44 @@ +package wasi:http@0.3.0-rc-2025-09-16; + +/// The `wasi:http/imports` world imports all the APIs for HTTP proxies. +/// It is intended to be `include`d in other worlds. +world imports { + /// HTTP proxies have access to time and randomness. + include wasi:clocks/imports@0.3.0-rc-2025-09-16; + import wasi:random/random@0.3.0-rc-2025-09-16; + + /// Proxies have standard output and error streams which are expected to + /// terminate in a developer-facing console provided by the host. + import wasi:cli/stdout@0.3.0-rc-2025-09-16; + import wasi:cli/stderr@0.3.0-rc-2025-09-16; + + /// TODO: this is a temporary workaround until component tooling is able to + /// gracefully handle the absence of stdin. Hosts must return an eof stream + /// for this import, which is what wasi-libc + tooling will do automatically + /// when this import is properly removed. + import wasi:cli/stdin@0.3.0-rc-2025-09-16; + + /// This is the default handler to use when user code simply wants to make an + /// HTTP request (e.g., via `fetch()`). + /// + /// This may also be used to pass synthesized or forwarded requests to another + /// component. + import handler; +} + +/// The `wasi:http/proxy` world captures a widely-implementable intersection of +/// hosts that includes HTTP forward and reverse proxies. Components targeting +/// this world may concurrently stream in and out any number of incoming and +/// outgoing HTTP requests. +world proxy { + include imports; + + /// The host delivers incoming HTTP requests to a component by calling the + /// `handle` function of this exported interface. A host may arbitrarily reuse + /// or not reuse component instance when delivering incoming HTTP requests and + /// thus a component must be able to handle 0..N calls to `handle`. + /// + /// This may also be used to receive synthesized or forwarded requests from + /// another component. + export handler; +} diff --git a/wit/deps/http-0.3.0-rc-2025-09-16/types.wit b/wit/deps/http-0.3.0-rc-2025-09-16/types.wit new file mode 100644 index 0000000..8269eea --- /dev/null +++ b/wit/deps/http-0.3.0-rc-2025-09-16/types.wit @@ -0,0 +1,419 @@ +/// This interface defines all of the types and methods for implementing HTTP +/// Requests and Responses, as well as their headers, trailers, and bodies. +interface types { + use wasi:clocks/monotonic-clock@0.3.0-rc-2025-09-16.{duration}; + + /// This type corresponds to HTTP standard Methods. + variant method { + get, + head, + post, + put, + delete, + connect, + options, + trace, + patch, + other(string) + } + + /// This type corresponds to HTTP standard Related Schemes. + variant scheme { + HTTP, + HTTPS, + other(string) + } + + /// These cases are inspired by the IANA HTTP Proxy Error Types: + /// + variant error-code { + DNS-timeout, + DNS-error(DNS-error-payload), + destination-not-found, + destination-unavailable, + destination-IP-prohibited, + destination-IP-unroutable, + connection-refused, + connection-terminated, + connection-timeout, + connection-read-timeout, + connection-write-timeout, + connection-limit-reached, + TLS-protocol-error, + TLS-certificate-error, + TLS-alert-received(TLS-alert-received-payload), + HTTP-request-denied, + HTTP-request-length-required, + HTTP-request-body-size(option), + HTTP-request-method-invalid, + HTTP-request-URI-invalid, + HTTP-request-URI-too-long, + HTTP-request-header-section-size(option), + HTTP-request-header-size(option), + HTTP-request-trailer-section-size(option), + HTTP-request-trailer-size(field-size-payload), + HTTP-response-incomplete, + HTTP-response-header-section-size(option), + HTTP-response-header-size(field-size-payload), + HTTP-response-body-size(option), + HTTP-response-trailer-section-size(option), + HTTP-response-trailer-size(field-size-payload), + HTTP-response-transfer-coding(option), + HTTP-response-content-coding(option), + HTTP-response-timeout, + HTTP-upgrade-failed, + HTTP-protocol-error, + loop-detected, + configuration-error, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. It also includes an optional string for an + /// unstructured description of the error. Users should not depend on the + /// string for diagnosing errors, as it's not required to be consistent + /// between implementations. + internal-error(option) + } + + /// Defines the case payload type for `DNS-error` above: + record DNS-error-payload { + rcode: option, + info-code: option + } + + /// Defines the case payload type for `TLS-alert-received` above: + record TLS-alert-received-payload { + alert-id: option, + alert-message: option + } + + /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: + record field-size-payload { + field-name: option, + field-size: option + } + + /// This type enumerates the different kinds of errors that may occur when + /// setting or appending to a `fields` resource. + variant header-error { + /// This error indicates that a `field-name` or `field-value` was + /// syntactically invalid when used with an operation that sets headers in a + /// `fields`. + invalid-syntax, + + /// This error indicates that a forbidden `field-name` was used when trying + /// to set a header in a `fields`. + forbidden, + + /// This error indicates that the operation on the `fields` was not + /// permitted because the fields are immutable. + immutable, + } + + /// This type enumerates the different kinds of errors that may occur when + /// setting fields of a `request-options` resource. + variant request-options-error { + /// Indicates the specified field is not supported by this implementation. + not-supported, + + /// Indicates that the operation on the `request-options` was not permitted + /// because it is immutable. + immutable, + } + + /// Field names are always strings. + /// + /// Field names should always be treated as case insensitive by the `fields` + /// resource for the purposes of equality checking. + type field-name = string; + + /// Field values should always be ASCII strings. However, in + /// reality, HTTP implementations often have to interpret malformed values, + /// so they are provided as a list of bytes. + type field-value = list; + + /// This following block defines the `fields` resource which corresponds to + /// HTTP standard Fields. Fields are a common representation used for both + /// Headers and Trailers. + /// + /// A `fields` may be mutable or immutable. A `fields` created using the + /// constructor, `from-list`, or `clone` will be mutable, but a `fields` + /// resource given by other means (including, but not limited to, + /// `request.headers`) might be be immutable. In an immutable fields, the + /// `set`, `append`, and `delete` operations will fail with + /// `header-error.immutable`. + /// + /// A `fields` resource should store `field-name`s and `field-value`s in their + /// original casing used to construct or mutate the `fields` resource. The `fields` + /// resource should use that original casing when serializing the fields for + /// transport or when returning them from a method. + resource fields { + + /// Construct an empty HTTP Fields. + /// + /// The resulting `fields` is mutable. + constructor(); + + /// Construct an HTTP Fields. + /// + /// The resulting `fields` is mutable. + /// + /// The list represents each name-value pair in the Fields. Names + /// which have multiple values are represented by multiple entries in this + /// list with the same name. + /// + /// The tuple is a pair of the field name, represented as a string, and + /// Value, represented as a list of bytes. In a valid Fields, all names + /// and values are valid UTF-8 strings. However, values are not always + /// well-formed, so they are represented as a raw list of bytes. + /// + /// An error result will be returned if any header or value was + /// syntactically invalid, or if a header was forbidden. + from-list: static func( + entries: list> + ) -> result; + + /// Get all of the values corresponding to a name. If the name is not present + /// in this `fields`, an empty list is returned. However, if the name is + /// present but empty, this is represented by a list with one or more + /// empty field-values present. + get: func(name: field-name) -> list; + + /// Returns `true` when the name is present in this `fields`. If the name is + /// syntactically invalid, `false` is returned. + has: func(name: field-name) -> bool; + + /// Set all of the values for a name. Clears any existing values for that + /// name, if they have been set. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + set: func(name: field-name, value: list) -> result<_, header-error>; + + /// Delete all values for a name. Does nothing if no values for the name + /// exist. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + delete: func(name: field-name) -> result<_, header-error>; + + /// Delete all values for a name. Does nothing if no values for the name + /// exist. + /// + /// Returns all values previously corresponding to the name, if any. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + get-and-delete: func(name: field-name) -> result, header-error>; + + /// Append a value for a name. Does not change or delete any existing + /// values for that name. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + append: func(name: field-name, value: field-value) -> result<_, header-error>; + + /// Retrieve the full set of names and values in the Fields. Like the + /// constructor, the list represents each name-value pair. + /// + /// The outer list represents each name-value pair in the Fields. Names + /// which have multiple values are represented by multiple entries in this + /// list with the same name. + /// + /// The names and values are always returned in the original casing and in + /// the order in which they will be serialized for transport. + copy-all: func() -> list>; + + /// Make a deep copy of the Fields. Equivalent in behavior to calling the + /// `fields` constructor on the return value of `copy-all`. The resulting + /// `fields` is mutable. + clone: func() -> fields; + } + + /// Headers is an alias for Fields. + type headers = fields; + + /// Trailers is an alias for Fields. + type trailers = fields; + + /// Represents an HTTP Request. + resource request { + + /// Construct a new `request` with a default `method` of `GET`, and + /// `none` values for `path-with-query`, `scheme`, and `authority`. + /// + /// `headers` is the HTTP Headers for the Request. + /// + /// `contents` is the optional body content stream with `none` + /// representing a zero-length content stream. + /// Once it is closed, `trailers` future must resolve to a result. + /// If `trailers` resolves to an error, underlying connection + /// will be closed immediately. + /// + /// `options` is optional `request-options` resource to be used + /// if the request is sent over a network connection. + /// + /// It is possible to construct, or manipulate with the accessor functions + /// below, a `request` with an invalid combination of `scheme` + /// and `authority`, or `headers` which are not permitted to be sent. + /// It is the obligation of the `handler.handle` implementation + /// to reject invalid constructions of `request`. + /// + /// The returned future resolves to result of transmission of this request. + new: static func( + headers: headers, + contents: option>, + trailers: future, error-code>>, + options: option + ) -> tuple>>; + + /// Get the Method for the Request. + get-method: func() -> method; + /// Set the Method for the Request. Fails if the string present in a + /// `method.other` argument is not a syntactically valid method. + set-method: func(method: method) -> result; + + /// Get the combination of the HTTP Path and Query for the Request. When + /// `none`, this represents an empty Path and empty Query. + get-path-with-query: func() -> option; + /// Set the combination of the HTTP Path and Query for the Request. When + /// `none`, this represents an empty Path and empty Query. Fails is the + /// string given is not a syntactically valid path and query uri component. + set-path-with-query: func(path-with-query: option) -> result; + + /// Get the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. + get-scheme: func() -> option; + /// Set the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. Fails if the + /// string given is not a syntactically valid uri scheme. + set-scheme: func(scheme: option) -> result; + + /// Get the authority of the Request's target URI. A value of `none` may be used + /// with Related Schemes which do not require an authority. The HTTP and + /// HTTPS schemes always require an authority. + get-authority: func() -> option; + /// Set the authority of the Request's target URI. A value of `none` may be used + /// with Related Schemes which do not require an authority. The HTTP and + /// HTTPS schemes always require an authority. Fails if the string given is + /// not a syntactically valid URI authority. + set-authority: func(authority: option) -> result; + + /// Get the `request-options` to be associated with this request + /// + /// The returned `request-options` resource is immutable: `set-*` operations + /// will fail if invoked. + /// + /// This `request-options` resource is a child: it must be dropped before + /// the parent `request` is dropped, or its ownership is transferred to + /// another component by e.g. `handler.handle`. + get-options: func() -> option; + + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + get-headers: func() -> headers; + + /// Get body of the Request. + /// + /// Stream returned by this method represents the contents of the body. + /// Once the stream is reported as closed, callers should await the returned + /// future to determine whether the body was received successfully. + /// The future will only resolve after the stream is reported as closed. + /// + /// This function takes a `res` future as a parameter, which can be used to + /// communicate an error in handling of the request. + /// + /// Note that function will move the `request`, but references to headers or + /// request options acquired from it previously will remain valid. + consume-body: static func(this: request, res: future>) -> tuple, future, error-code>>>; + } + + /// Parameters for making an HTTP Request. Each of these parameters is + /// currently an optional timeout applicable to the transport layer of the + /// HTTP protocol. + /// + /// These timeouts are separate from any the user may use to bound an + /// asynchronous call. + resource request-options { + /// Construct a default `request-options` value. + constructor(); + + /// The timeout for the initial connect to the HTTP Server. + get-connect-timeout: func() -> option; + + /// Set the timeout for the initial connect to the HTTP Server. An error + /// return value indicates that this timeout is not supported or that this + /// handle is immutable. + set-connect-timeout: func(duration: option) -> result<_, request-options-error>; + + /// The timeout for receiving the first byte of the Response body. + get-first-byte-timeout: func() -> option; + + /// Set the timeout for receiving the first byte of the Response body. An + /// error return value indicates that this timeout is not supported or that + /// this handle is immutable. + set-first-byte-timeout: func(duration: option) -> result<_, request-options-error>; + + /// The timeout for receiving subsequent chunks of bytes in the Response + /// body stream. + get-between-bytes-timeout: func() -> option; + + /// Set the timeout for receiving subsequent chunks of bytes in the Response + /// body stream. An error return value indicates that this timeout is not + /// supported or that this handle is immutable. + set-between-bytes-timeout: func(duration: option) -> result<_, request-options-error>; + + /// Make a deep copy of the `request-options`. + /// The resulting `request-options` is mutable. + clone: func() -> request-options; + } + + /// This type corresponds to the HTTP standard Status Code. + type status-code = u16; + + /// Represents an HTTP Response. + resource response { + + /// Construct a new `response`, with a default `status-code` of `200`. + /// If a different `status-code` is needed, it must be set via the + /// `set-status-code` method. + /// + /// `headers` is the HTTP Headers for the Response. + /// + /// `contents` is the optional body content stream with `none` + /// representing a zero-length content stream. + /// Once it is closed, `trailers` future must resolve to a result. + /// If `trailers` resolves to an error, underlying connection + /// will be closed immediately. + /// + /// The returned future resolves to result of transmission of this response. + new: static func( + headers: headers, + contents: option>, + trailers: future, error-code>>, + ) -> tuple>>; + + /// Get the HTTP Status Code for the Response. + get-status-code: func() -> status-code; + + /// Set the HTTP Status Code for the Response. Fails if the status-code + /// given is not a valid http status code. + set-status-code: func(status-code: status-code) -> result; + + /// Get the headers associated with the Response. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + get-headers: func() -> headers; + + /// Get body of the Response. + /// + /// Stream returned by this method represents the contents of the body. + /// Once the stream is reported as closed, callers should await the returned + /// future to determine whether the body was received successfully. + /// The future will only resolve after the stream is reported as closed. + /// + /// This function takes a `res` future as a parameter, which can be used to + /// communicate an error in handling of the response. + /// + /// Note that function will move the `response`, but references to headers + /// acquired from it previously will remain valid. + consume-body: static func(this: response, res: future>) -> tuple, future, error-code>>>; + } +} diff --git a/wit/deps/random-0.3.0-rc-2025-09-16/insecure-seed.wit b/wit/deps/random-0.3.0-rc-2025-09-16/insecure-seed.wit new file mode 100644 index 0000000..302151b --- /dev/null +++ b/wit/deps/random-0.3.0-rc-2025-09-16/insecure-seed.wit @@ -0,0 +1,27 @@ +package wasi:random@0.3.0-rc-2025-09-16; +/// The insecure-seed interface for seeding hash-map DoS resistance. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.3.0-rc-2025-09-16) +interface insecure-seed { + /// Return a 128-bit value that may contain a pseudo-random value. + /// + /// The returned value is not required to be computed from a CSPRNG, and may + /// even be entirely deterministic. Host implementations are encouraged to + /// provide pseudo-random values to any program exposed to + /// attacker-controlled content, to enable DoS protection built into many + /// languages' hash-map implementations. + /// + /// This function is intended to only be called once, by a source language + /// to initialize Denial Of Service (DoS) protection in its hash-map + /// implementation. + /// + /// # Expected future evolution + /// + /// This will likely be changed to a value import, to prevent it from being + /// called multiple times and potentially used for purposes other than DoS + /// protection. + @since(version = 0.3.0-rc-2025-09-16) + get-insecure-seed: func() -> tuple; +} diff --git a/wit/deps/random-0.3.0-rc-2025-09-16/insecure.wit b/wit/deps/random-0.3.0-rc-2025-09-16/insecure.wit new file mode 100644 index 0000000..39146e3 --- /dev/null +++ b/wit/deps/random-0.3.0-rc-2025-09-16/insecure.wit @@ -0,0 +1,25 @@ +package wasi:random@0.3.0-rc-2025-09-16; +/// The insecure interface for insecure pseudo-random numbers. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.3.0-rc-2025-09-16) +interface insecure { + /// Return `len` insecure pseudo-random bytes. + /// + /// This function is not cryptographically secure. Do not use it for + /// anything related to security. + /// + /// There are no requirements on the values of the returned bytes, however + /// implementations are encouraged to return evenly distributed values with + /// a long period. + @since(version = 0.3.0-rc-2025-09-16) + get-insecure-random-bytes: func(len: u64) -> list; + + /// Return an insecure pseudo-random `u64` value. + /// + /// This function returns the same type of pseudo-random data as + /// `get-insecure-random-bytes`, represented as a `u64`. + @since(version = 0.3.0-rc-2025-09-16) + get-insecure-random-u64: func() -> u64; +} diff --git a/wit/deps/random-0.3.0-rc-2025-09-16/random.wit b/wit/deps/random-0.3.0-rc-2025-09-16/random.wit new file mode 100644 index 0000000..fa1f111 --- /dev/null +++ b/wit/deps/random-0.3.0-rc-2025-09-16/random.wit @@ -0,0 +1,29 @@ +package wasi:random@0.3.0-rc-2025-09-16; +/// WASI Random is a random data API. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.3.0-rc-2025-09-16) +interface random { + /// Return `len` cryptographically-secure random or pseudo-random bytes. + /// + /// This function must produce data at least as cryptographically secure and + /// fast as an adequately seeded cryptographically-secure pseudo-random + /// number generator (CSPRNG). It must not block, from the perspective of + /// the calling program, under any circumstances, including on the first + /// request and on requests for numbers of bytes. The returned data must + /// always be unpredictable. + /// + /// This function must always return fresh data. Deterministic environments + /// must omit this function, rather than implementing it with deterministic + /// data. + @since(version = 0.3.0-rc-2025-09-16) + get-random-bytes: func(len: u64) -> list; + + /// Return a cryptographically-secure random or pseudo-random `u64` value. + /// + /// This function returns the same type of data as `get-random-bytes`, + /// represented as a `u64`. + @since(version = 0.3.0-rc-2025-09-16) + get-random-u64: func() -> u64; +} diff --git a/wit/deps/random-0.3.0-rc-2025-09-16/world.wit b/wit/deps/random-0.3.0-rc-2025-09-16/world.wit new file mode 100644 index 0000000..08c5ed8 --- /dev/null +++ b/wit/deps/random-0.3.0-rc-2025-09-16/world.wit @@ -0,0 +1,13 @@ +package wasi:random@0.3.0-rc-2025-09-16; + +@since(version = 0.3.0-rc-2025-09-16) +world imports { + @since(version = 0.3.0-rc-2025-09-16) + import random; + + @since(version = 0.3.0-rc-2025-09-16) + import insecure; + + @since(version = 0.3.0-rc-2025-09-16) + import insecure-seed; +} diff --git a/wit/deps/sockets-0.3.0-rc-2025-09-16/ip-name-lookup.wit b/wit/deps/sockets-0.3.0-rc-2025-09-16/ip-name-lookup.wit new file mode 100644 index 0000000..6a652ff --- /dev/null +++ b/wit/deps/sockets-0.3.0-rc-2025-09-16/ip-name-lookup.wit @@ -0,0 +1,62 @@ +@since(version = 0.3.0-rc-2025-09-16) +interface ip-name-lookup { + @since(version = 0.3.0-rc-2025-09-16) + use types.{ip-address}; + + /// Lookup error codes. + @since(version = 0.3.0-rc-2025-09-16) + enum error-code { + /// Unknown error + unknown, + + /// Access denied. + /// + /// POSIX equivalent: EACCES, EPERM + access-denied, + + /// `name` is a syntactically invalid domain name or IP address. + /// + /// POSIX equivalent: EINVAL + invalid-argument, + + /// Name does not exist or has no suitable associated IP addresses. + /// + /// POSIX equivalent: EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY + name-unresolvable, + + /// A temporary failure in name resolution occurred. + /// + /// POSIX equivalent: EAI_AGAIN + temporary-resolver-failure, + + /// A permanent failure in name resolution occurred. + /// + /// POSIX equivalent: EAI_FAIL + permanent-resolver-failure, + } + + /// Resolve an internet host name to a list of IP addresses. + /// + /// Unicode domain names are automatically converted to ASCII using IDNA encoding. + /// If the input is an IP address string, the address is parsed and returned + /// as-is without making any external requests. + /// + /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. + /// + /// The results are returned in connection order preference. + /// + /// This function never succeeds with 0 results. It either fails or succeeds + /// with at least one address. Additionally, this function never returns + /// IPv4-mapped IPv6 addresses. + /// + /// The returned future will resolve to an error code in case of failure. + /// It will resolve to success once the returned stream is exhausted. + /// + /// # References: + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + resolve-addresses: async func(name: string) -> result, error-code>; +} diff --git a/wit/deps/sockets-0.3.0-rc-2025-09-16/types.wit b/wit/deps/sockets-0.3.0-rc-2025-09-16/types.wit new file mode 100644 index 0000000..2ed1912 --- /dev/null +++ b/wit/deps/sockets-0.3.0-rc-2025-09-16/types.wit @@ -0,0 +1,725 @@ +@since(version = 0.3.0-rc-2025-09-16) +interface types { + @since(version = 0.3.0-rc-2025-09-16) + use wasi:clocks/monotonic-clock@0.3.0-rc-2025-09-16.{duration}; + + /// Error codes. + /// + /// In theory, every API can return any error code. + /// In practice, API's typically only return the errors documented per API + /// combined with a couple of errors that are always possible: + /// - `unknown` + /// - `access-denied` + /// - `not-supported` + /// - `out-of-memory` + /// + /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. + @since(version = 0.3.0-rc-2025-09-16) + enum error-code { + /// Unknown error + unknown, + + /// Access denied. + /// + /// POSIX equivalent: EACCES, EPERM + access-denied, + + /// The operation is not supported. + /// + /// POSIX equivalent: EOPNOTSUPP + not-supported, + + /// One of the arguments is invalid. + /// + /// POSIX equivalent: EINVAL + invalid-argument, + + /// Not enough memory to complete the operation. + /// + /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + out-of-memory, + + /// The operation timed out before it could finish completely. + timeout, + + /// The operation is not valid in the socket's current state. + invalid-state, + + /// A bind operation failed because the provided address is not an address that the `network` can bind to. + address-not-bindable, + + /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. + address-in-use, + + /// The remote address is not reachable + remote-unreachable, + + + /// The TCP connection was forcefully rejected + connection-refused, + + /// The TCP connection was reset. + connection-reset, + + /// A TCP connection was aborted. + connection-aborted, + + + /// The size of a datagram sent to a UDP socket exceeded the maximum + /// supported size. + datagram-too-large, + } + + @since(version = 0.3.0-rc-2025-09-16) + enum ip-address-family { + /// Similar to `AF_INET` in POSIX. + ipv4, + + /// Similar to `AF_INET6` in POSIX. + ipv6, + } + + @since(version = 0.3.0-rc-2025-09-16) + type ipv4-address = tuple; + @since(version = 0.3.0-rc-2025-09-16) + type ipv6-address = tuple; + + @since(version = 0.3.0-rc-2025-09-16) + variant ip-address { + ipv4(ipv4-address), + ipv6(ipv6-address), + } + + @since(version = 0.3.0-rc-2025-09-16) + record ipv4-socket-address { + /// sin_port + port: u16, + /// sin_addr + address: ipv4-address, + } + + @since(version = 0.3.0-rc-2025-09-16) + record ipv6-socket-address { + /// sin6_port + port: u16, + /// sin6_flowinfo + flow-info: u32, + /// sin6_addr + address: ipv6-address, + /// sin6_scope_id + scope-id: u32, + } + + @since(version = 0.3.0-rc-2025-09-16) + variant ip-socket-address { + ipv4(ipv4-socket-address), + ipv6(ipv6-socket-address), + } + + /// A TCP socket resource. + /// + /// The socket can be in one of the following states: + /// - `unbound` + /// - `bound` (See note below) + /// - `listening` + /// - `connecting` + /// - `connected` + /// - `closed` + /// See + /// for more information. + /// + /// Note: Except where explicitly mentioned, whenever this documentation uses + /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. + /// (i.e. `bound`, `listening`, `connecting` or `connected`) + /// + /// In addition to the general error codes documented on the + /// `types::error-code` type, TCP socket methods may always return + /// `error(invalid-state)` when in the `closed` state. + @since(version = 0.3.0-rc-2025-09-16) + resource tcp-socket { + + /// Create a new TCP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// Unlike POSIX, WASI sockets have no notion of a socket-level + /// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's + /// async support. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + create: static func(address-family: ip-address-family) -> result; + + /// Bind the socket to the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// Bind can be attempted multiple times on the same socket, even with + /// different arguments on each iteration. But never concurrently and + /// only as long as the previous bind failed. Once a bind succeeds, the + /// binding can't be changed anymore. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that can be bound to. (EADDRNOTAVAIL) + /// + /// # Implementors note + /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT + /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR + /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior + /// and SO_REUSEADDR performs something different entirely. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + bind: func(local-address: ip-socket-address) -> result<_, error-code>; + + /// Connect to a remote endpoint. + /// + /// On success, the socket is transitioned into the `connected` state and this function returns a connection resource. + /// + /// After a failed connection attempt, the socket will be in the `closed` + /// state and the only valid action left is to `drop` the socket. A single + /// socket can not be used to connect more than once. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) + /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `invalid-state`: The socket is already in the `connecting` state. (EALREADY) + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN) + /// - `invalid-state`: The socket is already in the `listening` state. (EOPNOTSUPP, EINVAL on Windows) + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + connect: async func(remote-address: ip-socket-address) -> result<_, error-code>; + + /// Start listening and return a stream of new inbound connections. + /// + /// Transitions the socket into the `listening` state. This can be called + /// at most once per socket. + /// + /// If the socket is not already explicitly bound, this function will + /// implicitly bind the socket to a random free port. + /// + /// Normally, the returned sockets are bound, in the `connected` state + /// and immediately ready for I/O. Though, depending on exact timing and + /// circumstances, a newly accepted connection may already be `closed` + /// by the time the server attempts to perform its first I/O on it. This + /// is true regardless of whether the WASI implementation uses + /// "synthesized" sockets or not (see Implementors Notes below). + /// + /// The following properties are inherited from the listener socket: + /// - `address-family` + /// - `keep-alive-enabled` + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// - `hop-limit` + /// - `receive-buffer-size` + /// - `send-buffer-size` + /// + /// # Typical errors + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN, EINVAL on BSD) + /// - `invalid-state`: The socket is already in the `listening` state. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// + /// # Implementors note + /// This method returns a single perpetual stream that should only close + /// on fatal errors (if any). Yet, the POSIX' `accept` function may also + /// return transient errors (e.g. ECONNABORTED). The exact details differ + /// per operation system. For example, the Linux manual mentions: + /// + /// > Linux accept() passes already-pending network errors on the new + /// > socket as an error code from accept(). This behavior differs from + /// > other BSD socket implementations. For reliable operation the + /// > application should detect the network errors defined for the + /// > protocol after accept() and treat them like EAGAIN by retrying. + /// > In the case of TCP/IP, these are ENETDOWN, EPROTO, ENOPROTOOPT, + /// > EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP, and ENETUNREACH. + /// Source: https://man7.org/linux/man-pages/man2/accept.2.html + /// + /// WASI implementations have two options to handle this: + /// - Optionally log it and then skip over non-fatal errors returned by + /// `accept`. Guest code never gets to see these failures. Or: + /// - Synthesize a `tcp-socket` resource that exposes the error when + /// attempting to send or receive on it. Guest code then sees these + /// failures as regular I/O errors. + /// + /// In either case, the stream returned by this `listen` method remains + /// operational. + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + listen: func() -> result, error-code>; + + /// Transmit data to peer. + /// + /// The caller should close the stream when it has no more data to send + /// to the peer. Under normal circumstances this will cause a FIN packet + /// to be sent out. Closing the stream is equivalent to calling + /// `shutdown(SHUT_WR)` in POSIX. + /// + /// This function may be called at most once and returns once the full + /// contents of the stream are transmitted or an error is encountered. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + send: async func(data: stream) -> result<_, error-code>; + + /// Read data from peer. + /// + /// This function returns a `stream` which provides the data received from the + /// socket, and a `future` providing additional error information in case the + /// socket is closed abnormally. + /// + /// If the socket is closed normally, `stream.read` on the `stream` will return + /// `read-status::closed` with no `error-context` and the future resolves to + /// the value `ok`. If the socket is closed abnormally, `stream.read` on the + /// `stream` returns `read-status::closed` with an `error-context` and the future + /// resolves to `err` with an `error-code`. + /// + /// `receive` is meant to be called only once per socket. If it is called more + /// than once, the subsequent calls return a new `stream` that fails as if it + /// were closed abnormally. + /// + /// If the caller is not expecting to receive any data from the peer, + /// they may drop the stream. Any data still in the receive queue + /// will be discarded. This is equivalent to calling `shutdown(SHUT_RD)` + /// in POSIX. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + receive: func() -> tuple, future>>; + + /// Get the bound local address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `get-local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + get-local-address: func() -> result; + + /// Get the remote address. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + get-remote-address: func() -> result; + + /// Whether the socket is in the `listening` state. + /// + /// Equivalent to the SO_ACCEPTCONN socket option. + @since(version = 0.3.0-rc-2025-09-16) + get-is-listening: func() -> bool; + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// This is the value passed to the constructor. + /// + /// Equivalent to the SO_DOMAIN socket option. + @since(version = 0.3.0-rc-2025-09-16) + get-address-family: func() -> ip-address-family; + + /// Hints the desired listen queue size. Implementations are free to ignore this. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// + /// # Typical errors + /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. + /// - `invalid-argument`: (set) The provided value was 0. + /// - `invalid-state`: (set) The socket is in the `connecting` or `connected` state. + @since(version = 0.3.0-rc-2025-09-16) + set-listen-backlog-size: func(value: u64) -> result<_, error-code>; + + /// Enables or disables keepalive. + /// + /// The keepalive behavior can be adjusted using: + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. + /// + /// Equivalent to the SO_KEEPALIVE socket option. + @since(version = 0.3.0-rc-2025-09-16) + get-keep-alive-enabled: func() -> result; + @since(version = 0.3.0-rc-2025-09-16) + set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; + + /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.3.0-rc-2025-09-16) + get-keep-alive-idle-time: func() -> result; + @since(version = 0.3.0-rc-2025-09-16) + set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; + + /// The time between keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPINTVL socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.3.0-rc-2025-09-16) + get-keep-alive-interval: func() -> result; + @since(version = 0.3.0-rc-2025-09-16) + set-keep-alive-interval: func(value: duration) -> result<_, error-code>; + + /// The maximum amount of keepalive packets TCP should send before aborting the connection. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPCNT socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.3.0-rc-2025-09-16) + get-keep-alive-count: func() -> result; + @since(version = 0.3.0-rc-2025-09-16) + set-keep-alive-count: func(value: u32) -> result<_, error-code>; + + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + @since(version = 0.3.0-rc-2025-09-16) + get-hop-limit: func() -> result; + @since(version = 0.3.0-rc-2025-09-16) + set-hop-limit: func(value: u8) -> result<_, error-code>; + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.3.0-rc-2025-09-16) + get-receive-buffer-size: func() -> result; + @since(version = 0.3.0-rc-2025-09-16) + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + @since(version = 0.3.0-rc-2025-09-16) + get-send-buffer-size: func() -> result; + @since(version = 0.3.0-rc-2025-09-16) + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + } + + /// A UDP socket handle. + @since(version = 0.3.0-rc-2025-09-16) + resource udp-socket { + + /// Create a new UDP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// Unlike POSIX, WASI sockets have no notion of a socket-level + /// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's + /// async support. + /// + /// # References: + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + create: static func(address-family: ip-address-family) -> result; + + /// Bind the socket to the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the port is zero, the socket will be bound to a random free port. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that can be bound to. (EADDRNOTAVAIL) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + bind: func(local-address: ip-socket-address) -> result<_, error-code>; + + /// Associate this socket with a specific peer address. + /// + /// On success, the `remote-address` of the socket is updated. + /// The `local-address` may be updated as well, based on the best network + /// path to `remote-address`. If the socket was not already explicitly + /// bound, this function will implicitly bind the socket to a random + /// free port. + /// + /// When a UDP socket is "connected", the `send` and `receive` methods + /// are limited to communicating with that peer only: + /// - `send` can only be used to send to this destination. + /// - `receive` will only return datagrams sent from the provided `remote-address`. + /// + /// The name "connect" was kept to align with the existing POSIX + /// terminology. Other than that, this function only changes the local + /// socket configuration and does not generate any network traffic. + /// The peer is not aware of this "connection". + /// + /// This method may be called multiple times on the same socket to change + /// its association, but only the most recent one will be effective. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// + /// # Implementors note + /// If the socket is already connected, some platforms (e.g. Linux) + /// require a disconnect before connecting to a different peer address. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + connect: func(remote-address: ip-socket-address) -> result<_, error-code>; + + /// Dissociate this socket from its peer address. + /// + /// After calling this method, `send` & `receive` are free to communicate + /// with any address again. + /// + /// The POSIX equivalent of this is calling `connect` with an `AF_UNSPEC` address. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not connected. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + disconnect: func() -> result<_, error-code>; + + /// Send a message on the socket to a particular peer. + /// + /// If the socket is connected, the peer address may be left empty. In + /// that case this is equivalent to `send` in POSIX. Otherwise it is + /// equivalent to `sendto`. + /// + /// Additionally, if the socket is connected, a `remote-address` argument + /// _may_ be provided but then it must be identical to the address + /// passed to `connect`. + /// + /// Implementations may trap if the `data` length exceeds 64 KiB. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `connect`. (EISCONN) + /// - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + send: async func(data: list, remote-address: option) -> result<_, error-code>; + + /// Receive a message on the socket. + /// + /// On success, the return value contains a tuple of the received data + /// and the address of the sender. Theoretical maximum length of the + /// data is 64 KiB. Though in practice, it will typically be less than + /// 1500 bytes. + /// + /// If the socket is connected, the sender address is guaranteed to + /// match the remote address passed to `connect`. + /// + /// # Typical errors + /// - `invalid-state`: The socket has not been bound yet. + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + receive: async func() -> result, ip-socket-address>, error-code>; + + /// Get the current bound address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `get-local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + get-local-address: func() -> result; + + /// Get the address the socket is currently "connected" to. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not "connected" to a specific remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.3.0-rc-2025-09-16) + get-remote-address: func() -> result; + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// This is the value passed to the constructor. + /// + /// Equivalent to the SO_DOMAIN socket option. + @since(version = 0.3.0-rc-2025-09-16) + get-address-family: func() -> ip-address-family; + + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + @since(version = 0.3.0-rc-2025-09-16) + get-unicast-hop-limit: func() -> result; + @since(version = 0.3.0-rc-2025-09-16) + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.3.0-rc-2025-09-16) + get-receive-buffer-size: func() -> result; + @since(version = 0.3.0-rc-2025-09-16) + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + @since(version = 0.3.0-rc-2025-09-16) + get-send-buffer-size: func() -> result; + @since(version = 0.3.0-rc-2025-09-16) + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + } +} diff --git a/wit/deps/sockets-0.3.0-rc-2025-09-16/world.wit b/wit/deps/sockets-0.3.0-rc-2025-09-16/world.wit new file mode 100644 index 0000000..44cc427 --- /dev/null +++ b/wit/deps/sockets-0.3.0-rc-2025-09-16/world.wit @@ -0,0 +1,9 @@ +package wasi:sockets@0.3.0-rc-2025-09-16; + +@since(version = 0.3.0-rc-2025-09-16) +world imports { + @since(version = 0.3.0-rc-2025-09-16) + import types; + @since(version = 0.3.0-rc-2025-09-16) + import ip-name-lookup; +} diff --git a/wit/init.wit b/wit/init.wit index 35f8826..2f5de1a 100644 --- a/wit/init.wit +++ b/wit/init.wit @@ -87,7 +87,6 @@ world init { } record symbols { - types-package: string, exports: list, resources: list<%resource>, records: list<%record>,