diff --git a/.config/dictionaries/project.dic b/.config/dictionaries/project.dic index fe4967f0c..81b511ab7 100644 --- a/.config/dictionaries/project.dic +++ b/.config/dictionaries/project.dic @@ -12,6 +12,7 @@ CEST chainsync chrono ciphertext +codegen codepoints coti cryptoxide @@ -25,12 +26,15 @@ drep dreps encryptor excalidraw +fmtchk +fmtfix fontawesome fsgr genhtml gmtime hardano ideascale +idents Intellij iohk jetbrains @@ -40,6 +44,7 @@ Jörmungandr kroki lcov Leshiy +lintfix localizable mdlint miniprotocols @@ -49,9 +54,11 @@ moderations multiera nanos netkey +nextest oneshot openapi opentelemetry +permissioned permissionless pg_isready plpgsql @@ -62,15 +69,24 @@ pubspec rapidoc redoc rustc +rustdoc +rustdocflags +rustflags saibatizoku seckey slotno stevenj tacho +testcov +testdocs +testunit thiserror timelike +toolsets Traceback vitss voteplan voteplans +WASI +webasm yoroi diff --git a/.vscode/settings.recommended.json b/.vscode/settings.recommended.json index 56d463b65..912a9ca09 100644 --- a/.vscode/settings.recommended.json +++ b/.vscode/settings.recommended.json @@ -53,7 +53,8 @@ "Earthfile": "earthfile" }, "rust-analyzer.linkedProjects": [ - "./hermes/bin/Cargo.toml" + "./hermes/Cargo.toml", + "./wasm/wasi-hermes-component-adapter/Cargo.toml" ], "rust-analyzer.check.overrideCommand": [ "bash", diff --git a/Earthfile b/Earthfile index 161f7bdec..d701fe47d 100644 --- a/Earthfile +++ b/Earthfile @@ -18,7 +18,18 @@ markdown-check-fix: # check-spelling Check spelling in this repo inside a container. check-spelling: DO github.com/input-output-hk/catalyst-ci/earthly/cspell:v2.0.10+CHECK - + +# check-spelling Check spelling in this repo inside a container. +spell-list-words: + FROM ghcr.io/streetsidesoftware/cspell:8.0.0 + WORKDIR /work + + COPY . . + + RUN cspell-cli --words-only --unique "wasm/**" | sort -f + + + repo-docs: # Create artifacts of extra files we embed inside the documentation when its built. FROM scratch diff --git a/cspell.json b/cspell.json index df1c9c013..269b8e0ea 100644 --- a/cspell.json +++ b/cspell.json @@ -227,7 +227,6 @@ "*.excalidraw", ".vscode/**", "**/.idea/**", - "hermes/wasm/*/**", "hermes/crates/cardano-chain-follower/examples/snapshot_data" ] -} +} \ No newline at end of file diff --git a/hermes/.cargo/config.toml b/hermes/.cargo/config.toml index 8f2abc5db..02c231407 100644 --- a/hermes/.cargo/config.toml +++ b/hermes/.cargo/config.toml @@ -1,11 +1,8 @@ # Use MOLD linker where possible, but ONLY in CI applicable targets. -# cspell: words rustflags armv gnueabihf msvc nextest idents rustdocflags -# cspell: words rustdoc lintfix lintrestrict testfast testdocs codegen testci testunit -# cspell: words fmtchk fmtfix testcov # Configure how Docker container targets build. -# If you want to customize these targets for a local build, then customize them in you: +# If you want to customize these targets for a local build, then customize them in your: # $CARGO_HOME/config.toml # NOT in the project itself. # These targets are ONLY the targets used by CI and inside docker builds. @@ -28,48 +25,12 @@ rustflags = [ "-C", "target-feature=-crt-static" ] -[target.wasm32-unknown-unknown] -rustflags = ["--cap-lints", "warn"] - [build] - -rustflags = [ - "-D", - "warnings", - "-D", - "missing_docs", - "-D", - "let_underscore_drop", - "-D", - "non_ascii_idents", - "-D", - "single_use_lifetimes", - "-D", - "trivial_casts", - "-D", - "trivial_numeric_casts", -] - +rustflags = [] rustdocflags = [ "--enable-index-page", "-Z", "unstable-options", - "-D", - "warnings", - "-D", - "missing_docs", - "-D", - "rustdoc::broken_intra_doc_links", - "-D", - "rustdoc::invalid_codeblock_attributes", - "-D", - "rustdoc::invalid_html_tags", - "-D", - "rustdoc::invalid_rust_codeblocks", - "-D", - "rustdoc::bare_urls", - "-D", - "rustdoc::unescaped_backticks", ] [profile.dev] @@ -78,7 +39,7 @@ debug = true debug-assertions = true overflow-checks = true lto = false -panic = 'unwind' +panic = "unwind" incremental = true codegen-units = 256 @@ -88,7 +49,7 @@ debug = false debug-assertions = false overflow-checks = false lto = "thin" -panic = 'unwind' +panic = "unwind" incremental = false codegen-units = 16 @@ -110,14 +71,14 @@ incremental = false codegen-units = 16 [alias] -lint = "clippy --all-targets -- -D warnings -D clippy::pedantic -D clippy::unwrap_used -D clippy::expect_used -D clippy::exit -D clippy::get_unwrap -D clippy::index_refutable_slice -D clippy::indexing_slicing -D clippy::match_on_vec_items -D clippy::match_wild_err_arm -D clippy::missing_panics_doc -D clippy::panic -D clippy::string_slice -D clippy::unchecked_duration_subtraction -D clippy::unreachable -D clippy::missing_docs_in_private_items" -lintfix = "clippy --all-targets --fix --allow-dirty -- -D warnings -D clippy::pedantic -D clippy::unwrap_used -D clippy::expect_used -D clippy::exit -D clippy::get_unwrap -D clippy::index_refutable_slice -D clippy::indexing_slicing -D clippy::match_on_vec_items -D clippy::match_wild_err_arm -D clippy::missing_panics_doc -D clippy::panic -D clippy::string_slice -D clippy::unchecked_duration_subtraction -D clippy::unreachable -D clippy::missing_docs_in_private_items" -lint-vscode = "clippy --message-format=json-diagnostic-rendered-ansi --all-targets -- -D warnings -D clippy::pedantic -D clippy::unwrap_used -D clippy::expect_used -D clippy::exit -D clippy::get_unwrap -D clippy::index_refutable_slice -D clippy::indexing_slicing -D clippy::match_on_vec_items -D clippy::match_wild_err_arm -D clippy::missing_panics_doc -D clippy::panic -D clippy::string_slice -D clippy::unchecked_duration_subtraction -D clippy::unreachable -D clippy::missing_docs_in_private_items" +lint = "clippy --all-targets" +lintfix = "clippy --all-targets --fix --allow-dirty" +lint-vscode = "clippy --message-format=json-diagnostic-rendered-ansi --all-targets" docs = "doc --release --no-deps --document-private-items --bins --lib --examples" # nightly docs build broken... when they are'nt we can enable these docs... --unit-graph --timings=html,json -Z unstable-options" -testunit = "nextest run --release --bins --lib -P ci" -testcov = "llvm-cov nextest --release --bins --lib -P ci" +testunit = "nextest run --release --bins --lib --tests --benches --no-fail-fast -P ci" +testcov = "llvm-cov nextest --release --bins --lib --tests --benches --no-fail-fast -P ci" testdocs = "test --doc --release" # Rust formatting, MUST be run with +nightly @@ -127,6 +88,6 @@ fmtfix = "fmt -- -v" [term] quiet = false # whether cargo output is quiet verbose = false # whether cargo provides verbose output -color = 'auto' # whether cargo colorizes output use `CARGO_TERM_COLOR="off"` to disable. -progress.when = 'never' # whether cargo shows progress bar +color = "auto" # whether cargo colorizes output use `CARGO_TERM_COLOR="off"` to disable. +progress.when = "never" # whether cargo shows progress bar progress.width = 80 # width of progress bar diff --git a/hermes/.config/nextest.toml b/hermes/.config/nextest.toml index 86c177122..be3673830 100644 --- a/hermes/.config/nextest.toml +++ b/hermes/.config/nextest.toml @@ -1,4 +1,4 @@ -# cspell: words nextest scrollability testcase +# cspell: words scrollability testcase [store] # The directory under the workspace root at which nextest-related files are # written. Profile-specific storage is currently written to dir/. diff --git a/hermes/Cargo.toml b/hermes/Cargo.toml index b46b944c3..c1fdc4c33 100644 --- a/hermes/Cargo.toml +++ b/hermes/Cargo.toml @@ -16,6 +16,40 @@ homepage = "https://input-output-hk.github.io/hermes" repository = "https://github.com/input-output-hk/hermes" license = "MIT OR Apache-2.0" +[workspace.lints.rust] +warnings = "deny" +missing_docs = "deny" +let_underscore_drop = "deny" +non_ascii_idents = "deny" +single_use_lifetimes = "deny" +trivial_casts = "deny" +trivial_numeric_casts = "deny" + +[workspace.lints.rustdoc] +broken_intra_doc_links = "deny" +invalid_codeblock_attributes = "deny" +invalid_html_tags = "deny" +invalid_rust_codeblocks = "deny" +bare_urls = "deny" +unescaped_backticks = "deny" + +[workspace.lints.clippy] +pedantic = "deny" +unwrap_used = "deny" +expect_used = "deny" +exit = "deny" +get_unwrap = "deny" +index_refutable_slice = "deny" +indexing_slicing = "deny" +match_on_vec_items = "deny" +match_wild_err_arm = "deny" +missing_panics_doc = "deny" +panic = "deny" +string_slice = "deny" +unchecked_duration_subtraction = "deny" +unreachable = "deny" +missing_docs_in_private_items = "deny" + [workspace.dependencies] # Stop using catalyst-pallas once pallas changes are merged. # issue: https://github.com/input-output-hk/hermes/issues/63 diff --git a/hermes/Earthfile b/hermes/Earthfile index 9d2032b55..075859c08 100644 --- a/hermes/Earthfile +++ b/hermes/Earthfile @@ -1,14 +1,14 @@ VERSION 0.7 -#cspell: words rustfmt toolsets USERARCH +#cspell: words rustfmt # Set up our target toolchains, and copy our files. builder: - FROM github.com/input-output-hk/catalyst-ci/earthly/rust:v2.4.0+rust-base + DO github.com/input-output-hk/catalyst-ci/earthly/rust:v2.6.0+SETUP - DO github.com/input-output-hk/catalyst-ci/earthly/rust:v2.4.0+SETUP - - COPY --dir .cargo .config Cargo.* clippy.toml deny.toml rustfmt.toml bin crates . + COPY --dir .cargo .config crates bin . + COPY Cargo.toml . + COPY clippy.toml deny.toml rustfmt.toml . ## ----------------------------------------------------------------------------- ## @@ -33,9 +33,7 @@ all-hosts-check: build: FROM +builder - RUN /scripts/std_build.py --with_test \ - --with_bench \ - --libs="cardano-chain-follower" \ + RUN /scripts/std_build.py --libs="cardano-chain-follower" \ --bins="hermes/hermes" SAVE ARTIFACT target/$TARGETARCH/doc doc diff --git a/hermes/README.md b/hermes/README.md new file mode 100644 index 000000000..fadec5a4e --- /dev/null +++ b/hermes/README.md @@ -0,0 +1,21 @@ + + +# Hermes core + +An implementation of the Hermes core engine in Rust + +* [Hermes core](#hermes-core) + * [Build notes](#build-notes) + +## Build notes + +During the build process, you may encounter specific known issues: +[tower/issues/466](https://github.com/tower-rs/tower/issues/466) +and [indexmap/issues/151](https://github.com/indexmap-rs/indexmap/issues/151). +These issues can impede the build's success. +We recommend explicitly setting the environment variable `CARGO_FEATURE_STD=1` as a temporary solution. +This workaround has effectively bypassed the mentioned problems until a permanent fix is implemented. + +```shell +CARGO_FEATURE_STD=1 cargo b +``` diff --git a/hermes/bin/Cargo.toml b/hermes/bin/Cargo.toml index 2b758ecc5..98aa71149 100644 --- a/hermes/bin/Cargo.toml +++ b/hermes/bin/Cargo.toml @@ -8,3 +8,6 @@ authors.workspace = true edition.workspace = true license.workspace = true repository.workspace = true + +[lints] +workspace = true diff --git a/hermes/crates/cardano-chain-follower/Cargo.toml b/hermes/crates/cardano-chain-follower/Cargo.toml index af7a669ec..40ccee5e0 100644 --- a/hermes/crates/cardano-chain-follower/Cargo.toml +++ b/hermes/crates/cardano-chain-follower/Cargo.toml @@ -8,6 +8,9 @@ homepage.workspace = true repository.workspace = true license.workspace = true +[lints] +workspace = true + [dependencies] pallas.workspace = true pallas-hardano.workspace = true diff --git a/hermes/deny.toml b/hermes/deny.toml index 4ac843fb5..926f2ef43 100644 --- a/hermes/deny.toml +++ b/hermes/deny.toml @@ -279,4 +279,4 @@ allow-git = [ # 1 or more gitlab.com organizations to allow git sources for #gitlab = [""] # 1 or more bitbucket.org organizations to allow git sources for -#bitbucket = [""] +#bitbucket = [""] \ No newline at end of file diff --git a/hermes/rust-toolchain.toml b/hermes/rust-toolchain.toml index 8209e4fd9..660e046d8 100644 --- a/hermes/rust-toolchain.toml +++ b/hermes/rust-toolchain.toml @@ -1,5 +1,4 @@ [toolchain] -channel = "1.73.0" +channel = "1.75.0" profile = "default" -components = [] targets = ["x86_64-unknown-linux-musl"] diff --git a/hermes/wasm/.cargo/config.toml b/hermes/wasm/.cargo/config.toml deleted file mode 100644 index 20fc00a39..000000000 --- a/hermes/wasm/.cargo/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -[build] - -target = "wasm32-unknown-unknown" - -rustflags = ["--cap-lints", "warn"] diff --git a/hermes/wasm/Cargo.toml b/hermes/wasm/Cargo.toml deleted file mode 100644 index 39b28f54d..000000000 --- a/hermes/wasm/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -#cspell: words Apisit Ritreungroj wasmparser wasi bindgen - -[workspace] -resolver = "2" -members = [ - "crates/wasi-component-adapter", - "crates/wasi-component-adapter/verify", -] - -[workspace.package] -edition = "2021" -version = "0.0.1" -authors = [ - "Apisit Ritreungroj ", - "Steven Johnson " -] -rust-version = "1.73" -homepage = "https://input-output-hk.github.io/hermes" -repository = "https://github.com/input-output-hk/hermes" -license = "MIT OR Apache-2.0" - -[workspace.dependencies] - -# for `wasi-preview1-component-adapter` -wit-bindgen = "0.16.0" -byte-array-literals = { path = "./crates/wasi-component-adapter/byte-array-literals", version = "0.0.1" } -wasm-encoder = "0.38.1" -object = "0.32.1" - -# for `wasi-preview1-component-adapter/verify` -wasmparser = "0.118.1" -wat = "1.0.82" -anyhow = "1.0.76" - -[workspace.lints] diff --git a/hermes/wasm/Earthfile b/hermes/wasm/Earthfile deleted file mode 100644 index e9070b1a5..000000000 --- a/hermes/wasm/Earthfile +++ /dev/null @@ -1,49 +0,0 @@ -VERSION 0.7 - -#cspell: words fmtchk rustfmt toolsets wasi wasmtime Earthfile - -# Fork the repo `wasmtime` and extract only the `wasi-preview1-component-adapter` crate from it. -fork-wasi-component-adapter: - LOCALLY - - # The local directory to store `wasi/wit`. - # This needs to include along with `wasi-preview1-component-adapter`. - ARG wit_local_dir=crates/wasi - # The local directory to place the extracted `wasi` crate. - ARG wasi_local_dir=crates/wasi-component-adapter - # The location after the `wasmtime` repo was cloned. - ARG wasi_git_dir=wasmtime/crates/wasi-preview1-component-adapter - - # Remove the existing local one. Clone and extract it from the repo. - RUN rm -rf $wasi_local_dir/ || true && \ - rm -rf $wit_local_dir/ || true && \ - git clone --depth 1 https://github.com/bytecodealliance/wasmtime.git && \ - mv $wasi_git_dir $wasi_local_dir && \ - mkdir $wit_local_dir && \ - mv wasmtime/crates/wasi/wit $wit_local_dir/wit && \ - rm -rf wasmtime/ - -# Set up our target toolchains, and copy our files. -builder: - FROM github.com/input-output-hk/catalyst-ci/earthly/rust:v2.0.3+rust-base - - DO github.com/input-output-hk/catalyst-ci/earthly/rust:v2.0.3+SETUP --toolchain=rust-toolchain.toml - - COPY --dir .cargo Cargo.* clippy.toml deny.toml rustfmt.toml crates . - -# Run quality checks. -check: - FROM +builder - - RUN cargo machete && \ - # cargo +nightly fmtchk && \ - cargo deny check - - # DO github.com/input-output-hk/catalyst-ci/earthly/rust:v2.0.3+CHECK - -build: - FROM +builder - - RUN cargo build --release - - SAVE ARTIFACT target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.wasm wasi-component-adapter.wasm diff --git a/hermes/wasm/clippy.toml b/hermes/wasm/clippy.toml deleted file mode 100644 index 154626ef4..000000000 --- a/hermes/wasm/clippy.toml +++ /dev/null @@ -1 +0,0 @@ -allow-unwrap-in-tests = true diff --git a/hermes/wasm/crates/wasi-component-adapter/Cargo.toml b/hermes/wasm/crates/wasi-component-adapter/Cargo.toml deleted file mode 100644 index cae715a7d..000000000 --- a/hermes/wasm/crates/wasi-component-adapter/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "wasi-preview1-component-adapter" -version.workspace = true -authors.workspace = true -edition.workspace = true -publish = false - -[lints] -workspace = true - -[dependencies] -wasi = { version = "0.11.0", default-features = false } -wit-bindgen = { workspace = true, default-features = false, features = ["macros"] } -byte-array-literals = { workspace = true } - -[build-dependencies] -wasm-encoder = { workspace = true } -object = { workspace = true, default-features = false, features = ["archive"] } - -[lib] -test = false -crate-type = ["cdylib"] -name = "wasi_snapshot_preview1" - -[features] -default = ["reactor"] -reactor = [] -command = [] -proxy = [] diff --git a/hermes/wasm/crates/wasi-component-adapter/README.md b/hermes/wasm/crates/wasi-component-adapter/README.md deleted file mode 100644 index ec0834318..000000000 --- a/hermes/wasm/crates/wasi-component-adapter/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# `wasi_snapshot_preview1.wasm` - -> **Note**: This repository is a work in progress. This is intended to be an -> internal tool which not everyone has to look at but many might rely on. You -> may need to reach out via issues or -> [Zulip](https://bytecodealliance.zulipchat.com/) to learn more about this -> repository. - -This repository currently contains an implementation of a WebAssembly module: -`wasi_snapshot_preview1.wasm`. This module bridges the `wasi_snapshot_preview1` -ABI to the preview2 ABI of the component model. At this time the preview2 APIs -themselves are not done being specified so a local copy of `wit/*.wit` is used -instead. - -## Building - -This adapter can be built with: - -```sh -$ cargo build -p wasi-preview1-component-adapter --target wasm32-unknown-unknown --release -``` - -And the artifact will be located at -`target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.wasm`. - -This by default builds a "reactor" adapter which means that it only provides -adaptation from preview1 to preview2. Alternatively you can also build a -"command" adapter by passing `--features command --no-default-features` which -will additionally export a `run` function entrypoint. This is suitable for use -with preview1 binaries that export a `_start` function. - -Alternatively the latest copy of the command and reactor adapters can be -[downloaded from the `dev` tag assets][dev-tag] - -[dev-tag]: https://github.com/bytecodealliance/wasmtime/releases/tag/dev - -## Using - -With a `wasi_snapshot_preview1.wasm` file on-hand you can create a component -from a module that imports WASI functions using the [`wasm-tools` -CLI](https://github.com/bytecodealliance/wasm-tools) - -```sh -$ cat foo.rs -fn main() { - println!("Hello, world!"); -} -$ rustc foo.rs --target wasm32-wasi -$ wasm-tools print foo.wasm | grep '(import' - (import "wasi_snapshot_preview1" "fd_write" (func ... - (import "wasi_snapshot_preview1" "environ_get" (func ... - (import "wasi_snapshot_preview1" "environ_sizes_get" ... - (import "wasi_snapshot_preview1" "proc_exit" (func ... -$ wasm-tools component new foo.wasm --adapt wasi_snapshot_preview1.wasm -o component.wasm - -# Inspect the generated `component.wasm` -$ wasm-tools validate component.wasm --features component-model -$ wasm-tools component wit component.wasm -``` - -Here the `component.wasm` that's generated is a ready-to-run component which -imports wasi preview2 functions and is compatible with the wasi-preview1-using -module internally. diff --git a/hermes/wasm/crates/wasi-component-adapter/byte-array-literals/src/lib.rs b/hermes/wasm/crates/wasi-component-adapter/byte-array-literals/src/lib.rs deleted file mode 100644 index ea9f013d9..000000000 --- a/hermes/wasm/crates/wasi-component-adapter/byte-array-literals/src/lib.rs +++ /dev/null @@ -1,98 +0,0 @@ -extern crate proc_macro; - -use proc_macro::{Delimiter, Group, Literal, Punct, Spacing, TokenStream, TokenTree}; - -/// Expand a `str` literal into a byte array. -#[proc_macro] -pub fn str(input: TokenStream) -> TokenStream { - let rv = convert_str(input); - - vec![TokenTree::Group(Group::new( - Delimiter::Bracket, - rv.into_iter().collect(), - ))] - .into_iter() - .collect() -} - -/// The same as `str` but appends a `'\n'`. -#[proc_macro] -pub fn str_nl(input: TokenStream) -> TokenStream { - let mut rv = convert_str(input); - - rv.push(TokenTree::Literal(Literal::u8_suffixed(b'\n'))); - - vec![TokenTree::Group(Group::new( - Delimiter::Bracket, - rv.into_iter().collect(), - ))] - .into_iter() - .collect() -} - -fn convert_str(input: TokenStream) -> Vec { - let mut it = input.into_iter(); - - let mut tokens = Vec::new(); - match it.next() { - Some(TokenTree::Literal(l)) => { - for b in to_string(l).into_bytes() { - tokens.push(TokenTree::Literal(Literal::u8_suffixed(b))); - tokens.push(TokenTree::Punct(Punct::new(',', Spacing::Alone))); - } - } - _ => panic!(), - } - - assert!(it.next().is_none()); - tokens -} - -fn to_string(lit: Literal) -> String { - let formatted = lit.to_string(); - - let mut it = formatted.chars(); - assert_eq!(it.next(), Some('"')); - - let mut rv = String::new(); - loop { - match it.next() { - Some('"') => match it.next() { - Some(_) => panic!(), - None => break, - }, - Some('\\') => match it.next() { - Some('x') => { - let hi = it.next().unwrap().to_digit(16).unwrap(); - let lo = it.next().unwrap().to_digit(16).unwrap(); - let v = (hi << 16) | lo; - rv.push(v as u8 as char); - } - Some('u') => { - assert_eq!(it.next(), Some('{')); - let mut c = it.next().unwrap(); - let mut ch = 0; - while let Some(v) = c.to_digit(16) { - ch *= 16; - ch |= v; - c = it.next().unwrap(); - } - assert_eq!(c, '}'); - rv.push(::std::char::from_u32(ch).unwrap()); - } - Some('0') => rv.push('\0'), - Some('\\') => rv.push('\\'), - Some('\"') => rv.push('\"'), - Some('r') => rv.push('\r'), - Some('n') => rv.push('\n'), - Some('t') => rv.push('\t'), - Some(_) => panic!(), - None => panic!(), - }, - Some(c) => rv.push(c), - None => panic!(), - } - } - - rv -} diff --git a/hermes/wasm/crates/wasi-component-adapter/verify/Cargo.toml b/hermes/wasm/crates/wasi-component-adapter/verify/Cargo.toml deleted file mode 100644 index f739dd707..000000000 --- a/hermes/wasm/crates/wasi-component-adapter/verify/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "verify-component-adapter" -version.workspace = true -edition.workspace = true -authors.workspace = true -publish = false - -[lints] -workspace = true - -[dependencies] -wasmparser = { workspace = true } -wat = { workspace = true } -anyhow = { workspace = true } diff --git a/hermes/wasm/crates/wasi-component-adapter/verify/README.md b/hermes/wasm/crates/wasi-component-adapter/verify/README.md deleted file mode 100644 index dc4768b56..000000000 --- a/hermes/wasm/crates/wasi-component-adapter/verify/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# verify-component-adapter - -The `wasi-preview1-component-adapter` crate must compile to a wasm binary that -meets a challenging set of constraints, in order to be used as an adapter by -the `wasm-tools component new` tool. - -There are a limited set of wasm sections allowed in the binary, and a limited -set of wasm modules we allow imports from. - -This crate is a bin target which parses a wasm file and reports an error if it -does not fit in those constraints. diff --git a/hermes/wasm/crates/wasi-component-adapter/verify/src/main.rs b/hermes/wasm/crates/wasi-component-adapter/verify/src/main.rs deleted file mode 100644 index d013fe1fb..000000000 --- a/hermes/wasm/crates/wasi-component-adapter/verify/src/main.rs +++ /dev/null @@ -1,86 +0,0 @@ -use anyhow::{bail, Result}; -use std::env; -use wasmparser::*; - -fn main() -> Result<()> { - let file = env::args() - .nth(1) - .expect("must pass wasm file as an argument"); - let wasm = wat::parse_file(&file)?; - - let mut validator = Validator::new(); - for payload in Parser::new(0).parse_all(&wasm) { - let payload = payload?; - validator.payload(&payload)?; - match payload { - Payload::Version { encoding, .. } => { - if encoding != Encoding::Module { - bail!("adapter must be a core wasm module, not a component"); - } - } - Payload::End(_) => {} - Payload::TypeSection(_) => {} - Payload::ImportSection(s) => { - for i in s { - let i = i?; - match i.ty { - TypeRef::Func(_) => { - if i.module.starts_with("wasi:") { - continue; - } - if i.module == "__main_module__" { - continue; - } - bail!("import from unknown module `{}`", i.module); - } - TypeRef::Table(_) => bail!("should not import table"), - TypeRef::Global(_) => bail!("should not import globals"), - TypeRef::Memory(_) => {} - TypeRef::Tag(_) => bail!("unsupported `tag` type"), - } - } - } - Payload::TableSection(_) => {} - Payload::MemorySection(_) => { - bail!("preview1.wasm should import memory"); - } - Payload::GlobalSection(_) => {} - - Payload::ExportSection(_) => {} - - Payload::FunctionSection(_) => {} - - Payload::CodeSectionStart { .. } => {} - Payload::CodeSectionEntry(_) => {} - Payload::CustomSection(_) => {} - - // sections that shouldn't appear in the specially-crafted core wasm - // adapter self we're processing - Payload::DataCountSection { .. } - | Payload::ElementSection(_) - | Payload::DataSection(_) - | Payload::StartSection { .. } - | Payload::TagSection(_) - | Payload::UnknownSection { .. } => { - bail!("unsupported section {payload:?} found in preview1.wasm") - } - - // component-model related things that shouldn't show up - Payload::ModuleSection { .. } - | Payload::ComponentSection { .. } - | Payload::InstanceSection(_) - | Payload::ComponentInstanceSection(_) - | Payload::ComponentAliasSection(_) - | Payload::ComponentCanonicalSection(_) - | Payload::ComponentStartSection { .. } - | Payload::ComponentImportSection(_) - | Payload::CoreTypeSection(_) - | Payload::ComponentExportSection(_) - | Payload::ComponentTypeSection(_) => { - bail!("component section found in preview1.wasm") - } - } - } - - Ok(()) -} diff --git a/hermes/wasm/crates/wasi/wit/deps/cli/imports.wit b/hermes/wasm/crates/wasi/wit/deps/cli/imports.wit deleted file mode 100644 index 9965ea35e..000000000 --- a/hermes/wasm/crates/wasi/wit/deps/cli/imports.wit +++ /dev/null @@ -1,20 +0,0 @@ -package wasi:cli@0.2.0-rc-2023-12-05; - -world imports { - include wasi:clocks/imports@0.2.0-rc-2023-11-10; - include wasi:filesystem/imports@0.2.0-rc-2023-11-10; - include wasi:sockets/imports@0.2.0-rc-2023-11-10; - include wasi:random/imports@0.2.0-rc-2023-11-10; - include wasi:io/imports@0.2.0-rc-2023-11-10; - - import environment; - import exit; - import stdin; - import stdout; - import stderr; - import terminal-input; - import terminal-output; - import terminal-stdin; - import terminal-stdout; - import terminal-stderr; -} diff --git a/hermes/wasm/crates/wasi/wit/deps/io/world.wit b/hermes/wasm/crates/wasi/wit/deps/io/world.wit deleted file mode 100644 index 8243da2ee..000000000 --- a/hermes/wasm/crates/wasi/wit/deps/io/world.wit +++ /dev/null @@ -1,6 +0,0 @@ -package wasi:io@0.2.0-rc-2023-11-10; - -world imports { - import streams; - import poll; -} diff --git a/hermes/wasm/crates/wasi/wit/test.wit b/hermes/wasm/crates/wasi/wit/test.wit deleted file mode 100644 index f3cc6151b..000000000 --- a/hermes/wasm/crates/wasi/wit/test.wit +++ /dev/null @@ -1,22 +0,0 @@ -package wasmtime:wasi; - -// only used as part of `test-programs` -world test-reactor { - include wasi:cli/imports@0.2.0-rc-2023-12-05; - - export add-strings: func(s: list) -> u32; - export get-strings: func() -> list; - - use wasi:io/streams@0.2.0-rc-2023-11-10.{output-stream}; - - export write-strings-to: func(o: output-stream) -> result; - - use wasi:filesystem/types@0.2.0-rc-2023-11-10.{descriptor-stat}; - export pass-an-imported-record: func(d: descriptor-stat) -> string; -} - -world test-command { - include wasi:cli/imports@0.2.0-rc-2023-12-05; - import wasi:http/types@0.2.0-rc-2023-12-05; - import wasi:http/outgoing-handler@0.2.0-rc-2023-12-05; -} diff --git a/wasm/.cspell.json b/wasm/.cspell.json new file mode 100644 index 000000000..52560cc36 --- /dev/null +++ b/wasm/.cspell.json @@ -0,0 +1,283 @@ +{ + "$schema": "https: //raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", + "version": "0.2", + "words": [ + "ABSTIME", + "ACCEPTCONN", + "ACCES", + "ADDRFAMILY", + "ATIM", + "atim", + "BADF", + "badfd", + "bindgen", + "bufused", + "bytecodealliance", + "bytestream", + "cabi", + "Ciovec", + "CLOCKID", + "Clockid", + "consistient", + "constness", + "CREAT", + "CSPRNG", + "ctim", + "ctypes", + "datagram", + "datagrams", + "datalen", + "DEADLK", + "DGRAM", + "DIRCOOKIE", + "Dircookie", + "dirflags", + "disginguish", + "doews", + "Dont", + "dont", + "DONTNEED", + "DQUOT", + "DSYNC", + "EACCES", + "EADDRINUSE", + "EADDRNOTAVAIL", + "EAFNOSUPPORT", + "EAGAIN", + "EALREADY", + "EBADF", + "EBUSY", + "ECONNABORTED", + "ECONNREFUSED", + "ECONNRESET", + "EDEADLK", + "EDESTADDRREQ", + "EDQUOT", + "EEXIST", + "EFAULT", + "EFBIG", + "EHOSTDOWN", + "EHOSTUNREACH", + "EILSEQ", + "EINPROGRESS", + "EINTR", + "EINVAL", + "EISCONN", + "EISDIR", + "ELOOP", + "EMFILE", + "EMLINK", + "EMSGSIZE", + "ENAMETOOLONG", + "encourated", + "ENETDOWN", + "ENETRESET", + "ENETUNREACH", + "ENFILE", + "ENOBUFS", + "ENODEV", + "ENOENT", + "ENOLCK", + "ENOMEM", + "ENONET", + "ENOSPC", + "ENOSYS", + "ENOTCONN", + "ENOTDIR", + "ENOTEMPTY", + "ENOTRECOVERABLE", + "ENOTSUP", + "ENOTTY", + "environc", + "ENXIO", + "EOPNOTSUPP", + "EOVERFLOW", + "EPERM", + "EPIPE", + "epresents", + "Equivelant", + "equivelant", + "EROFS", + "ERRNO", + "Errno", + "errno", + "ESPIPE", + "ETIMEDOUT", + "ETXTBSY", + "eventaully", + "EVENTRWFLAGS", + "EVENTTYPE", + "EWOULDBLOCK", + "EXDEV", + "Exitcode", + "fadvise", + "fallocate", + "FBIG", + "fcntl", + "fdatasync", + "FDFLAGS", + "Fdflags", + "fdflags", + "FDSTAT", + "Fdstat", + "fdstat", + "Filedelta", + "Filesize", + "filesize", + "FILESTAT", + "Filestat", + "filestat", + "filesystems", + "flowinfo", + "freelist", + "fstat", + "fstatat", + "FSTFLAGS", + "Fstflags", + "ftruncate", + "futimens", + "getaddrinfo", + "GETFL", + "getres", + "globaltype", + "IDNA", + "IFMT", + "Illumos", + "ILSEQ", + "INADDR", + "inducating", + "integrety", + "INVAL", + "inval", + "Iovec", + "iovs", + "IPPROTO", + "ISDIR", + "isdir", + "KEEPCNT", + "KEEPIDLE", + "KEEPINTVL", + "libc", + "libfoo", + "libwasm", + "linkat", + "LOOKUPFLAGS", + "Lookupflags", + "lookupflags", + "lseek", + "mkdirat", + "MLINK", + "MSGSIZE", + "MTIM", + "mtim", + "NAMETOOLONG", + "namlen", + "nbytes", + "nead", + "nevents", + "newoffset", + "nlink", + "NODATA", + "NODEV", + "NOENT", + "NOLCK", + "NOMEM", + "NONAME", + "NONBLOCK", + "nonoverlapping", + "NOREUSE", + "NOSPC", + "nostack", + "NOTDIR", + "NOTEMPTY", + "notempty", + "NOTRECOVERABLE", + "NOTSUP", + "NOTTY", + "nread", + "nsubscriptions", + "nwritten", + "NXIO", + "occured", + "OFLAGS", + "Oflags", + "oflags", + "oneoff", + "openat", + "outparam", + "Pollable", + "pollable", + "Pollables", + "pollables", + "pread", + "preadv", + "Preopen", + "preopen", + "Preopened", + "preopened", + "preopens", + "Prestat", + "prestat", + "propogate", + "Punct", + "pwrite", + "pwritev", + "rcode", + "RCVBUF", + "RDWR", + "READDIR", + "readdir", + "readlinkat", + "readv", + "reaedy", + "realloc", + "recieved", + "recvfrom", + "reloc", + "REMOVEDIR", + "renameat", + "repr", + "REUSEADDR", + "Riflags", + "Roflags", + "ROFS", + "romem", + "sandboxing", + "satisfry", + "sched", + "Sdflags", + "sendto", + "SETFL", + "Siflags", + "SNDBUF", + "SPIPE", + "SUBCLOCKFLAGS", + "sunfishcode", + "symlinkat", + "syms", + "SYMTAB", + "symtab", + "systimespec", + "transfered", + "TXTBSY", + "Uninit", + "uninit", + "unlinkat", + "unrepresentable", + "unroutable", + "UNSPEC", + "upwrap", + "userdata", + "userlands", + "utimensat", + "Vecs", + "vtables", + "wasmtime", + "wasmtime's", + "WILLNEED", + "writev", + "writting", + "XDEV", + "zstack" + ] +} \ No newline at end of file diff --git a/wasm/Earthfile b/wasm/Earthfile new file mode 100644 index 000000000..e8d9bf884 --- /dev/null +++ b/wasm/Earthfile @@ -0,0 +1,12 @@ +VERSION 0.7 + +#cspell: words rustfmt wasi + +# Make am Artifact which consists of the WASI SRC. +wasi-src: + FROM scratch + + WORKDIR / + COPY --dir wasi . + + SAVE ARTIFACT /wasi wasi diff --git a/wasm/wasi-hermes-component-adapter/.cargo/config.toml b/wasm/wasi-hermes-component-adapter/.cargo/config.toml new file mode 100644 index 000000000..02c231407 --- /dev/null +++ b/wasm/wasi-hermes-component-adapter/.cargo/config.toml @@ -0,0 +1,93 @@ +# Use MOLD linker where possible, but ONLY in CI applicable targets. + +# Configure how Docker container targets build. + +# If you want to customize these targets for a local build, then customize them in your: +# $CARGO_HOME/config.toml +# NOT in the project itself. +# These targets are ONLY the targets used by CI and inside docker builds. + +# DO NOT remove `"-C", "target-feature=+crt-static"` from the rustflags for these targets. + +# Should be the default to have fully static rust programs in CI +[target.x86_64-unknown-linux-musl] +linker = "clang" +rustflags = [ + "-C", "link-arg=-fuse-ld=/usr/bin/mold", + "-C", "target-feature=-crt-static" +] + +# Should be the default to have fully static rust programs in CI +[target.aarch64-unknown-linux-musl] +linker = "clang" +rustflags = [ + "-C", "link-arg=-fuse-ld=/usr/bin/mold", + "-C", "target-feature=-crt-static" +] + +[build] +rustflags = [] +rustdocflags = [ + "--enable-index-page", + "-Z", + "unstable-options", +] + +[profile.dev] +opt-level = 1 +debug = true +debug-assertions = true +overflow-checks = true +lto = false +panic = "unwind" +incremental = true +codegen-units = 256 + +[profile.release] +opt-level = 3 +debug = false +debug-assertions = false +overflow-checks = false +lto = "thin" +panic = "unwind" +incremental = false +codegen-units = 16 + +[profile.test] +opt-level = 3 +debug = true +lto = false +debug-assertions = true +incremental = true +codegen-units = 256 + +[profile.bench] +opt-level = 3 +debug = false +debug-assertions = false +overflow-checks = false +lto = "thin" +incremental = false +codegen-units = 16 + +[alias] +lint = "clippy --all-targets" +lintfix = "clippy --all-targets --fix --allow-dirty" +lint-vscode = "clippy --message-format=json-diagnostic-rendered-ansi --all-targets" + +docs = "doc --release --no-deps --document-private-items --bins --lib --examples" +# nightly docs build broken... when they are'nt we can enable these docs... --unit-graph --timings=html,json -Z unstable-options" +testunit = "nextest run --release --bins --lib --tests --benches --no-fail-fast -P ci" +testcov = "llvm-cov nextest --release --bins --lib --tests --benches --no-fail-fast -P ci" +testdocs = "test --doc --release" + +# Rust formatting, MUST be run with +nightly +fmtchk = "fmt -- --check -v --color=always" +fmtfix = "fmt -- -v" + +[term] +quiet = false # whether cargo output is quiet +verbose = false # whether cargo provides verbose output +color = "auto" # whether cargo colorizes output use `CARGO_TERM_COLOR="off"` to disable. +progress.when = "never" # whether cargo shows progress bar +progress.width = 80 # width of progress bar diff --git a/wasm/wasi-hermes-component-adapter/.config/nextest.toml b/wasm/wasi-hermes-component-adapter/.config/nextest.toml new file mode 100644 index 000000000..be3673830 --- /dev/null +++ b/wasm/wasi-hermes-component-adapter/.config/nextest.toml @@ -0,0 +1,49 @@ +# cspell: words scrollability testcase +[store] +# The directory under the workspace root at which nextest-related files are +# written. Profile-specific storage is currently written to dir/. +# dir = "target/nextest" + +[profile.default] +# Print out output for failing tests as soon as they fail, and also at the end +# of the run (for easy scrollability). +failure-output = "immediate-final" + +# Do not cancel the test run on the first failure. +fail-fast = true + +status-level = "all" +final-status-level = "all" + +[profile.ci] +# Print out output for failing tests as soon as they fail, and also at the end +# of the run (for easy scrollability). +failure-output = "immediate-final" +# Do not cancel the test run on the first failure. +fail-fast = false + +status-level = "all" +final-status-level = "all" + + +[profile.ci.junit] +# Output a JUnit report into the given file inside 'store.dir/'. +# If unspecified, JUnit is not written out. + +path = "junit.xml" + +# The name of the top-level "report" element in JUnit report. If aggregating +# reports across different test runs, it may be useful to provide separate names +# for each report. +report-name = "nextest" + +# Whether standard output and standard error for passing tests should be stored in the JUnit report. +# Output is stored in the and elements of the element. +store-success-output = true + +# Whether standard output and standard error for failing tests should be stored in the JUnit report. +# Output is stored in the and elements of the element. +# +# Note that if a description can be extracted from the output, it is always stored in the +# element. +store-failure-output = true diff --git a/wasm/wasi-hermes-component-adapter/.gitignore b/wasm/wasi-hermes-component-adapter/.gitignore new file mode 100644 index 000000000..aa901d56d --- /dev/null +++ b/wasm/wasi-hermes-component-adapter/.gitignore @@ -0,0 +1,15 @@ +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb \ No newline at end of file diff --git a/wasm/wasi-hermes-component-adapter/Cargo.toml b/wasm/wasi-hermes-component-adapter/Cargo.toml new file mode 100644 index 000000000..971d74609 --- /dev/null +++ b/wasm/wasi-hermes-component-adapter/Cargo.toml @@ -0,0 +1,90 @@ +#cspell: words Apisit Ritreungroj wasi bindgen cdylib + +[workspace] +resolver = "2" +members = [ + "./byte-array-literals" +] + +[workspace.package] +edition = "2021" +version = "0.0.1" +authors = [ + "Apisit Ritreungroj ", + "Steven Johnson " +] +homepage = "https://input-output-hk.github.io/hermes" +repository = "https://github.com/input-output-hk/hermes" +# inherited license from https://github.com/bytecodealliance/wasmtime +license = "Apache-2.0 WITH LLVM-exception" + +[workspace.lints.rust] +warnings = "deny" +missing_docs = "deny" +let_underscore_drop = "deny" +non_ascii_idents = "deny" +single_use_lifetimes = "deny" +trivial_casts = "deny" +trivial_numeric_casts = "deny" + +[workspace.lints.rustdoc] +broken_intra_doc_links = "deny" +invalid_codeblock_attributes = "deny" +invalid_html_tags = "deny" +invalid_rust_codeblocks = "deny" +bare_urls = "deny" +unescaped_backticks = "deny" + +[workspace.lints.clippy] +pedantic = "deny" +unwrap_used = "deny" +expect_used = "deny" +exit = "deny" +get_unwrap = "deny" +index_refutable_slice = "deny" +indexing_slicing = "deny" +match_on_vec_items = "deny" +match_wild_err_arm = "deny" +missing_panics_doc = "deny" +panic = "deny" +string_slice = "deny" +unchecked_duration_subtraction = "deny" +unreachable = "deny" +missing_docs_in_private_items = "deny" + +[workspace.dependencies] +wit-bindgen = { version = "0.16.0", default-features = false } +wasi = { version = "0.11.0", default-features = false } +byte-array-literals = { path = "./byte-array-literals", version = "0.0.1" } +wasm-encoder = "0.38.1" +object = { version = "0.32.1", default-features = false } + +[package] +name = "wasi-hermes-component-adapter" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +publish = false + +[lib] +crate-type = ["cdylib"] +name = "wasi_snapshot_hermes" + +[lints] +workspace = true + +[dependencies] +wasi = { workspace = true } +wit-bindgen = { workspace = true, features = ["macros"] } +byte-array-literals = { workspace = true } + +[build-dependencies] +wasm-encoder = { workspace = true } +object = { workspace = true, features = ["archive"] } + +[features] +default = ["reactor"] +reactor = [] +proxy = [] + diff --git a/wasm/wasi-hermes-component-adapter/Earthfile b/wasm/wasi-hermes-component-adapter/Earthfile new file mode 100644 index 000000000..196ff28ce --- /dev/null +++ b/wasm/wasi-hermes-component-adapter/Earthfile @@ -0,0 +1,38 @@ +VERSION 0.7 + +#cspell: words rustfmt wasi + +# Set up our target toolchains, and copy source files. +builder: + DO github.com/input-output-hk/catalyst-ci/earthly/rust:v2.6.0+SETUP + + COPY --dir .cargo .config src byte-array-literals . + COPY ..+wasi-src/wasi ../wasi + COPY Cargo.toml build.rs . + COPY clippy.toml deny.toml rustfmt.toml . + +# Run quality checks. +check: + FROM +builder + + RUN /scripts/std_checks.py + +# Test which runs check with all supported host tooling. Needs qemu or rosetta to run. +# Only used to validate tooling is working across host toolsets. +all-hosts-check: + BUILD --platform=linux/amd64 --platform=linux/arm64 +check + +# Build the service. +build: + FROM +builder + + RUN /scripts/std_build.py --disable_tests \ + --disable_benches \ + --build_flags="--target=wasm32-unknown-unknown" + + SAVE ARTIFACT target/wasm32-unknown-unknown/release/wasi_snapshot_hermes.wasm wasi-hermes-component-adapter.wasm + +# Test which runs check with all supported host tooling. Needs qemu or rosetta to run. +# Only used to validate tooling is working across host toolsets. +all-hosts-build: + BUILD --platform=linux/amd64 --platform=linux/arm64 +build diff --git a/wasm/wasi-hermes-component-adapter/LICENSE.txt b/wasm/wasi-hermes-component-adapter/LICENSE.txt new file mode 100644 index 000000000..f9d81955f --- /dev/null +++ b/wasm/wasi-hermes-component-adapter/LICENSE.txt @@ -0,0 +1,220 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/hermes/wasm/README.md b/wasm/wasi-hermes-component-adapter/README.md similarity index 71% rename from hermes/wasm/README.md rename to wasm/wasi-hermes-component-adapter/README.md index 6eb793826..c7449f3f5 100644 --- a/hermes/wasm/README.md +++ b/wasm/wasi-hermes-component-adapter/README.md @@ -10,13 +10,10 @@ located in `crates/wasi-component-adapter` and `crates/wasi`. ## Configuration -The Rust configuration file locates in `.cargo/config.toml`. -It already specified the build target to `wasm32-unknown-unknown`. - -To compile all the packages, simply run the command: +To compile, simply run the command: ```bash -cargo build --release +cargo build --target=wasm32-unknown-unknown --release ``` To build the WebAssembly binary output `.wasm`. diff --git a/hermes/wasm/crates/wasi-component-adapter/build.rs b/wasm/wasi-hermes-component-adapter/build.rs similarity index 92% rename from hermes/wasm/crates/wasi-component-adapter/build.rs rename to wasm/wasi-hermes-component-adapter/build.rs index 09f56df15..2df5f0a8c 100644 --- a/hermes/wasm/crates/wasi-component-adapter/build.rs +++ b/wasm/wasi-hermes-component-adapter/build.rs @@ -1,13 +1,15 @@ -use std::env; -use std::path::PathBuf; +//! Build script for the WASI component adapter. +use std::{env, path::PathBuf}; + +#[allow(clippy::unwrap_used)] fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let wasm = build_raw_intrinsics(); let archive = build_archive(&wasm); - std::fs::write(out_dir.join("libwasm-raw-intrinsics.a"), &archive).unwrap(); + std::fs::write(out_dir.join("libwasm-raw-intrinsics.a"), archive).unwrap(); println!("cargo:rustc-link-lib=static=wasm-raw-intrinsics"); println!( "cargo:rustc-link-search=native={}", @@ -67,9 +69,12 @@ fn main() { /// /// The main trickiness here is getting the `reloc.CODE` and `linking` sections /// right. +#[allow(clippy::too_many_lines)] fn build_raw_intrinsics() -> Vec { - use wasm_encoder::Instruction::*; - use wasm_encoder::*; + use wasm_encoder::{ + ConstExpr, CustomSection, Encode, FunctionSection, GlobalSection, GlobalType, Instruction, + Module, RawSection, SectionId, TypeSection, ValType, + }; let mut module = Module::new(); @@ -126,12 +131,12 @@ fn build_raw_intrinsics() -> Vec { let mut body = Vec::new(); 0u32.encode(&mut body); // no locals if instruction == global_set { - LocalGet(0).encode(&mut body); + Instruction::LocalGet(0).encode(&mut body); } let global_offset = body.len() + 1; // global.get $global ;; but with maximal encoding of $global body.extend_from_slice(&[instruction, 0x80u8 + global, 0x80, 0x80, 0x80, 0x00]); - End.encode(&mut body); + Instruction::End.encode(&mut body); body.len().encode(code); // length of the function let offset = code.len() + global_offset; code.extend_from_slice(&body); // the function itself @@ -148,8 +153,8 @@ fn build_raw_intrinsics() -> Vec { data: &code, }); - // Here the linking section is constructed. There is one symbol for each function and global. The injected - // globals here are referenced in the relocations below. + // Here the linking section is constructed. There is one symbol for each function and + // global. The injected globals here are referenced in the relocations below. // // More information about this format is at // https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md @@ -236,6 +241,9 @@ fn build_raw_intrinsics() -> Vec { /// /// Like above this is still tricky, mainly around the production of the symbol /// table. +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] +#[allow(clippy::cast_possible_truncation)] fn build_archive(wasm: &[u8]) -> Vec { use object::{bytes_of, endian::BigEndian, U32Bytes}; @@ -246,8 +254,8 @@ fn build_archive(wasm: &[u8]) -> Vec { // that looks like: // // * a big-endian 32-bit integer for the number of symbols - // * N big-endian 32-bit integers for the offset to the object file, within - // the entire archive, for which object has the symbol + // * N big-endian 32-bit integers for the offset to the object file, within the entire + // archive, for which object has the symbol // * N nul-delimited strings for each symbol // // Here we're building an archive with just a few symbols so it's a bit @@ -264,10 +272,10 @@ fn build_archive(wasm: &[u8]) -> Vec { let mut symbol_table = Vec::new(); symbol_table.extend_from_slice(bytes_of(&U32Bytes::new(BigEndian, syms.len() as u32))); - for _ in syms.iter() { + for _ in &syms { symbol_table.extend_from_slice(bytes_of(&U32Bytes::new(BigEndian, 0))); } - for s in syms.iter() { + for s in &syms { symbol_table.extend_from_slice(&std::ffi::CString::new(*s).unwrap().into_bytes_with_nul()); } @@ -311,6 +319,6 @@ fn build_archive(wasm: &[u8]) -> Vec { size: format!("{:<10}", wasm.len()).as_bytes().try_into().unwrap(), terminator: object::archive::TERMINATOR, })); - archive.extend_from_slice(&wasm); + archive.extend_from_slice(wasm); archive } diff --git a/hermes/wasm/crates/wasi-component-adapter/byte-array-literals/Cargo.toml b/wasm/wasi-hermes-component-adapter/byte-array-literals/Cargo.toml similarity index 75% rename from hermes/wasm/crates/wasi-component-adapter/byte-array-literals/Cargo.toml rename to wasm/wasi-hermes-component-adapter/byte-array-literals/Cargo.toml index 5be0dd938..0757a0619 100644 --- a/hermes/wasm/crates/wasi-component-adapter/byte-array-literals/Cargo.toml +++ b/wasm/wasi-hermes-component-adapter/byte-array-literals/Cargo.toml @@ -3,9 +3,11 @@ name = "byte-array-literals" version.workspace = true authors.workspace = true edition.workspace = true +license.workspace = true publish = false +[lints] +workspace = true + [lib] proc-macro = true -test = false -doctest = false diff --git a/hermes/wasm/crates/wasi-component-adapter/byte-array-literals/README.md b/wasm/wasi-hermes-component-adapter/byte-array-literals/README.md similarity index 89% rename from hermes/wasm/crates/wasi-component-adapter/byte-array-literals/README.md rename to wasm/wasi-hermes-component-adapter/byte-array-literals/README.md index 156854940..b8af63e90 100644 --- a/hermes/wasm/crates/wasi-component-adapter/byte-array-literals/README.md +++ b/wasm/wasi-hermes-component-adapter/byte-array-literals/README.md @@ -8,7 +8,8 @@ wasm32-unknown-unknown) cannot contain any data sections. The answer that @sunfishcode discovered is that these string literals, if represented as an array of u8 literals, these will somehow not end up in the data section, at least when compiled with opt-level='s' on today's rustc -(1.69.0). So, this crate exists to transform these literals using a proc +(1.69.0). +So, this crate exists to transform these literals using a proc macro. It is very possible this cheat code will abruptly stop working in some future diff --git a/wasm/wasi-hermes-component-adapter/byte-array-literals/src/lib.rs b/wasm/wasi-hermes-component-adapter/byte-array-literals/src/lib.rs new file mode 100644 index 000000000..0227317f2 --- /dev/null +++ b/wasm/wasi-hermes-component-adapter/byte-array-literals/src/lib.rs @@ -0,0 +1,108 @@ +//! Converts `str` literals into byte arrays. + +use proc_macro::{Delimiter, Group, Literal, Punct, Spacing, TokenStream, TokenTree}; + +/// Expand a `str` literal into a byte array. +#[proc_macro] +pub fn str(input: TokenStream) -> TokenStream { + let rv = convert_str(input); + + vec![TokenTree::Group(Group::new( + Delimiter::Bracket, + rv.into_iter().collect(), + ))] + .into_iter() + .collect() +} + +/// The same as `str` but appends a `'\n'`. +#[proc_macro] +pub fn str_nl(input: TokenStream) -> TokenStream { + let mut rv = convert_str(input); + + rv.push(TokenTree::Literal(Literal::u8_suffixed(b'\n'))); + + vec![TokenTree::Group(Group::new( + Delimiter::Bracket, + rv.into_iter().collect(), + ))] + .into_iter() + .collect() +} + +#[allow(clippy::panic)] +#[allow(clippy::missing_docs_in_private_items)] +#[allow(clippy::single_match_else)] +fn convert_str(input: TokenStream) -> Vec { + let mut it = input.into_iter(); + + let mut tokens = Vec::new(); + match it.next() { + Some(TokenTree::Literal(l)) => { + for b in to_string(&l).into_bytes() { + tokens.push(TokenTree::Literal(Literal::u8_suffixed(b))); + tokens.push(TokenTree::Punct(Punct::new(',', Spacing::Alone))); + } + }, + _ => panic!(), + } + + assert!(it.next().is_none()); + tokens +} + +#[allow(clippy::unwrap_used)] +#[allow(clippy::cast_possible_truncation)] +#[allow(clippy::panic)] +#[allow(clippy::missing_docs_in_private_items)] +fn to_string(lit: &Literal) -> String { + let formatted = lit.to_string(); + + let mut it = formatted.chars(); + assert_eq!(it.next(), Some('"')); + + let mut rv = String::new(); + loop { + match it.next() { + Some('"') => { + match it.next() { + Some(_) => panic!(), + None => break, + } + }, + Some('\\') => { + match it.next() { + Some('x') => { + let hi = it.next().unwrap().to_digit(16).unwrap(); + let lo = it.next().unwrap().to_digit(16).unwrap(); + let v = (hi << 16) | lo; + rv.push(v as u8 as char); + }, + Some('u') => { + assert_eq!(it.next(), Some('{')); + let mut c = it.next().unwrap(); + let mut ch = 0; + while let Some(v) = c.to_digit(16) { + ch *= 16; + ch |= v; + c = it.next().unwrap(); + } + assert_eq!(c, '}'); + rv.push(::std::char::from_u32(ch).unwrap()); + }, + Some('0') => rv.push('\0'), + Some('\\') => rv.push('\\'), + Some('\"') => rv.push('\"'), + Some('r') => rv.push('\r'), + Some('n') => rv.push('\n'), + Some('t') => rv.push('\t'), + Some(_) | None => panic!(), + } + }, + Some(c) => rv.push(c), + None => panic!(), + } + } + + rv +} diff --git a/wasm/wasi-hermes-component-adapter/clippy.toml b/wasm/wasi-hermes-component-adapter/clippy.toml new file mode 100644 index 000000000..6933b8164 --- /dev/null +++ b/wasm/wasi-hermes-component-adapter/clippy.toml @@ -0,0 +1 @@ +allow-expect-in-tests = true diff --git a/hermes/wasm/deny.toml b/wasm/wasi-hermes-component-adapter/deny.toml similarity index 97% rename from hermes/wasm/deny.toml rename to wasm/wasi-hermes-component-adapter/deny.toml index 430b40014..926f2ef43 100644 --- a/hermes/wasm/deny.toml +++ b/wasm/wasi-hermes-component-adapter/deny.toml @@ -97,16 +97,19 @@ ignore = [ # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html [licenses] # The lint level for crates which do not have a detectable license -unlicensed = "warn" +unlicensed = "deny" +# Don't warn if a listed license isn't found +unused-allowed-license="allow" # List of explicitly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. allow = [ "MIT", "Apache-2.0", - "Apache-2.0 WITH LLVM-exception", "Unicode-DFS-2016", - "BSD-3-Clause" + "BSD-3-Clause", + "BlueOak-1.0.0", + "Apache-2.0 WITH LLVM-exception" ] # List of explicitly disallowed licenses # See https://spdx.org/licenses/ for list of possible licenses @@ -264,7 +267,11 @@ unknown-git = "deny" # if not specified. If it is specified but empty, no registries are allowed. allow-registry = ["https://github.com/rust-lang/crates.io-index"] # List of URLs for allowed Git repositories -allow-git = [] +allow-git = [ + # Remove this once upstream changes are merged. + # issue: https://github.com/input-output-hk/hermes/issues/63 + "https://github.com/input-output-hk/catalyst-pallas" +] [sources.allow-org] # 1 or more github.com organizations to allow git sources for @@ -272,4 +279,4 @@ allow-git = [] # 1 or more gitlab.com organizations to allow git sources for #gitlab = [""] # 1 or more bitbucket.org organizations to allow git sources for -#bitbucket = [""] +#bitbucket = [""] \ No newline at end of file diff --git a/hermes/wasm/rust-toolchain.toml b/wasm/wasi-hermes-component-adapter/rust-toolchain.toml similarity index 66% rename from hermes/wasm/rust-toolchain.toml rename to wasm/wasi-hermes-component-adapter/rust-toolchain.toml index 2ecf59782..9606ca4cb 100644 --- a/hermes/wasm/rust-toolchain.toml +++ b/wasm/wasi-hermes-component-adapter/rust-toolchain.toml @@ -1,5 +1,4 @@ [toolchain] -channel = "1.73.0" +channel = "1.75.0" profile = "default" -components = [] targets = ["wasm32-unknown-unknown"] diff --git a/hermes/wasm/rustfmt.toml b/wasm/wasi-hermes-component-adapter/rustfmt.toml similarity index 100% rename from hermes/wasm/rustfmt.toml rename to wasm/wasi-hermes-component-adapter/rustfmt.toml diff --git a/hermes/wasm/crates/wasi-component-adapter/src/descriptors.rs b/wasm/wasi-hermes-component-adapter/src/descriptors.rs similarity index 84% rename from hermes/wasm/crates/wasi-component-adapter/src/descriptors.rs rename to wasm/wasi-hermes-component-adapter/src/descriptors.rs index 23e0a45d4..9bc97c2c4 100644 --- a/hermes/wasm/crates/wasi-component-adapter/src/descriptors.rs +++ b/wasm/wasi-hermes-component-adapter/src/descriptors.rs @@ -1,18 +1,27 @@ -use crate::bindings::wasi::cli::{stderr, stdin, stdout}; -use crate::bindings::wasi::io::streams::{InputStream, OutputStream}; -use crate::{BlockingMode, BumpArena, ImportAlloc, TrappingUnwrap, WasmStr}; -use core::cell::{Cell, OnceCell, UnsafeCell}; -use core::mem::MaybeUninit; +use core::{ + cell::{Cell, OnceCell, UnsafeCell}, + mem::MaybeUninit, +}; + use wasi::{Errno, Fd}; #[cfg(not(feature = "proxy"))] use crate::bindings::wasi::filesystem::types as filesystem; #[cfg(not(feature = "proxy"))] use crate::File; - +use crate::{ + bindings::wasi::{ + cli::{stderr, stdin, stdout}, + io::streams::{InputStream, OutputStream}, + }, + BlockingMode, BumpArena, ImportAlloc, TrappingUnwrap, WasmStr, +}; + +#[allow(clippy::missing_docs_in_private_items)] pub const MAX_DESCRIPTORS: usize = 128; #[repr(C)] +#[allow(clippy::missing_docs_in_private_items)] pub enum Descriptor { /// A closed descriptor, holding a reference to the previous closed /// descriptor to support reusing them. @@ -40,13 +49,14 @@ pub struct Streams { impl Streams { /// Return the input stream, initializing it on the fly if needed. + #[allow(clippy::single_match_else)] pub fn get_read_stream(&self) -> Result<&InputStream, Errno> { match self.input.get() { Some(wasi_stream) => Ok(wasi_stream), None => { let input = match &self.type_ { - // For directories, preview 1 behavior was to return ERRNO_BADF on attempts to read - // or write. + // For directories, preview 1 behavior was to return ERRNO_BADF on attempts to + // read or write. #[cfg(not(feature = "proxy"))] StreamType::File(File { descriptor_type: filesystem::DescriptorType::Directory, @@ -55,26 +65,24 @@ impl Streams { // For files, we may have adjusted the position for seeking, so // create a new stream. #[cfg(not(feature = "proxy"))] - StreamType::File(file) => { - let input = file.fd.read_via_stream(file.position.get())?; - input - } + StreamType::File(file) => file.fd.read_via_stream(file.position.get())?, _ => return Err(wasi::ERRNO_BADF), }; self.input.set(input).trapping_unwrap(); Ok(self.input.get().trapping_unwrap()) - } + }, } } /// Return the output stream, initializing it on the fly if needed. + #[allow(clippy::single_match_else)] pub fn get_write_stream(&self) -> Result<&OutputStream, Errno> { match self.output.get() { Some(wasi_stream) => Ok(wasi_stream), None => { let output = match &self.type_ { - // For directories, preview 1 behavior was to return ERRNO_BADF on attempts to read - // or write. + // For directories, preview 1 behavior was to return ERRNO_BADF on attempts to + // read or write. #[cfg(not(feature = "proxy"))] StreamType::File(File { descriptor_type: filesystem::DescriptorType::Directory, @@ -84,22 +92,22 @@ impl Streams { // create a new stream. #[cfg(not(feature = "proxy"))] StreamType::File(file) => { - let output = if file.append { + if file.append { file.fd.append_via_stream()? } else { file.fd.write_via_stream(file.position.get())? - }; - output - } + } + }, _ => return Err(wasi::ERRNO_BADF), }; self.output.set(output).trapping_unwrap(); Ok(self.output.get().trapping_unwrap()) - } + }, } } } +#[allow(clippy::missing_docs_in_private_items)] pub enum StreamType { /// Streams for implementing stdio. Stdio(Stdio), @@ -109,6 +117,7 @@ pub enum StreamType { File(File), } +#[allow(clippy::missing_docs_in_private_items)] pub enum Stdio { Stdin, Stdout, @@ -116,27 +125,17 @@ pub enum Stdio { } impl Stdio { + #[allow(clippy::missing_docs_in_private_items, clippy::unused_self)] pub fn filetype(&self) -> wasi::Filetype { - #[cfg(not(feature = "proxy"))] - let is_terminal = { - use crate::bindings::wasi::cli; - match self { - Stdio::Stdin => cli::terminal_stdin::get_terminal_stdin().is_some(), - Stdio::Stdout => cli::terminal_stdout::get_terminal_stdout().is_some(), - Stdio::Stderr => cli::terminal_stderr::get_terminal_stderr().is_some(), - } - }; - #[cfg(feature = "proxy")] - let is_terminal = false; - if is_terminal { - wasi::FILETYPE_CHARACTER_DEVICE - } else { - wasi::FILETYPE_UNKNOWN - } + // `self` is unused in this simplified version of the function. + // We retain it for internal API compatibility. + // wasi::FILETYPE_CHARACTER_DEVICE + wasi::FILETYPE_UNKNOWN } } #[repr(C)] +#[allow(clippy::missing_docs_in_private_items)] pub struct Descriptors { /// Storage of mapping from preview1 file descriptors to preview2 file /// descriptors. @@ -152,7 +151,9 @@ pub struct Descriptors { preopens: Cell>, } +#[allow(clippy::missing_docs_in_private_items)] impl Descriptors { + #[allow(clippy::items_after_statements)] pub fn new(import_alloc: &ImportAlloc, arena: &BumpArena) -> Self { let d = Descriptors { table: UnsafeCell::new(MaybeUninit::uninit()), @@ -164,7 +165,7 @@ impl Descriptors { fn new_once(val: T) -> OnceCell { let cell = OnceCell::new(); - let _ = cell.set(val); + let _unused = cell.set(val); cell } @@ -193,6 +194,9 @@ impl Descriptors { } #[cfg(not(feature = "proxy"))] + #[allow(trivial_casts)] + #[allow(clippy::borrow_as_ptr)] + #[allow(clippy::semicolon_if_nothing_returned)] fn open_preopens(&self, import_alloc: &ImportAlloc, arena: &BumpArena) { #[link(wasm_import_module = "wasi:filesystem/preopens@0.2.0-rc-2023-11-10")] #[allow(improper_ctypes)] // FIXME(bytecodealliance/wit-bindgen#684) @@ -236,6 +240,8 @@ impl Descriptors { self.preopens.set(Some(preopens)); } + #[allow(clippy::indexing_slicing)] + #[allow(clippy::unnecessary_fallible_conversions)] fn push(&self, desc: Descriptor) -> Result { unsafe { let table = (*self.table.get()).as_mut_ptr(); @@ -249,6 +255,7 @@ impl Descriptors { } } + #[allow(clippy::unnecessary_fallible_conversions)] fn table(&self) -> &[Descriptor] { unsafe { std::slice::from_raw_parts( @@ -258,6 +265,7 @@ impl Descriptors { } } + #[allow(clippy::unnecessary_fallible_conversions)] fn table_mut(&mut self) -> &mut [Descriptor] { unsafe { std::slice::from_raw_parts_mut( @@ -267,6 +275,8 @@ impl Descriptors { } } + #[allow(clippy::unreachable)] + #[allow(clippy::single_match_else)] pub fn open(&mut self, d: Descriptor) -> Result { match self.closed { // No closed descriptors: expand table @@ -283,7 +293,7 @@ impl Descriptors { // Point closed to the following item self.closed = next_closed; Ok(freelist_head) - } + }, } } @@ -308,13 +318,15 @@ impl Descriptors { } // Internal: close a fd, returning the descriptor. + #[allow(clippy::single_match)] fn close_(&mut self, fd: Fd) -> Result { // Throw an error if closing an fd which is already closed match self.get(fd)? { Descriptor::Closed(_) => Err(wasi::ERRNO_BADF)?, - _ => {} + _ => {}, } - // Mutate the descriptor to be closed, and push the closed fd onto the head of the linked list: + // Mutate the descriptor to be closed, and push the closed fd onto the head of the linked + // list: let last_closed = self.closed; let prev = std::mem::replace(self.get_mut(fd)?, Descriptor::Closed(last_closed)); self.closed = Some(fd); @@ -336,6 +348,7 @@ impl Descriptors { } // Implementation of fd_renumber + #[allow(clippy::cast_lossless)] pub fn renumber(&mut self, from_fd: Fd, to_fd: Fd) -> Result<(), Errno> { // First, ensure from_fd is in bounds: let _ = self.get(from_fd)?; @@ -354,9 +367,7 @@ impl Descriptors { // A bunch of helper functions implemented in terms of the above pub functions: pub fn get_stream_with_error_mut( - &mut self, - fd: Fd, - error: Errno, + &mut self, fd: Fd, error: Errno, ) -> Result<&mut Streams, Errno> { match self.get_mut(fd)? { Descriptor::Streams(streams) => Ok(streams), @@ -365,6 +376,7 @@ impl Descriptors { } #[cfg(not(feature = "proxy"))] + #[allow(clippy::match_same_arms)] pub fn get_file_with_error(&self, fd: Fd, error: Errno) -> Result<&File, Errno> { match self.get(fd)? { Descriptor::Streams(Streams { @@ -419,23 +431,24 @@ impl Descriptors { self.get_stream_with_error_mut(fd, wasi::ERRNO_SPIPE) } - pub fn get_read_stream(&self, fd: Fd) -> Result<&InputStream, Errno> { - match self.get(fd)? { - Descriptor::Streams(streams) => streams.get_read_stream(), - Descriptor::Closed(_) | Descriptor::Bad => Err(wasi::ERRNO_BADF), - } - } - - pub fn get_write_stream(&self, fd: Fd) -> Result<&OutputStream, Errno> { - match self.get(fd)? { - Descriptor::Streams(streams) => streams.get_write_stream(), - Descriptor::Closed(_) | Descriptor::Bad => Err(wasi::ERRNO_BADF), - } - } + // pub fn get_read_stream(&self, fd: Fd) -> Result<&InputStream, Errno> { + // match self.get(fd)? { + // Descriptor::Streams(streams) => streams.get_read_stream(), + // Descriptor::Closed(_) | Descriptor::Bad => Err(wasi::ERRNO_BADF), + // } + // } + // + // pub fn get_write_stream(&self, fd: Fd) -> Result<&OutputStream, Errno> { + // match self.get(fd)? { + // Descriptor::Streams(streams) => streams.get_write_stream(), + // Descriptor::Closed(_) | Descriptor::Bad => Err(wasi::ERRNO_BADF), + // } + // } } #[cfg(not(feature = "proxy"))] #[repr(C)] +#[allow(clippy::missing_docs_in_private_items)] pub struct Preopen { /// This is `MaybeUninit` because we take ownership of the `Descriptor` to /// put it in our own table. @@ -445,6 +458,7 @@ pub struct Preopen { #[cfg(not(feature = "proxy"))] #[repr(C)] +#[allow(clippy::missing_docs_in_private_items)] pub struct PreopenList { pub base: *const Preopen, pub len: usize, diff --git a/hermes/wasm/crates/wasi-component-adapter/src/lib.rs b/wasm/wasi-hermes-component-adapter/src/lib.rs similarity index 79% rename from hermes/wasm/crates/wasi-component-adapter/src/lib.rs rename to wasm/wasi-hermes-component-adapter/src/lib.rs index 8ef496d56..eb1acc5db 100644 --- a/hermes/wasm/crates/wasi-component-adapter/src/lib.rs +++ b/wasm/wasi-hermes-component-adapter/src/lib.rs @@ -1,3 +1,5 @@ +//! WASI component adapter crate. + // The proxy world has no filesystem which most of this file is concerned with, // so disable many warnings to avoid having to contort code too much for the // proxy world. @@ -12,56 +14,146 @@ ) )] -use crate::bindings::wasi::clocks::{monotonic_clock, wall_clock}; -use crate::bindings::wasi::io::poll; -use crate::bindings::wasi::io::streams; -use crate::bindings::wasi::random::random; -use core::cell::OnceCell; -use core::cell::{Cell, RefCell, RefMut, UnsafeCell}; -use core::cmp::min; -use core::ffi::c_void; -use core::hint::black_box; -use core::mem::{self, align_of, forget, size_of, ManuallyDrop, MaybeUninit}; -use core::ops::{Deref, DerefMut}; -use core::ptr::{self, null_mut}; -use core::slice; -use poll::Pollable; -use wasi::*; +use core::{ + cell::{Cell, OnceCell, RefCell, RefMut, UnsafeCell}, + cmp::min, + // ffi::c_void, + hint::black_box, + mem::{self, /* align_of, */ forget, size_of, ManuallyDrop, MaybeUninit}, + ops::{Deref, DerefMut}, + ptr::{self, null_mut}, + slice, +}; + +// use poll::Pollable; // Hermes does not support `poll` +use wasi::{ + Advice, + Ciovec, + Clockid, + Dircookie, + Dirent, + Errno, // Event, EventFdReadwrite, + // Exitcode, + Fd, + Fdflags, + Fdstat, + Filedelta, + Filesize, + Filestat, + Fstflags, + Iovec, + Lookupflags, + Oflags, + Prestat, + PrestatDir, + PrestatU, + Riflags, + Rights, + Roflags, + Sdflags, + Siflags, + Signal, + Size, + // Subscription, + Timestamp, + Whence, + ADVICE_DONTNEED, + ADVICE_NOREUSE, + ADVICE_NORMAL, + ADVICE_RANDOM, + ADVICE_SEQUENTIAL, + ADVICE_WILLNEED, + CLOCKID_MONOTONIC, + CLOCKID_REALTIME, + ERRNO_ACCES, + ERRNO_AGAIN, + ERRNO_ALREADY, + ERRNO_BADF, + ERRNO_BUSY, + ERRNO_DEADLK, + ERRNO_DQUOT, + ERRNO_EXIST, + ERRNO_FBIG, + ERRNO_ILSEQ, + ERRNO_INPROGRESS, + ERRNO_INTR, + ERRNO_INVAL, + ERRNO_IO, + ERRNO_ISDIR, + ERRNO_LOOP, + ERRNO_MLINK, + ERRNO_MSGSIZE, + ERRNO_NAMETOOLONG, + ERRNO_NODEV, + ERRNO_NOENT, + ERRNO_NOLCK, + ERRNO_NOMEM, + ERRNO_NOSPC, + ERRNO_NOTDIR, + ERRNO_NOTEMPTY, + ERRNO_NOTRECOVERABLE, + ERRNO_NOTSUP, + ERRNO_NOTTY, + ERRNO_NXIO, + ERRNO_OVERFLOW, + ERRNO_PERM, + ERRNO_PIPE, + ERRNO_ROFS, + ERRNO_SPIPE, + ERRNO_SUCCESS, + ERRNO_TXTBSY, + ERRNO_XDEV, + // EVENTRWFLAGS_FD_READWRITE_HANGUP, + FDFLAGS_APPEND, + FDFLAGS_DSYNC, + FDFLAGS_NONBLOCK, + FDFLAGS_RSYNC, + FDFLAGS_SYNC, + FILETYPE_BLOCK_DEVICE, + FILETYPE_CHARACTER_DEVICE, + FILETYPE_DIRECTORY, + FILETYPE_REGULAR_FILE, + FILETYPE_SYMBOLIC_LINK, + FILETYPE_UNKNOWN, + FSTFLAGS_ATIM, + FSTFLAGS_ATIM_NOW, + FSTFLAGS_MTIM, + FSTFLAGS_MTIM_NOW, + LOOKUPFLAGS_SYMLINK_FOLLOW, + OFLAGS_CREAT, + OFLAGS_DIRECTORY, + OFLAGS_EXCL, + OFLAGS_TRUNC, + RIGHTS_FD_READ, + RIGHTS_FD_WRITE, + // SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME, + WHENCE_CUR, + WHENCE_END, + WHENCE_SET, +}; #[cfg(not(feature = "proxy"))] use crate::bindings::wasi::filesystem::types as filesystem; +use crate::bindings::wasi::{ + clocks::{monotonic_clock, wall_clock}, + io::streams, + random::random, +}; -#[cfg(any( - all(feature = "command", feature = "reactor"), - all(feature = "reactor", feature = "proxy"), - all(feature = "command", feature = "proxy"), -))] -compile_error!( - "only one of the `command`, `reactor` or `proxy` features may be selected at a time" -); +#[cfg(all(feature = "reactor", feature = "proxy"))] +compile_error!("only one of the `reactor` or `proxy` features may be selected at a time"); #[macro_use] mod macros; +#[allow(clippy::missing_docs_in_private_items)] mod descriptors; use crate::descriptors::{Descriptor, Descriptors, StreamType, Streams}; +#[allow(missing_docs)] +#[allow(clippy::doc_markdown)] +#[allow(clippy::ignored_unit_patterns)] pub mod bindings { - #[cfg(feature = "command")] - wit_bindgen::generate!({ - path: "../wasi/wit", - world: "wasi:cli/command", - std_feature, - raw_strings, - // Automatically generated bindings for these functions will allocate - // Vecs, which in turn pulls in the panic machinery from std, which - // creates vtables that end up in the wasm elem section, which we - // can't support in these special core-wasm adapters. - // Instead, we manually define the bindings for these functions in - // terms of raw pointers. - skip: ["run", "get-environment", "poll"], - }); - #[cfg(feature = "reactor")] wit_bindgen::generate!({ path: "../wasi/wit", @@ -79,7 +171,7 @@ pub mod bindings { #[cfg(feature = "proxy")] wit_bindgen::generate!({ - path: "./crates/wasi/wit", + path: "./wasi/wit", inline: r#" package wasmtime:adapter; @@ -98,17 +190,6 @@ pub mod bindings { }); } -#[export_name = "wasi:cli/run@0.2.0-rc-2023-12-05#run"] -#[cfg(feature = "command")] -pub unsafe extern "C" fn run() -> u32 { - #[link(wasm_import_module = "__main_module__")] - extern "C" { - fn _start(); - } - _start(); - 0 -} - #[cfg(feature = "proxy")] macro_rules! cfg_filesystem_available { ($($t:tt)*) => { @@ -116,6 +197,7 @@ macro_rules! cfg_filesystem_available { }; } #[cfg(not(feature = "proxy"))] +#[allow(clippy::missing_docs_in_private_items)] macro_rules! cfg_filesystem_available { ($($t:tt)*) => ($($t)*); } @@ -123,11 +205,14 @@ macro_rules! cfg_filesystem_available { // The unwrap/expect methods in std pull panic when they fail, which pulls // in unwinding machinery that we can't use in the adapter. Instead, use this // extension trait to get postfixed upwrap on Option and Result. +#[allow(clippy::missing_docs_in_private_items)] trait TrappingUnwrap { fn trapping_unwrap(self) -> T; } impl TrappingUnwrap for Option { + #[allow(clippy::unreachable)] + #[allow(clippy::single_match_else)] fn trapping_unwrap(self) -> T { match self { Some(t) => t, @@ -137,6 +222,8 @@ impl TrappingUnwrap for Option { } impl TrappingUnwrap for Result { + #[allow(clippy::unreachable)] + #[allow(clippy::single_match_else)] fn trapping_unwrap(self) -> T { match self { Ok(t) => t, @@ -152,6 +239,7 @@ impl TrappingUnwrap for Result { /// from WASI Preview 1 to Preview 2. It will use this function to reserve /// descriptors for its own use, valid only for use with libc functions. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn adapter_open_badfd(fd: *mut u32) -> Errno { State::with(|state| { *fd = state.descriptors_mut().open(Descriptor::Bad)?; @@ -161,24 +249,27 @@ pub unsafe extern "C" fn adapter_open_badfd(fd: *mut u32) -> Errno { /// Close a descriptor previously opened using `adapter_open_badfd`. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn adapter_close_badfd(fd: u32) -> Errno { State::with(|state| state.descriptors_mut().close(fd)) } #[no_mangle] +#[allow(missing_docs)] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn reset_adapter_state() { let state = get_state_ptr(); if !state.is_null() { - State::init(state) + State::init(state); } } #[no_mangle] +#[allow(missing_docs)] +#[allow(clippy::unreachable)] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn cabi_import_realloc( - old_ptr: *mut u8, - old_size: usize, - align: usize, - new_size: usize, + old_ptr: *mut u8, old_size: usize, align: usize, new_size: usize, ) -> *mut u8 { if !old_ptr.is_null() || old_size != 0 { unreachable!(); @@ -193,18 +284,23 @@ pub unsafe extern "C" fn cabi_import_realloc( /// Bump-allocated memory arena. This is a singleton - the /// memory will be sized according to `bump_arena_size()`. +#[allow(clippy::missing_docs_in_private_items)] pub struct BumpArena { data: MaybeUninit<[u8; bump_arena_size()]>, position: Cell, } impl BumpArena { + #[allow(clippy::missing_docs_in_private_items)] fn new() -> Self { BumpArena { data: MaybeUninit::uninit(), position: Cell::new(0), } } + + #[allow(clippy::unreachable)] + #[allow(clippy::missing_docs_in_private_items)] fn alloc(&self, align: usize, size: usize) -> *mut u8 { let start = self.data.as_ptr() as usize; let next = start + self.position.get(); @@ -217,6 +313,7 @@ impl BumpArena { alloc as *mut u8 } } +#[allow(clippy::missing_docs_in_private_items)] fn align_to(ptr: usize, align: usize) -> usize { (ptr + (align - 1)) & !(align - 1) } @@ -226,6 +323,8 @@ fn align_to(ptr: usize, align: usize) -> usize { // because we can't use RefCell to borrow() the variants of the enum - only // Cell provides mutability without pulling in panic machinery - so it would // make the accessors a lot more awkward to write. +#[allow(missing_docs)] +#[allow(clippy::missing_docs_in_private_items)] pub struct ImportAlloc { // When not-null, allocator should use this buffer/len pair at most once // to satisfy allocations. @@ -236,6 +335,7 @@ pub struct ImportAlloc { } impl ImportAlloc { + #[allow(clippy::missing_docs_in_private_items)] fn new() -> Self { ImportAlloc { buffer: Cell::new(std::ptr::null_mut()), @@ -247,6 +347,7 @@ impl ImportAlloc { /// Expect at most one import allocation during execution of the provided closure. /// Use the provided buffer to satisfy that import allocation. The user is responsible /// for making sure allocated imports are not used beyond the lifetime of the buffer. + #[allow(clippy::unreachable)] fn with_buffer(&self, buffer: *mut u8, len: usize, f: impl FnOnce() -> T) -> T { if self.arena.get().is_some() { unreachable!("arena mode") @@ -262,8 +363,10 @@ impl ImportAlloc { } /// Permit many import allocations during execution of the provided closure. - /// Use the provided BumpArena to satisfry those allocations. The user is responsible - /// for making sure allocated imports are not used beyond the lifetime of the arena. + /// Use the provided `BumpArena` to satisfry those allocations. The user is + /// responsible for making sure allocated imports are not used beyond the lifetime + /// of the arena. + #[allow(clippy::unreachable)] fn with_arena(&self, arena: &BumpArena, f: impl FnOnce() -> T) -> T { if !self.buffer.get().is_null() { unreachable!("buffer mode") @@ -280,7 +383,8 @@ impl ImportAlloc { r } - /// To be used by cabi_import_realloc only! + /// To be used by `cabi_import_realloc` only! + #[allow(clippy::unreachable)] fn alloc(&self, align: usize, size: usize) -> *mut u8 { if let Some(arena) = self.arena.get() { arena.alloc(align, size) @@ -310,11 +414,10 @@ impl ImportAlloc { /// (ish) limit. That's just an implementation limit though which can be lifted /// by dynamically calling the main module's allocator as necessary for more data. #[no_mangle] +#[allow(clippy::missing_safety_doc)] +#[allow(clippy::unreachable)] pub unsafe extern "C" fn cabi_export_realloc( - old_ptr: *mut u8, - old_size: usize, - align: usize, - new_size: usize, + old_ptr: *mut u8, old_size: usize, align: usize, new_size: usize, ) -> *mut u8 { if !old_ptr.is_null() || old_size != 0 { unreachable!(); @@ -330,6 +433,7 @@ pub unsafe extern "C" fn cabi_export_realloc( /// Read command-line argument data. /// The size of the array should match that returned by `args_sizes_get` #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn args_get(mut argv: *mut *mut u8, mut argv_buf: *mut u8) -> Errno { State::with(|state| { #[cfg(not(feature = "proxy"))] @@ -355,6 +459,8 @@ pub unsafe extern "C" fn args_get(mut argv: *mut *mut u8, mut argv_buf: *mut u8) /// Return command-line argument data sizes. #[no_mangle] +#[allow(clippy::similar_names)] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn args_sizes_get(argc: *mut Size, argv_buf_size: *mut Size) -> Errno { State::with(|state| { #[cfg(feature = "proxy")] @@ -377,6 +483,7 @@ pub unsafe extern "C" fn args_sizes_get(argc: *mut Size, argv_buf_size: *mut Siz /// Read environment variable data. /// The sizes of the buffers should match that returned by `environ_sizes_get`. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn environ_get(environ: *mut *mut u8, environ_buf: *mut u8) -> Errno { State::with(|state| { #[cfg(not(feature = "proxy"))] @@ -407,9 +514,9 @@ pub unsafe extern "C" fn environ_get(environ: *mut *mut u8, environ_buf: *mut u8 /// Return environment variable data sizes. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn environ_sizes_get( - environc: *mut Size, - environ_buf_size: *mut Size, + environc: *mut Size, environ_buf_size: *mut Size, ) -> Errno { if !matches!( get_allocation_state(), @@ -444,8 +551,8 @@ pub unsafe extern "C" fn environ_sizes_get( } /// Return the resolution of a clock. -/// Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks, -/// return `errno::inval`. +/// Implementations are required to provide a non-zero value for supported clocks. For +/// unsupported clocks, return `errno::inval`. /// Note: This is similar to `clock_getres` in POSIX. #[no_mangle] pub extern "C" fn clock_res_get(id: Clockid, resolution: &mut Timestamp) -> Errno { @@ -453,7 +560,7 @@ pub extern "C" fn clock_res_get(id: Clockid, resolution: &mut Timestamp) -> Errn CLOCKID_MONOTONIC => { *resolution = monotonic_clock::resolution(); ERRNO_SUCCESS - } + }, CLOCKID_REALTIME => { let res = wall_clock::resolution(); *resolution = match Timestamp::from(res.seconds) @@ -464,7 +571,7 @@ pub extern "C" fn clock_res_get(id: Clockid, resolution: &mut Timestamp) -> Errn None => return ERRNO_OVERFLOW, }; ERRNO_SUCCESS - } + }, _ => ERRNO_BADF, } } @@ -472,16 +579,15 @@ pub extern "C" fn clock_res_get(id: Clockid, resolution: &mut Timestamp) -> Errn /// Return the time value of a clock. /// Note: This is similar to `clock_gettime` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn clock_time_get( - id: Clockid, - _precision: Timestamp, - time: &mut Timestamp, + id: Clockid, _precision: Timestamp, time: &mut Timestamp, ) -> Errno { match id { CLOCKID_MONOTONIC => { *time = monotonic_clock::now(); ERRNO_SUCCESS - } + }, CLOCKID_REALTIME => { let res = wall_clock::now(); *time = match Timestamp::from(res.seconds) @@ -492,7 +598,7 @@ pub unsafe extern "C" fn clock_time_get( None => return ERRNO_OVERFLOW, }; ERRNO_SUCCESS - } + }, _ => ERRNO_BADF, } } @@ -500,11 +606,9 @@ pub unsafe extern "C" fn clock_time_get( /// Provide file advisory information on a file descriptor. /// Note: This is similar to `posix_fadvise` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_advise( - fd: Fd, - offset: Filesize, - len: Filesize, - advice: Advice, + fd: Fd, offset: Filesize, len: Filesize, advice: Advice, ) -> Errno { cfg_filesystem_available! { let advice = match advice { @@ -528,6 +632,7 @@ pub unsafe extern "C" fn fd_advise( /// Force the allocation of space in a file. /// Note: This is similar to `posix_fallocate` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_allocate(fd: Fd, _offset: Filesize, _len: Filesize) -> Errno { cfg_filesystem_available! { State::with(|state| { @@ -543,6 +648,7 @@ pub unsafe extern "C" fn fd_allocate(fd: Fd, _offset: Filesize, _len: Filesize) /// Close a file descriptor. /// Note: This is similar to `close` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_close(fd: Fd) -> Errno { State::with(|state| { if let Descriptor::Bad = state.descriptors().get(fd)? { @@ -565,6 +671,7 @@ pub unsafe extern "C" fn fd_close(fd: Fd) -> Errno { /// Synchronize the data of a file to disk. /// Note: This is similar to `fdatasync` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_datasync(fd: Fd) -> Errno { cfg_filesystem_available! { State::with(|state| { @@ -577,8 +684,12 @@ pub unsafe extern "C" fn fd_datasync(fd: Fd) -> Errno { } /// Get the attributes of a file descriptor. -/// Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. +/// Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as +/// additional fields. #[no_mangle] +#[allow(clippy::missing_safety_doc)] +#[allow(clippy::single_match_else)] +#[allow(clippy::too_many_lines)] pub unsafe extern "C" fn fd_fdstat_get(fd: Fd, stat: *mut Fdstat) -> Errno { cfg_filesystem_available! { State::with(|state| { @@ -702,6 +813,7 @@ pub unsafe extern "C" fn fd_fdstat_get(fd: Fd, stat: *mut Fdstat) -> Errno { /// Adjust the flags associated with a file descriptor. /// Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_fdstat_set_flags(fd: Fd, flags: Fdflags) -> Errno { // Only support changing the NONBLOCK or APPEND flags. if flags & !(FDFLAGS_NONBLOCK | FDFLAGS_APPEND) != 0 { @@ -729,12 +841,12 @@ pub unsafe extern "C" fn fd_fdstat_set_flags(fd: Fd, flags: Fdflags) -> Errno { } } -/// Does not do anything if `fd` corresponds to a valid descriptor and returns [`wasi::ERRNO_BADF`] otherwise. +/// Does not do anything if `fd` corresponds to a valid descriptor and returns +/// [`wasi::ERRNO_BADF`] otherwise. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_fdstat_set_rights( - fd: Fd, - _fs_rights_base: Rights, - _fs_rights_inheriting: Rights, + fd: Fd, _fs_rights_base: Rights, _fs_rights_inheriting: Rights, ) -> Errno { State::with(|state| { let ds = state.descriptors(); @@ -747,6 +859,7 @@ pub unsafe extern "C" fn fd_fdstat_set_rights( /// Return the attributes of an open file. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_filestat_get(fd: Fd, buf: *mut Filestat) -> Errno { cfg_filesystem_available! { State::with(|state| { @@ -794,9 +907,10 @@ pub unsafe extern "C" fn fd_filestat_get(fd: Fd, buf: *mut Filestat) -> Errno { } } -/// Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros. -/// Note: This is similar to `ftruncate` in POSIX. +/// Adjust the size of an open file. If this increases the file's size, the extra bytes +/// are filled with zeros. Note: This is similar to `ftruncate` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_filestat_set_size(fd: Fd, size: Filesize) -> Errno { cfg_filesystem_available! { State::with(|state| { @@ -809,6 +923,7 @@ pub unsafe extern "C" fn fd_filestat_set_size(fd: Fd, size: Filesize) -> Errno { } #[cfg(not(feature = "proxy"))] +#[allow(clippy::missing_docs_in_private_items)] fn systimespec(set: bool, ts: Timestamp, now: bool) -> Result { if set && now { Err(wasi::ERRNO_INVAL) @@ -827,11 +942,10 @@ fn systimespec(set: bool, ts: Timestamp, now: bool) -> Result Errno { cfg_filesystem_available! { State::with(|state| { @@ -856,12 +970,10 @@ pub unsafe extern "C" fn fd_filestat_set_times( /// Read from a file descriptor, without using and updating the file descriptor's offset. /// Note: This is similar to `preadv` in POSIX. #[no_mangle] +#[allow(clippy::missing_panics_doc)] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_pread( - fd: Fd, - mut iovs_ptr: *const Iovec, - mut iovs_len: usize, - offset: Filesize, - nread: *mut Size, + fd: Fd, mut iovs_ptr: *const Iovec, mut iovs_len: usize, offset: Filesize, nread: *mut Size, ) -> Errno { cfg_filesystem_available! { // Advance to the first non-empty buffer. @@ -900,6 +1012,7 @@ pub unsafe extern "C" fn fd_pread( /// Return a description of the given preopened file descriptor. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_prestat_get(fd: Fd, buf: *mut Prestat) -> Errno { if !matches!( get_allocation_state(), @@ -931,6 +1044,7 @@ pub unsafe extern "C" fn fd_prestat_get(fd: Fd, buf: *mut Prestat) -> Errno { /// Return a description of the given preopened file descriptor. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_prestat_dir_name(fd: Fd, path: *mut u8, path_max_len: Size) -> Errno { cfg_filesystem_available! { State::with(|state| { @@ -952,12 +1066,10 @@ pub unsafe extern "C" fn fd_prestat_dir_name(fd: Fd, path: *mut u8, path_max_len /// Write to a file descriptor, without using and updating the file descriptor's offset. /// Note: This is similar to `pwritev` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] +#[allow(clippy::cast_possible_truncation)] pub unsafe extern "C" fn fd_pwrite( - fd: Fd, - mut iovs_ptr: *const Ciovec, - mut iovs_len: usize, - offset: Filesize, - nwritten: *mut Size, + fd: Fd, mut iovs_ptr: *const Ciovec, mut iovs_len: usize, offset: Filesize, nwritten: *mut Size, ) -> Errno { cfg_filesystem_available! { // Advance to the first non-empty buffer. @@ -986,11 +1098,10 @@ pub unsafe extern "C" fn fd_pwrite( /// Read from a file descriptor. /// Note: This is similar to `readv` in POSIX. #[no_mangle] +#[allow(clippy::missing_panics_doc)] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_read( - fd: Fd, - mut iovs_ptr: *const Iovec, - mut iovs_len: usize, - nread: *mut Size, + fd: Fd, mut iovs_ptr: *const Iovec, mut iovs_len: usize, nread: *mut Size, ) -> Errno { // Advance to the first non-empty buffer. while iovs_len != 0 && (*iovs_ptr).buf_len == 0 { @@ -1028,10 +1139,10 @@ pub unsafe extern "C" fn fd_read( Err(streams::StreamError::Closed) => { *nread = 0; return Ok(()); - } + }, Err(streams::StreamError::LastOperationFailed(e)) => { Err(stream_error_to_errno(e))? - } + }, }; assert_eq!(data.as_ptr(), ptr); @@ -1051,12 +1162,14 @@ pub unsafe extern "C" fn fd_read( *nread = len; forget(data); Ok(()) - } + }, Descriptor::Closed(_) | Descriptor::Bad => Err(ERRNO_BADF), } }) } +#[allow(clippy::missing_docs_in_private_items)] +#[allow(clippy::needless_pass_by_value)] fn stream_error_to_errno(err: streams::Error) -> Errno { #[cfg(feature = "proxy")] return ERRNO_IO; @@ -1079,23 +1192,25 @@ fn stream_error_to_errno(err: streams::Error) -> Errno { #[no_mangle] #[cfg(feature = "proxy")] pub unsafe extern "C" fn fd_readdir( - fd: Fd, - buf: *mut u8, - buf_len: Size, - cookie: Dircookie, - bufused: *mut Size, + fd: Fd, buf: *mut u8, buf_len: Size, cookie: Dircookie, bufused: *mut Size, ) -> Errno { wasi::ERRNO_NOTSUP } #[no_mangle] #[cfg(not(feature = "proxy"))] +#[allow(missing_docs)] +#[allow(clippy::ptr_as_ptr)] +#[allow(clippy::len_zero)] +#[allow(clippy::indexing_slicing)] +#[allow(clippy::borrow_as_ptr)] +#[allow(clippy::single_match_else)] +#[allow(clippy::items_after_statements)] +#[allow(clippy::too_many_lines)] +#[allow(clippy::missing_safety_doc)] +#[allow(trivial_casts)] pub unsafe extern "C" fn fd_readdir( - fd: Fd, - buf: *mut u8, - buf_len: Size, - cookie: Dircookie, - bufused: *mut Size, + fd: Fd, buf: *mut u8, buf_len: Size, cookie: Dircookie, bufused: *mut Size, ) -> Errno { let mut buf = slice::from_raw_parts_mut(buf, buf_len); return State::with(|state| { @@ -1137,7 +1252,7 @@ pub unsafe extern "C" fn fd_readdir( use_cache: true, dir_descriptor: &dir.fd, } - } + }, // Either a dirent stream wasn't previously available, a different // cookie was requested, or a brand new directory is now being read. @@ -1157,12 +1272,12 @@ pub unsafe extern "C" fn fd_readdir( // parameter. for _ in wasi::DIRCOOKIE_START..cookie { match iter.next() { - Some(Ok(_)) => {} + Some(Ok(_)) => {}, Some(Err(e)) => return Err(e), None => return Ok(()), } } - } + }, }; while buf.len() > 0 { @@ -1224,6 +1339,7 @@ pub unsafe extern "C" fn fd_readdir( Ok(()) }); + #[allow(clippy::missing_docs_in_private_items)] struct DirectoryEntryIterator<'a> { state: &'a State, use_cache: bool, @@ -1257,7 +1373,7 @@ pub unsafe extern "C" fn fd_readdir( d_namlen: 1, }; return Some(Ok((dirent, &self.state.dotdot[..1]))); - } + }, 1 => { let dirent = wasi::Dirent { d_next: self.cookie, @@ -1266,8 +1382,8 @@ pub unsafe extern "C" fn fd_readdir( d_namlen: 2, }; return Some(Ok((dirent, &self.state.dotdot[..]))); - } - _ => {} + }, + _ => {}, } if self.use_cache { @@ -1325,6 +1441,7 @@ pub unsafe extern "C" fn fd_readdir( /// This function provides a way to atomically renumber file descriptors, which /// would disappear if `dup2()` were to be removed entirely. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_renumber(fd: Fd, to: Fd) -> Errno { State::with(|state| state.descriptors_mut().renumber(fd, to)) } @@ -1332,11 +1449,11 @@ pub unsafe extern "C" fn fd_renumber(fd: Fd, to: Fd) -> Errno { /// Move the offset of a file descriptor. /// Note: This is similar to `lseek` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] +#[allow(clippy::cast_sign_loss)] +#[allow(clippy::cast_possible_wrap)] pub unsafe extern "C" fn fd_seek( - fd: Fd, - offset: Filedelta, - whence: Whence, - newoffset: *mut Filesize, + fd: Fd, offset: Filedelta, whence: Whence, newoffset: *mut Filesize, ) -> Errno { cfg_filesystem_available! { State::with(|state| { @@ -1377,6 +1494,7 @@ pub unsafe extern "C" fn fd_seek( /// Synchronize the data and metadata of a file to disk. /// Note: This is similar to `fsync` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_sync(fd: Fd) -> Errno { cfg_filesystem_available! { State::with(|state| { @@ -1391,6 +1509,7 @@ pub unsafe extern "C" fn fd_sync(fd: Fd) -> Errno { /// Return the current offset of a file descriptor. /// Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn fd_tell(fd: Fd, offset: *mut Filesize) -> Errno { cfg_filesystem_available! { State::with(|state| { @@ -1405,11 +1524,10 @@ pub unsafe extern "C" fn fd_tell(fd: Fd, offset: *mut Filesize) -> Errno { /// Write to a file descriptor. /// Note: This is similar to `writev` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] +#[allow(clippy::similar_names)] pub unsafe extern "C" fn fd_write( - fd: Fd, - mut iovs_ptr: *const Ciovec, - mut iovs_len: usize, - nwritten: *mut Size, + fd: Fd, mut iovs_ptr: *const Ciovec, mut iovs_len: usize, nwritten: *mut Size, ) -> Errno { if !matches!( get_allocation_state(), @@ -1469,7 +1587,7 @@ pub unsafe extern "C" fn fd_write( *nwritten = nbytes; Ok(()) - } + }, Descriptor::Closed(_) | Descriptor::Bad => Err(ERRNO_BADF), } }) @@ -1478,10 +1596,9 @@ pub unsafe extern "C" fn fd_write( /// Create a directory. /// Note: This is similar to `mkdirat` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn path_create_directory( - fd: Fd, - path_ptr: *const u8, - path_len: usize, + fd: Fd, path_ptr: *const u8, path_len: usize, ) -> Errno { cfg_filesystem_available! { let path = slice::from_raw_parts(path_ptr, path_len); @@ -1498,12 +1615,9 @@ pub unsafe extern "C" fn path_create_directory( /// Return the attributes of a file or directory. /// Note: This is similar to `stat` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn path_filestat_get( - fd: Fd, - flags: Lookupflags, - path_ptr: *const u8, - path_len: usize, - buf: *mut Filestat, + fd: Fd, flags: Lookupflags, path_ptr: *const u8, path_len: usize, buf: *mut Filestat, ) -> Errno { cfg_filesystem_available! { let path = slice::from_raw_parts(path_ptr, path_len); @@ -1533,14 +1647,11 @@ pub unsafe extern "C" fn path_filestat_get( /// Adjust the timestamps of a file or directory. /// Note: This is similar to `utimensat` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] +#[allow(clippy::similar_names)] pub unsafe extern "C" fn path_filestat_set_times( - fd: Fd, - flags: Lookupflags, - path_ptr: *const u8, - path_len: usize, - atim: Timestamp, - mtim: Timestamp, - fst_flags: Fstflags, + fd: Fd, flags: Lookupflags, path_ptr: *const u8, path_len: usize, atim: Timestamp, + mtim: Timestamp, fst_flags: Fstflags, ) -> Errno { cfg_filesystem_available! { let path = slice::from_raw_parts(path_ptr, path_len); @@ -1569,14 +1680,10 @@ pub unsafe extern "C" fn path_filestat_set_times( /// Create a hard link. /// Note: This is similar to `linkat` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn path_link( - old_fd: Fd, - old_flags: Lookupflags, - old_path_ptr: *const u8, - old_path_len: usize, - new_fd: Fd, - new_path_ptr: *const u8, - new_path_len: usize, + old_fd: Fd, old_flags: Lookupflags, old_path_ptr: *const u8, old_path_len: usize, new_fd: Fd, + new_path_ptr: *const u8, new_path_len: usize, ) -> Errno { cfg_filesystem_available! { let old_path = slice::from_raw_parts(old_path_ptr, old_path_len); @@ -1601,16 +1708,11 @@ pub unsafe extern "C" fn path_link( /// guaranteed to be less than 2**31. /// Note: This is similar to `openat` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] +#[allow(clippy::similar_names)] pub unsafe extern "C" fn path_open( - fd: Fd, - dirflags: Lookupflags, - path_ptr: *const u8, - path_len: usize, - oflags: Oflags, - fs_rights_base: Rights, - fs_rights_inheriting: Rights, - fdflags: Fdflags, - opened_fd: *mut Fd, + fd: Fd, dirflags: Lookupflags, path_ptr: *const u8, path_len: usize, oflags: Oflags, + fs_rights_base: Rights, fs_rights_inheriting: Rights, fdflags: Fdflags, opened_fd: *mut Fd, ) -> Errno { cfg_filesystem_available! { let _ = fs_rights_inheriting; @@ -1658,13 +1760,9 @@ pub unsafe extern "C" fn path_open( /// Read the contents of a symbolic link. /// Note: This is similar to `readlinkat` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn path_readlink( - fd: Fd, - path_ptr: *const u8, - path_len: usize, - buf: *mut u8, - buf_len: Size, - bufused: *mut Size, + fd: Fd, path_ptr: *const u8, path_len: usize, buf: *mut u8, buf_len: Size, bufused: *mut Size, ) -> Errno { cfg_filesystem_available! { let path = slice::from_raw_parts(path_ptr, path_len); @@ -1712,10 +1810,9 @@ pub unsafe extern "C" fn path_readlink( /// Return `errno::notempty` if the directory is not empty. /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn path_remove_directory( - fd: Fd, - path_ptr: *const u8, - path_len: usize, + fd: Fd, path_ptr: *const u8, path_len: usize, ) -> Errno { cfg_filesystem_available! { let path = slice::from_raw_parts(path_ptr, path_len); @@ -1732,12 +1829,9 @@ pub unsafe extern "C" fn path_remove_directory( /// Rename a file or directory. /// Note: This is similar to `renameat` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn path_rename( - old_fd: Fd, - old_path_ptr: *const u8, - old_path_len: usize, - new_fd: Fd, - new_path_ptr: *const u8, + old_fd: Fd, old_path_ptr: *const u8, old_path_len: usize, new_fd: Fd, new_path_ptr: *const u8, new_path_len: usize, ) -> Errno { cfg_filesystem_available! { @@ -1757,11 +1851,9 @@ pub unsafe extern "C" fn path_rename( /// Create a symbolic link. /// Note: This is similar to `symlinkat` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn path_symlink( - old_path_ptr: *const u8, - old_path_len: usize, - fd: Fd, - new_path_ptr: *const u8, + old_path_ptr: *const u8, old_path_len: usize, fd: Fd, new_path_ptr: *const u8, new_path_len: usize, ) -> Errno { cfg_filesystem_available! { @@ -1781,6 +1873,7 @@ pub unsafe extern "C" fn path_symlink( /// Return `errno::isdir` if the path refers to a directory. /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn path_unlink_file(fd: Fd, path_ptr: *const u8, path_len: usize) -> Errno { cfg_filesystem_available! { let path = slice::from_raw_parts(path_ptr, path_len); @@ -1794,283 +1887,301 @@ pub unsafe extern "C" fn path_unlink_file(fd: Fd, path_ptr: *const u8, path_len: } } -struct Pollables { - pointer: *mut Pollable, - index: usize, - length: usize, -} - -impl Pollables { - unsafe fn push(&mut self, pollable: Pollable) { - assert!(self.index < self.length); - // Use `ptr::write` instead of `*... = pollable` because `ptr::write` - // doesn't call drop on the old memory. - self.pointer.add(self.index).write(pollable); - self.index += 1; - } -} - +// Hermes DOES NOT support `poll` +// #[allow(clippy::missing_docs_in_private_items)] +// struct Pollables { +// pointer: *mut Pollable, +// index: usize, +// length: usize, +// } +// +// impl Pollables { +// #[allow(clippy::missing_docs_in_private_items)] +// unsafe fn push(&mut self, pollable: Pollable) { +// assert!(self.index < self.length); +// Use `ptr::write` instead of `*... = pollable` because `ptr::write` +// doesn't call drop on the old memory. +// self.pointer.add(self.index).write(pollable); +// self.index += 1; +// } +// } +// // We create new pollable handles for each `poll_oneoff` call, so drop them all // after the call. -impl Drop for Pollables { - fn drop(&mut self) { - while self.index != 0 { - self.index -= 1; - unsafe { - core::ptr::drop_in_place(self.pointer.add(self.index)); - } - } - } -} - -/// Concurrently poll for the occurrence of a set of events. -#[no_mangle] -pub unsafe extern "C" fn poll_oneoff( - r#in: *const Subscription, - out: *mut Event, - nsubscriptions: Size, - nevents: *mut Size, -) -> Errno { - *nevents = 0; - - let subscriptions = slice::from_raw_parts(r#in, nsubscriptions); - - // We're going to split the `nevents` buffer into two non-overlapping - // buffers: one to store the pollable handles, and the other to store - // the bool results. - // - // First, we assert that this is possible: - assert!(align_of::() >= align_of::()); - assert!(align_of::() >= align_of::()); - assert!( - nsubscriptions - .checked_mul(size_of::()) - .trapping_unwrap() - >= nsubscriptions - .checked_mul(size_of::()) - .trapping_unwrap() - .checked_add( - nsubscriptions - .checked_mul(size_of::()) - .trapping_unwrap() - ) - .trapping_unwrap() - ); - // Store the pollable handles at the beginning, and the bool results at the - // end, so that we don't clobber the bool results when writting the events. - let pollables = out as *mut c_void as *mut Pollable; - let results = out.add(nsubscriptions).cast::().sub(nsubscriptions); - - // Indefinite sleeping is not supported in preview1. - if nsubscriptions == 0 { - return ERRNO_INVAL; - } - - State::with(|state| { - const EVENTTYPE_CLOCK: u8 = wasi::EVENTTYPE_CLOCK.raw(); - const EVENTTYPE_FD_READ: u8 = wasi::EVENTTYPE_FD_READ.raw(); - const EVENTTYPE_FD_WRITE: u8 = wasi::EVENTTYPE_FD_WRITE.raw(); - - let mut pollables = Pollables { - pointer: pollables, - index: 0, - length: nsubscriptions, - }; - - for subscription in subscriptions { - pollables.push(match subscription.u.tag { - EVENTTYPE_CLOCK => { - let clock = &subscription.u.u.clock; - let absolute = (clock.flags & SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME) - == SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME; - match clock.id { - CLOCKID_REALTIME => { - let timeout = if absolute { - // Convert `clock.timeout` to `Datetime`. - let mut datetime = wall_clock::Datetime { - seconds: clock.timeout / 1_000_000_000, - nanoseconds: (clock.timeout % 1_000_000_000) as _, - }; - - // Subtract `now`. - let now = wall_clock::now(); - datetime.seconds -= now.seconds; - if datetime.nanoseconds < now.nanoseconds { - datetime.seconds -= 1; - datetime.nanoseconds += 1_000_000_000; - } - datetime.nanoseconds -= now.nanoseconds; - - // Convert to nanoseconds. - let nanos = datetime - .seconds - .checked_mul(1_000_000_000) - .ok_or(ERRNO_OVERFLOW)?; - nanos - .checked_add(datetime.nanoseconds.into()) - .ok_or(ERRNO_OVERFLOW)? - } else { - clock.timeout - }; - - monotonic_clock::subscribe_duration(timeout) - } - - CLOCKID_MONOTONIC => { - if absolute { - monotonic_clock::subscribe_instant(clock.timeout) - } else { - monotonic_clock::subscribe_duration(clock.timeout) - } - } - - _ => return Err(ERRNO_INVAL), - } - } - - EVENTTYPE_FD_READ => state - .descriptors() - .get_read_stream(subscription.u.u.fd_read.file_descriptor) - .map(|stream| stream.subscribe())?, - - EVENTTYPE_FD_WRITE => state - .descriptors() - .get_write_stream(subscription.u.u.fd_write.file_descriptor) - .map(|stream| stream.subscribe())?, - - _ => return Err(ERRNO_INVAL), - }); - } - - #[link(wasm_import_module = "wasi:io/poll@0.2.0-rc-2023-11-10")] - #[allow(improper_ctypes)] // FIXME(bytecodealliance/wit-bindgen#684) - extern "C" { - #[link_name = "poll"] - fn poll_import(pollables: *const Pollable, len: usize, rval: *mut ReadyList); - } - let mut ready_list = ReadyList { - base: std::ptr::null(), - len: 0, - }; - - state.import_alloc.with_buffer( - results.cast(), - nsubscriptions - .checked_mul(size_of::()) - .trapping_unwrap(), - || { - poll_import( - pollables.pointer, - pollables.length, - &mut ready_list as *mut _, - ); - }, - ); - - assert!(ready_list.len <= nsubscriptions); - assert_eq!(ready_list.base, results as *const u32); - - drop(pollables); - - let ready = std::slice::from_raw_parts(ready_list.base, ready_list.len); - - let mut count = 0; - - for subscription in ready { - let subscription = *subscriptions.as_ptr().add(*subscription as usize); - - let type_; - - let (error, nbytes, flags) = match subscription.u.tag { - EVENTTYPE_CLOCK => { - type_ = wasi::EVENTTYPE_CLOCK; - (ERRNO_SUCCESS, 0, 0) - } - - EVENTTYPE_FD_READ => { - type_ = wasi::EVENTTYPE_FD_READ; - let ds = state.descriptors(); - let desc = ds - .get(subscription.u.u.fd_read.file_descriptor) - .trapping_unwrap(); - match desc { - Descriptor::Streams(streams) => match &streams.type_ { - #[cfg(not(feature = "proxy"))] - StreamType::File(file) => match file.fd.stat() { - Ok(stat) => { - let nbytes = stat.size.saturating_sub(file.position.get()); - ( - ERRNO_SUCCESS, - nbytes, - if nbytes == 0 { - EVENTRWFLAGS_FD_READWRITE_HANGUP - } else { - 0 - }, - ) - } - Err(e) => (e.into(), 1, 0), - }, - StreamType::Stdio(_) => (ERRNO_SUCCESS, 1, 0), - }, - _ => unreachable!(), - } - } - EVENTTYPE_FD_WRITE => { - type_ = wasi::EVENTTYPE_FD_WRITE; - let ds = state.descriptors(); - let desc = ds - .get(subscription.u.u.fd_write.file_descriptor) - .trapping_unwrap(); - match desc { - Descriptor::Streams(streams) => match &streams.type_ { - #[cfg(not(feature = "proxy"))] - StreamType::File(_) => (ERRNO_SUCCESS, 1, 0), - StreamType::Stdio(_) => (ERRNO_SUCCESS, 1, 0), - }, - _ => unreachable!(), - } - } - - _ => unreachable!(), - }; - - *out.add(count) = Event { - userdata: subscription.userdata, - error, - type_, - fd_readwrite: EventFdReadwrite { nbytes, flags }, - }; - - count += 1; - } - - *nevents = count; - - Ok(()) - }) -} +// impl Drop for Pollables { +// fn drop(&mut self) { +// while self.index != 0 { +// self.index -= 1; +// unsafe { +// core::ptr::drop_in_place(self.pointer.add(self.index)); +// } +// } +// } +// } +// +// Concurrently poll for the occurrence of a set of events. +// #[no_mangle] +// #[allow(clippy::unreachable)] +// #[allow(clippy::single_match_else)] +// #[allow(clippy::ptr_cast_constness)] +// #[allow(clippy::borrow_as_ptr)] +// #[allow(clippy::redundant_closure_for_method_calls)] +// #[allow(clippy::missing_docs_in_private_items)] +// #[allow(clippy::ptr_as_ptr)] +// #[allow(clippy::too_many_lines)] +// #[allow(clippy::missing_panics_doc)] +// #[allow(clippy::missing_safety_doc)] +// #[allow(clippy::items_after_statements)] +// #[allow(trivial_casts)] +// #[allow(clippy::similar_names)] +// pub unsafe extern "C" fn poll_oneoff( +// r#in: *const Subscription, out: *mut Event, nsubscriptions: Size, nevents: *mut Size, +// ) -> Errno { +// nevents = 0; +// +// let subscriptions = slice::from_raw_parts(r#in, nsubscriptions); +// +// We're going to split the `nevents` buffer into two non-overlapping +// buffers: one to store the pollable handles, and the other to store +// the bool results. +// +// First, we assert that this is possible: +// assert!(align_of::() >= align_of::()); +// assert!(align_of::() >= align_of::()); +// assert!( +// nsubscriptions +// .checked_mul(size_of::()) +// .trapping_unwrap() +// >= nsubscriptions +// .checked_mul(size_of::()) +// .trapping_unwrap() +// .checked_add( +// nsubscriptions +// .checked_mul(size_of::()) +// .trapping_unwrap() +// ) +// .trapping_unwrap() +// ); +// Store the pollable handles at the beginning, and the bool results at the +// end, so that we don't clobber the bool results when writting the events. +// let pollables = out as *mut c_void as *mut Pollable; +// let results = out.add(nsubscriptions).cast::().sub(nsubscriptions); +// +// Indefinite sleeping is not supported in preview1. +// if nsubscriptions == 0 { +// return ERRNO_INVAL; +// } +// +// State::with(|state| { +// const EVENTTYPE_CLOCK: u8 = wasi::EVENTTYPE_CLOCK.raw(); +// const EVENTTYPE_FD_READ: u8 = wasi::EVENTTYPE_FD_READ.raw(); +// const EVENTTYPE_FD_WRITE: u8 = wasi::EVENTTYPE_FD_WRITE.raw(); +// +// let mut pollables = Pollables { +// pointer: pollables, +// index: 0, +// length: nsubscriptions, +// }; +// +// for subscription in subscriptions { +// pollables.push(match subscription.u.tag { +// EVENTTYPE_CLOCK => { +// let clock = &subscription.u.u.clock; +// let absolute = (clock.flags & SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME) +// == SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME; +// match clock.id { +// CLOCKID_REALTIME => { +// let timeout = if absolute { +// Convert `clock.timeout` to `Datetime`. +// let mut datetime = wall_clock::Datetime { +// seconds: clock.timeout / 1_000_000_000, +// nanoseconds: (clock.timeout % 1_000_000_000) as _, +// }; +// +// Subtract `now`. +// let now = wall_clock::now(); +// datetime.seconds -= now.seconds; +// if datetime.nanoseconds < now.nanoseconds { +// datetime.seconds -= 1; +// datetime.nanoseconds += 1_000_000_000; +// } +// datetime.nanoseconds -= now.nanoseconds; +// +// Convert to nanoseconds. +// let nanos = datetime +// .seconds +// .checked_mul(1_000_000_000) +// .ok_or(ERRNO_OVERFLOW)?; +// nanos +// .checked_add(datetime.nanoseconds.into()) +// .ok_or(ERRNO_OVERFLOW)? +// } else { +// clock.timeout +// }; +// +// monotonic_clock::subscribe_duration(timeout) +// }, +// +// CLOCKID_MONOTONIC => { +// if absolute { +// monotonic_clock::subscribe_instant(clock.timeout) +// } else { +// monotonic_clock::subscribe_duration(clock.timeout) +// } +// }, +// +// _ => return Err(ERRNO_INVAL), +// } +// }, +// +// EVENTTYPE_FD_READ => state +// .descriptors() +// .get_read_stream(subscription.u.u.fd_read.file_descriptor) +// .map(|stream| stream.subscribe())?, +// +// EVENTTYPE_FD_WRITE => state +// .descriptors() +// .get_write_stream(subscription.u.u.fd_write.file_descriptor) +// .map(|stream| stream.subscribe())?, +// +// _ => return Err(ERRNO_INVAL), +// }); +// } +// +// #[link(wasm_import_module = "wasi:io/poll@0.2.0-rc-2023-11-10")] +// #[allow(improper_ctypes)] // FIXME(bytecodealliance/wit-bindgen#684) +// extern "C" { +// #[link_name = "poll"] +// fn poll_import(pollables: *const Pollable, len: usize, rval: *mut ReadyList); +// } +// let mut ready_list = ReadyList { +// base: std::ptr::null(), +// len: 0, +// }; +// +// state.import_alloc.with_buffer( +// results.cast(), +// nsubscriptions +// .checked_mul(size_of::()) +// .trapping_unwrap(), +// || { +// poll_import( +// pollables.pointer, +// pollables.length, +// &mut ready_list as *mut _, +// ); +// }, +// ); +// +// assert!(ready_list.len <= nsubscriptions); +// assert_eq!(ready_list.base, results as *const u32); +// +// drop(pollables); +// +// let ready = std::slice::from_raw_parts(ready_list.base, ready_list.len); +// +// let mut count = 0; +// +// for subscription in ready { +// let subscription = *subscriptions.as_ptr().add(*subscription as usize); +// +// let type_; +// +// let (error, nbytes, flags) = match subscription.u.tag { +// EVENTTYPE_CLOCK => { +// type_ = wasi::EVENTTYPE_CLOCK; +// (ERRNO_SUCCESS, 0, 0) +// }, +// +// EVENTTYPE_FD_READ => { +// type_ = wasi::EVENTTYPE_FD_READ; +// let ds = state.descriptors(); +// let desc = ds +// .get(subscription.u.u.fd_read.file_descriptor) +// .trapping_unwrap(); +// match desc { +// Descriptor::Streams(streams) => match &streams.type_ { +// #[cfg(not(feature = "proxy"))] +// StreamType::File(file) => match file.fd.stat() { +// Ok(stat) => { +// let nbytes = stat.size.saturating_sub(file.position.get()); +// ( +// ERRNO_SUCCESS, +// nbytes, +// if nbytes == 0 { +// EVENTRWFLAGS_FD_READWRITE_HANGUP +// } else { +// 0 +// }, +// ) +// }, +// Err(e) => (e.into(), 1, 0), +// }, +// StreamType::Stdio(_) => (ERRNO_SUCCESS, 1, 0), +// }, +// _ => unreachable!(), +// } +// }, +// EVENTTYPE_FD_WRITE => { +// type_ = wasi::EVENTTYPE_FD_WRITE; +// let ds = state.descriptors(); +// let desc = ds +// .get(subscription.u.u.fd_write.file_descriptor) +// .trapping_unwrap(); +// match desc { +// Descriptor::Streams(streams) => match &streams.type_ { +// #[cfg(not(feature = "proxy"))] +// StreamType::File(_) => (ERRNO_SUCCESS, 1, 0), +// StreamType::Stdio(_) => (ERRNO_SUCCESS, 1, 0), +// }, +// _ => unreachable!(), +// } +// }, +// +// _ => unreachable!(), +// }; +// +// out.add(count) = Event { +// userdata: subscription.userdata, +// error, +// type_, +// fd_readwrite: EventFdReadwrite { nbytes, flags }, +// }; +// +// count += 1; +// } +// +// nevents = count; +// +// Ok(()) +// }) +// } /// Terminate the process normally. An exit code of 0 indicates successful /// termination of the program. The meanings of other values is dependent on /// the environment. -#[no_mangle] -pub unsafe extern "C" fn proc_exit(rval: Exitcode) -> ! { - #[cfg(feature = "proxy")] - { - unreachable!("no other implementation available in proxy world"); - } - #[cfg(not(feature = "proxy"))] - { - let status = if rval == 0 { Ok(()) } else { Err(()) }; - crate::bindings::wasi::cli::exit::exit(status); // does not return - unreachable!("host exit implementation didn't exit!") // actually unreachable - } -} +/// It is not valid to `exit` from a Hermes module. +// #[no_mangle] +// #[allow(clippy::unreachable)] +// #[allow(clippy::missing_safety_doc)] +// pub unsafe extern "C" fn proc_exit(rval: Exitcode) -> ! { +// #[cfg(feature = "proxy")] +// { +// unreachable!("no other implementation available in proxy world"); +// } +// #[cfg(not(feature = "proxy"))] +// { +// let status = if rval == 0 { Ok(()) } else { Err(()) }; +// crate::bindings::wasi::cli::exit::exit(status); // does not return +// unreachable!("host exit implementation didn't exit!") // actually unreachable +// } +// } /// Send a signal to the process of the calling thread. /// Note: This is similar to `raise` in POSIX. #[no_mangle] +#[allow(clippy::unreachable)] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn proc_raise(_sig: Signal) -> Errno { unreachable!() } @@ -2078,6 +2189,7 @@ pub unsafe extern "C" fn proc_raise(_sig: Signal) -> Errno { /// Temporarily yield execution of the calling thread. /// Note: This is similar to `sched_yield` in POSIX. #[no_mangle] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn sched_yield() -> Errno { // TODO: This is not yet covered in Preview2. @@ -2091,6 +2203,9 @@ pub unsafe extern "C" fn sched_yield() -> Errno { /// required, it's advisable to use this function to seed a pseudo-random /// number generator, rather than to provide the random data directly. #[no_mangle] +#[allow(clippy::cast_possible_truncation)] +#[allow(clippy::missing_panics_doc)] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn random_get(buf: *mut u8, buf_len: Size) -> Errno { if matches!( get_allocation_state(), @@ -2117,6 +2232,8 @@ pub unsafe extern "C" fn random_get(buf: *mut u8, buf_len: Size) -> Errno { /// Accept a new incoming connection. /// Note: This is similar to `accept` in POSIX. #[no_mangle] +#[allow(clippy::unreachable)] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn sock_accept(_fd: Fd, _flags: Fdflags, _connection: *mut Fd) -> Errno { unreachable!() } @@ -2125,13 +2242,11 @@ pub unsafe extern "C" fn sock_accept(_fd: Fd, _flags: Fdflags, _connection: *mut /// Note: This is similar to `recv` in POSIX, though it also supports reading /// the data into multiple buffers in the manner of `readv`. #[no_mangle] +#[allow(clippy::unreachable)] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn sock_recv( - _fd: Fd, - _ri_data_ptr: *const Iovec, - _ri_data_len: usize, - _ri_flags: Riflags, - _ro_datalen: *mut Size, - _ro_flags: *mut Roflags, + _fd: Fd, _ri_data_ptr: *const Iovec, _ri_data_len: usize, _ri_flags: Riflags, + _ro_datalen: *mut Size, _ro_flags: *mut Roflags, ) -> Errno { unreachable!() } @@ -2140,11 +2255,10 @@ pub unsafe extern "C" fn sock_recv( /// Note: This is similar to `send` in POSIX, though it also supports writing /// the data from multiple buffers in the manner of `writev`. #[no_mangle] +#[allow(clippy::unreachable)] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn sock_send( - _fd: Fd, - _si_data_ptr: *const Ciovec, - _si_data_len: usize, - _si_flags: Siflags, + _fd: Fd, _si_data_ptr: *const Ciovec, _si_data_len: usize, _si_flags: Siflags, _so_datalen: *mut Size, ) -> Errno { unreachable!() @@ -2153,20 +2267,26 @@ pub unsafe extern "C" fn sock_send( /// Shut down socket send and receive channels. /// Note: This is similar to `shutdown` in POSIX. #[no_mangle] +#[allow(clippy::unreachable)] +#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn sock_shutdown(_fd: Fd, _how: Sdflags) -> Errno { unreachable!() } #[cfg(not(feature = "proxy"))] +#[allow(clippy::missing_docs_in_private_items)] fn datetime_to_timestamp(datetime: Option) -> Timestamp { match datetime { - Some(datetime) => u64::from(datetime.nanoseconds) - .saturating_add(datetime.seconds.saturating_mul(1_000_000_000)), + Some(datetime) => { + u64::from(datetime.nanoseconds) + .saturating_add(datetime.seconds.saturating_mul(1_000_000_000)) + }, None => 0, } } #[cfg(not(feature = "proxy"))] +#[allow(clippy::missing_docs_in_private_items)] fn at_flags_from_lookupflags(flags: Lookupflags) -> filesystem::PathFlags { if flags & LOOKUPFLAGS_SYMLINK_FOLLOW == LOOKUPFLAGS_SYMLINK_FOLLOW { filesystem::PathFlags::SYMLINK_FOLLOW @@ -2176,6 +2296,7 @@ fn at_flags_from_lookupflags(flags: Lookupflags) -> filesystem::PathFlags { } #[cfg(not(feature = "proxy"))] +#[allow(clippy::missing_docs_in_private_items)] fn o_flags_from_oflags(flags: Oflags) -> filesystem::OpenFlags { let mut o_flags = filesystem::OpenFlags::empty(); if flags & OFLAGS_CREAT == OFLAGS_CREAT { @@ -2194,6 +2315,7 @@ fn o_flags_from_oflags(flags: Oflags) -> filesystem::OpenFlags { } #[cfg(not(feature = "proxy"))] +#[allow(clippy::missing_docs_in_private_items)] fn descriptor_flags_from_flags(rights: Rights, fdflags: Fdflags) -> filesystem::DescriptorFlags { let mut flags = filesystem::DescriptorFlags::empty(); if rights & wasi::RIGHTS_FD_READ == wasi::RIGHTS_FD_READ { @@ -2263,6 +2385,7 @@ impl From for Errno { } #[cfg(not(feature = "proxy"))] +#[allow(clippy::unreachable)] impl From for wasi::Filetype { fn from(ty: filesystem::DescriptorType) -> wasi::Filetype { match ty { @@ -2271,17 +2394,19 @@ impl From for wasi::Filetype { filesystem::DescriptorType::BlockDevice => FILETYPE_BLOCK_DEVICE, filesystem::DescriptorType::CharacterDevice => FILETYPE_CHARACTER_DEVICE, // preview1 never had a FIFO code. - filesystem::DescriptorType::Fifo => FILETYPE_UNKNOWN, + filesystem::DescriptorType::Fifo | filesystem::DescriptorType::Unknown => { + FILETYPE_UNKNOWN + }, // TODO: Add a way to disginguish between FILETYPE_SOCKET_STREAM and // FILETYPE_SOCKET_DGRAM. filesystem::DescriptorType::Socket => unreachable!(), filesystem::DescriptorType::SymbolicLink => FILETYPE_SYMBOLIC_LINK, - filesystem::DescriptorType::Unknown => FILETYPE_UNKNOWN, } } } #[derive(Clone, Copy)] +#[allow(missing_docs)] pub enum BlockingMode { NonBlocking, Blocking, @@ -2291,20 +2416,21 @@ impl BlockingMode { // note: these methods must take self, not &self, to avoid rustc creating a constant // out of a BlockingMode literal that it places in .romem, creating a data section and // breaking our fragile linking scheme + #[allow(clippy::missing_docs_in_private_items)] fn read( - self, - input_stream: &streams::InputStream, - read_len: u64, + self, input_stream: &streams::InputStream, read_len: u64, ) -> Result, streams::StreamError> { match self { BlockingMode::NonBlocking => input_stream.read(read_len), BlockingMode::Blocking => input_stream.blocking_read(read_len), } } + + #[allow(clippy::missing_docs_in_private_items)] + #[allow(clippy::indexing_slicing)] + #[allow(clippy::cast_possible_truncation)] fn write( - self, - output_stream: &streams::OutputStream, - mut bytes: &[u8], + self, output_stream: &streams::OutputStream, mut bytes: &[u8], ) -> Result { match self { BlockingMode::Blocking => { @@ -2314,15 +2440,15 @@ impl BlockingMode { let (chunk, rest) = bytes.split_at(len); bytes = rest; match output_stream.blocking_write_and_flush(chunk) { - Ok(()) => {} + Ok(()) => {}, Err(streams::StreamError::Closed) => return Err(ERRNO_IO), Err(streams::StreamError::LastOperationFailed(e)) => { return Err(stream_error_to_errno(e)) - } + }, } } Ok(total) - } + }, BlockingMode::NonBlocking => { let permit = match output_stream.check_write() { @@ -2330,7 +2456,7 @@ impl BlockingMode { Err(streams::StreamError::Closed) => 0, Err(streams::StreamError::LastOperationFailed(e)) => { return Err(stream_error_to_errno(e)) - } + }, }; let len = bytes.len().min(permit as usize); @@ -2339,29 +2465,30 @@ impl BlockingMode { } match output_stream.write(&bytes[..len]) { - Ok(_) => {} + Ok(()) => {}, Err(streams::StreamError::Closed) => return Ok(0), Err(streams::StreamError::LastOperationFailed(e)) => { return Err(stream_error_to_errno(e)) - } + }, } match output_stream.blocking_flush() { - Ok(_) => {} + Ok(()) => {}, Err(streams::StreamError::Closed) => return Ok(0), Err(streams::StreamError::LastOperationFailed(e)) => { return Err(stream_error_to_errno(e)) - } + }, } Ok(len) - } + }, } } } #[repr(C)] #[cfg(not(feature = "proxy"))] +#[allow(missing_docs)] pub struct File { /// The handle to the preview2 descriptor that this file is referencing. fd: filesystem::Descriptor, @@ -2383,14 +2510,13 @@ pub struct File { #[cfg(not(feature = "proxy"))] impl File { + #[allow(clippy::missing_docs_in_private_items)] fn is_dir(&self) -> bool { - match self.descriptor_type { - filesystem::DescriptorType::Directory => true, - _ => false, - } + matches!(self.descriptor_type, filesystem::DescriptorType::Directory) } } +#[allow(clippy::missing_docs_in_private_items)] const PAGE_SIZE: usize = 65536; /// The maximum path length. WASI doesn't explicitly guarantee this, but all @@ -2405,6 +2531,7 @@ const DIRENT_CACHE: usize = 256; const MAGIC: u32 = u32::from_le_bytes(*b"ugh!"); #[repr(C)] // used for now to keep magic1 and magic2 at the start and end +#[allow(clippy::missing_docs_in_private_items)] struct State { /// A canary constant value located at the beginning of this structure to /// try to catch memory corruption coming from the bottom. @@ -2457,6 +2584,7 @@ struct State { } #[cfg(not(feature = "proxy"))] +#[allow(clippy::missing_docs_in_private_items)] struct DirentCache { stream: Cell>, for_fd: Cell, @@ -2466,21 +2594,28 @@ struct DirentCache { } #[cfg(not(feature = "proxy"))] +#[allow(clippy::missing_docs_in_private_items)] struct DirectoryEntryStream(filesystem::DirectoryEntryStream); #[repr(C)] +#[allow(missing_docs)] +#[allow(clippy::missing_docs_in_private_items)] pub struct WasmStr { ptr: *const u8, len: usize, } #[repr(C)] +#[allow(missing_docs)] +#[allow(clippy::missing_docs_in_private_items)] pub struct WasmStrList { base: *const WasmStr, len: usize, } #[repr(C)] +#[allow(missing_docs)] +#[allow(clippy::missing_docs_in_private_items)] pub struct StrTuple { key: WasmStr, value: WasmStr, @@ -2488,6 +2623,8 @@ pub struct StrTuple { #[derive(Copy, Clone)] #[repr(C)] +#[allow(missing_docs)] +#[allow(clippy::missing_docs_in_private_items)] pub struct StrTupleList { base: *const StrTuple, len: usize, @@ -2495,11 +2632,14 @@ pub struct StrTupleList { #[derive(Copy, Clone)] #[repr(C)] +#[allow(missing_docs)] +#[allow(clippy::missing_docs_in_private_items)] pub struct ReadyList { base: *const u32, len: usize, } +#[allow(clippy::missing_docs_in_private_items)] const fn bump_arena_size() -> usize { // The total size of the struct should be a page, so start there let mut start = PAGE_SIZE; @@ -2530,6 +2670,7 @@ const _: () = { #[allow(unused)] #[repr(i32)] +#[allow(clippy::missing_docs_in_private_items)] enum AllocationState { StackUnallocated, StackAllocating, @@ -2547,6 +2688,7 @@ extern "C" { } impl State { + #[allow(clippy::missing_docs_in_private_items)] fn with(f: impl FnOnce(&State) -> Result<(), Errno>) -> Errno { let state_ref = State::ptr(); assert_eq!(state_ref.magic1, MAGIC); @@ -2558,6 +2700,7 @@ impl State { } } + #[allow(clippy::missing_docs_in_private_items)] fn ptr() -> &'static State { unsafe { let mut ptr = get_state_ptr(); @@ -2570,14 +2713,14 @@ impl State { } #[cold] + #[allow(clippy::cast_ptr_alignment)] + #[allow(clippy::ptr_as_ptr)] + #[allow(clippy::missing_docs_in_private_items)] fn new() -> *mut State { #[link(wasm_import_module = "__main_module__")] extern "C" { fn cabi_realloc( - old_ptr: *mut u8, - old_len: usize, - align: usize, - new_len: usize, + old_ptr: *mut u8, old_len: usize, align: usize, new_len: usize, ) -> *mut u8; } @@ -2607,6 +2750,7 @@ impl State { } #[cold] + #[allow(clippy::missing_docs_in_private_items)] unsafe fn init(state: *mut State) { state.write(State { magic1: MAGIC, @@ -2639,7 +2783,8 @@ impl State { } /// Accessor for the descriptors member that ensures it is properly initialized - fn descriptors<'a>(&'a self) -> impl Deref + 'a { + #[allow(clippy::unreachable)] + fn descriptors(&self) -> impl Deref + '_ { let mut d = self .descriptors .try_borrow_mut() @@ -2651,7 +2796,8 @@ impl State { } /// Mut accessor for the descriptors member that ensures it is properly initialized - fn descriptors_mut<'a>(&'a self) -> impl DerefMut + Deref + 'a { + #[allow(clippy::unreachable)] + fn descriptors_mut(&self) -> impl DerefMut + Deref + '_ { let mut d = self .descriptors .try_borrow_mut() @@ -2663,6 +2809,9 @@ impl State { } #[cfg(not(feature = "proxy"))] + #[allow(clippy::missing_docs_in_private_items)] + #[allow(clippy::borrow_as_ptr)] + #[allow(trivial_casts)] fn get_environment(&self) -> &[StrTuple] { if self.env_vars.get().is_none() { #[link(wasm_import_module = "wasi:cli/environment@0.2.0-rc-2023-12-05")] @@ -2676,11 +2825,11 @@ impl State { }; self.import_alloc .with_arena(&self.long_lived_arena, || unsafe { - get_environment_import(&mut list as *mut _) + get_environment_import(&mut list as *mut _); }); self.env_vars.set(Some(unsafe { - /* allocation comes from long lived arena, so it is safe to - * cast this to a &'static slice: */ + // allocation comes from long lived arena, so it is safe to + // cast this to a &'static slice: std::slice::from_raw_parts(list.base, list.len) })); } @@ -2688,6 +2837,9 @@ impl State { } #[cfg(not(feature = "proxy"))] + #[allow(clippy::missing_docs_in_private_items)] + #[allow(clippy::borrow_as_ptr)] + #[allow(trivial_casts)] fn get_args(&self) -> &[WasmStr] { if self.args.get().is_none() { #[link(wasm_import_module = "wasi:cli/environment@0.2.0-rc-2023-12-05")] @@ -2701,11 +2853,11 @@ impl State { }; self.import_alloc .with_arena(&self.long_lived_arena, || unsafe { - get_args_import(&mut list as *mut _) + get_args_import(&mut list as *mut _); }); self.args.set(Some(unsafe { - /* allocation comes from long lived arena, so it is safe to - * cast this to a &'static slice: */ + // allocation comes from long lived arena, so it is safe to + // cast this to a &'static slice: std::slice::from_raw_parts(list.base, list.len) })); } diff --git a/hermes/wasm/crates/wasi-component-adapter/src/macros.rs b/wasm/wasi-hermes-component-adapter/src/macros.rs similarity index 93% rename from hermes/wasm/crates/wasi-component-adapter/src/macros.rs rename to wasm/wasi-hermes-component-adapter/src/macros.rs index c1ec4a515..7ba31d972 100644 --- a/hermes/wasm/crates/wasi-component-adapter/src/macros.rs +++ b/wasm/wasi-hermes-component-adapter/src/macros.rs @@ -8,7 +8,7 @@ use crate::bindings::wasi::cli::stderr::get_stderr; #[allow(dead_code)] #[doc(hidden)] pub fn print(message: &[u8]) { - let _ = get_stderr().blocking_write_and_flush(message); + let _unused = get_stderr().blocking_write_and_flush(message); } /// A minimal `eprint` for debugging. @@ -33,11 +33,13 @@ macro_rules! eprintln { }}; } +#[allow(clippy::missing_docs_in_private_items)] +#[allow(clippy::items_after_statements)] pub(crate) fn eprint_u32(x: u32) { if x == 0 { eprint!("0"); } else { - eprint_u32_impl(x) + eprint_u32_impl(x); } fn eprint_u32_impl(x: u32) { diff --git a/wasm/wasi/unsupported/Readme.md b/wasm/wasi/unsupported/Readme.md new file mode 100644 index 000000000..46b527613 --- /dev/null +++ b/wasm/wasi/unsupported/Readme.md @@ -0,0 +1,6 @@ +# Unsupported WASI Components + +The files in this directory are currently unsupported and no support is planned for Hermes. + +These are retained in this directory temporarily, and for documentation purposes only. +They will be cleaned up closer to release. diff --git a/hermes/wasm/crates/wasi/wit/command-extended.wit b/wasm/wasi/unsupported/command-extended.wit similarity index 59% rename from hermes/wasm/crates/wasi/wit/command-extended.wit rename to wasm/wasi/unsupported/command-extended.wit index 16f01bc23..4bff99e65 100644 --- a/hermes/wasm/crates/wasi/wit/command-extended.wit +++ b/wasm/wasi/unsupported/command-extended.wit @@ -1,6 +1,6 @@ // All of the same imports and exports available in the wasi:cli/command world // with addition of HTTP proxy related imports: world command-extended { - include wasi:cli/command@0.2.0-rc-2023-12-05; - import wasi:http/outgoing-handler@0.2.0-rc-2023-12-05; + include wasi:cli/command@0.2.0; + import wasi:http/outgoing-handler@0.2.0; } diff --git a/hermes/wasm/crates/wasi/wit/deps/cli/command.wit b/wasm/wasi/unsupported/deps/cli/command.wit similarity index 58% rename from hermes/wasm/crates/wasi/wit/deps/cli/command.wit rename to wasm/wasi/unsupported/deps/cli/command.wit index cc82ae5dc..d8005bd38 100644 --- a/hermes/wasm/crates/wasi/wit/deps/cli/command.wit +++ b/wasm/wasi/unsupported/deps/cli/command.wit @@ -1,4 +1,4 @@ -package wasi:cli@0.2.0-rc-2023-12-05; +package wasi:cli@0.2.0; world command { include imports; diff --git a/hermes/wasm/crates/wasi/wit/deps/cli/exit.wit b/wasm/wasi/unsupported/deps/cli/exit.wit similarity index 100% rename from hermes/wasm/crates/wasi/wit/deps/cli/exit.wit rename to wasm/wasi/unsupported/deps/cli/exit.wit diff --git a/hermes/wasm/crates/wasi/wit/deps/cli/run.wit b/wasm/wasi/unsupported/deps/cli/run.wit similarity index 100% rename from hermes/wasm/crates/wasi/wit/deps/cli/run.wit rename to wasm/wasi/unsupported/deps/cli/run.wit diff --git a/hermes/wasm/crates/wasi/wit/deps/cli/terminal.wit b/wasm/wasi/unsupported/deps/cli/terminal.wit similarity index 75% rename from hermes/wasm/crates/wasi/wit/deps/cli/terminal.wit rename to wasm/wasi/unsupported/deps/cli/terminal.wit index 47495769b..38c724efc 100644 --- a/hermes/wasm/crates/wasi/wit/deps/cli/terminal.wit +++ b/wasm/wasi/unsupported/deps/cli/terminal.wit @@ -1,19 +1,21 @@ +/// 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. interface terminal-input { /// The input side of a terminal. resource 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. } +/// 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. interface terminal-output { /// The output side of a terminal. resource 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. } /// An interface providing an optional `terminal-input` for stdin as a diff --git a/wasm/wasi/unsupported/deps/hermes-api/database.wit b/wasm/wasi/unsupported/deps/hermes-api/database.wit new file mode 100644 index 000000000..009b792a6 --- /dev/null +++ b/wasm/wasi/unsupported/deps/hermes-api/database.wit @@ -0,0 +1,12 @@ +/// Interface to local database instance +/// +/// Only available if Hermes was built with the `db` feature +default interface database { + use pkg.types.{cbor} + + /// Set a value in the local key-value store + kv-set: func(key: string, value: cbor) + + /// Get a value from the local key-value store + kv-get: func(key: string) -> option +} diff --git a/wasm/wasi/unsupported/deps/hermes-api/logging.wit b/wasm/wasi/unsupported/deps/hermes-api/logging.wit new file mode 100644 index 000000000..8f3b756c8 --- /dev/null +++ b/wasm/wasi/unsupported/deps/hermes-api/logging.wit @@ -0,0 +1,58 @@ +/// # Logging API +/// +/// Logging API functionality exposed to the Hermes WASM Modules. +/// +/// ## Permissions +/// +/// This API is ALWAYS available, and not permissioned. + +/// Logging API Interface +default interface logging { + use pkg.types.{json} + + /// The supported logging levels + enum level { + /// Debug Log Level + debug, + /// Tracing Log level + trace, + /// General Informational Log Level + info, + /// Warning about something that might be a problem. + warn, + /// A very serious error + error, + } + + /// Generate a Log + /// + /// The Hermes API will add extra information to the log, such as the instance of the webasm + /// module being logged. + /// The Webasm module does not need to concern itself with this kind of information, and should + /// log as if it is the only instance. + /// It also should not log any webasm shared context, except where it is relevant to the log message itself. + /// + /// **Parameters** + /// + /// - `level` : The log level this message is for. + /// - `file` : The name of the src file being logged from. + /// - `fn` : The function within the file being logged from. + /// - `msg` : A Single line message to be logged. (Should have no newlines or format). + /// - `data` : A Free form json payload that will be logged with the msg. This must be valid JSON. + /// + /// *Notes* + /// + /// The `data` parameter may contain a record of the format: + /// ```json + /// { + /// "bt" : [ , ] + /// } + /// ``` + /// The logger will interpret this as a backtrace where each entry in the array is one line of the backtrace. + /// The format of the backtrace lines is up to the webasm, code. + /// The individual backtrace entries may contain line breaks if the backtrace entry is + /// multiline. + /// This is to keep the broadest flexibility for multiple languages capabilities. + /// The backtrace must be sorted with most recent lines of the backtrace occurring first in the array. + log: func(level: level, file: string, fn: string, line: u32, col:u32, msg: string, data: option) -> () +} diff --git a/wasm/wasi/unsupported/deps/hermes-api/types.wit b/wasm/wasi/unsupported/deps/hermes-api/types.wit new file mode 100644 index 000000000..03575d96f --- /dev/null +++ b/wasm/wasi/unsupported/deps/hermes-api/types.wit @@ -0,0 +1,16 @@ +/// Definitions of Common Types that can be used by multiple interfaces. +/// +/// Note: +/// Only types that can be used in multiple API's are defined here. +/// All API specific types reside with their API definition. + +default interface types { + /// JSON is just a string. + /// This type is used to indicate the string MUST be properly formatted JSON. + type json = string + + /// CBOR is a binary cbor data type. + /// This type is used to indicate the binary array MUST be CBOR data. + type cbor = list + +} \ No newline at end of file diff --git a/wasm/wasi/unsupported/deps/hermes-event/init.wit b/wasm/wasi/unsupported/deps/hermes-event/init.wit new file mode 100644 index 000000000..fc3d4cac7 --- /dev/null +++ b/wasm/wasi/unsupported/deps/hermes-event/init.wit @@ -0,0 +1,21 @@ +/// # Init API +/// +/// The Init Event is only ever called once per application the webasm module is used in. +/// +/// ## Permissions +/// +/// This API is ALWAYS available, and not permissioned. + +/// Logging API Interface +default interface init { + + /// Perform application start up initialization. + /// + /// This will only ever be called once when the application this module is a part of is started. + /// The module must export this interface to use it. + /// + /// Returns: + /// - `true` - Initialization is successful, the application may commence. + /// - `false` - Fatal error during Initialization. DO NOT START APPLICATION. + init: func() -> bool +} \ No newline at end of file diff --git a/hermes/wasm/crates/wasi/wit/deps/io/poll.wit b/wasm/wasi/unsupported/deps/io/poll.wit similarity index 97% rename from hermes/wasm/crates/wasi/wit/deps/io/poll.wit rename to wasm/wasi/unsupported/deps/io/poll.wit index bddde3c19..c982f1af7 100644 --- a/hermes/wasm/crates/wasi/wit/deps/io/poll.wit +++ b/wasm/wasi/unsupported/deps/io/poll.wit @@ -1,4 +1,4 @@ -package wasi:io@0.2.0-rc-2023-11-10; +package wasi:io@0.2.0; /// A poll API intended to let users wait for I/O events on multiple handles /// at once. diff --git a/hermes/wasm/crates/wasi/wit/deps/sockets/instance-network.wit b/wasm/wasi/unsupported/deps/sockets/instance-network.wit similarity index 100% rename from hermes/wasm/crates/wasi/wit/deps/sockets/instance-network.wit rename to wasm/wasi/unsupported/deps/sockets/instance-network.wit diff --git a/hermes/wasm/crates/wasi/wit/deps/sockets/ip-name-lookup.wit b/wasm/wasi/unsupported/deps/sockets/ip-name-lookup.wit similarity index 97% rename from hermes/wasm/crates/wasi/wit/deps/sockets/ip-name-lookup.wit rename to wasm/wasi/unsupported/deps/sockets/ip-name-lookup.wit index 931ccf7e0..8e639ec59 100644 --- a/hermes/wasm/crates/wasi/wit/deps/sockets/ip-name-lookup.wit +++ b/wasm/wasi/unsupported/deps/sockets/ip-name-lookup.wit @@ -1,6 +1,6 @@ interface ip-name-lookup { - use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; + use wasi:io/poll@0.2.0.{pollable}; use network.{network, error-code, ip-address}; diff --git a/hermes/wasm/crates/wasi/wit/deps/sockets/network.wit b/wasm/wasi/unsupported/deps/sockets/network.wit similarity index 86% rename from hermes/wasm/crates/wasi/wit/deps/sockets/network.wit rename to wasm/wasi/unsupported/deps/sockets/network.wit index 6bb07cd6f..9cadf0650 100644 --- a/hermes/wasm/crates/wasi/wit/deps/sockets/network.wit +++ b/wasm/wasi/unsupported/deps/sockets/network.wit @@ -18,8 +18,6 @@ interface network { /// /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. enum error-code { - // ### GENERAL ERRORS ### - /// Unknown error unknown, @@ -64,9 +62,6 @@ interface network { would-block, - - // ### TCP & UDP SOCKET ERRORS ### - /// The operation is not valid in the socket's current state. invalid-state, @@ -83,24 +78,21 @@ interface network { remote-unreachable, - // ### TCP SOCKET ERRORS ### - - /// The connection was forcefully rejected + /// The TCP connection was forcefully rejected connection-refused, - /// The connection was reset. + /// The TCP connection was reset. connection-reset, - /// A connection was aborted. + /// A TCP connection was aborted. connection-aborted, - // ### UDP SOCKET ERRORS ### + /// The size of a datagram sent to a UDP socket exceeded the maximum + /// supported size. datagram-too-large, - // ### NAME LOOKUP ERRORS ### - /// Name does not exist or has no suitable associated IP addresses. name-unresolvable, @@ -128,15 +120,21 @@ interface network { } record ipv4-socket-address { - port: u16, // sin_port - address: ipv4-address, // sin_addr + /// sin_port + port: u16, + /// sin_addr + address: ipv4-address, } record ipv6-socket-address { - port: u16, // sin6_port - flow-info: u32, // sin6_flowinfo - address: ipv6-address, // sin6_addr - scope-id: u32, // sin6_scope_id + /// sin6_port + port: u16, + /// sin6_flowinfo + flow-info: u32, + /// sin6_addr + address: ipv6-address, + /// sin6_scope_id + scope-id: u32, } variant ip-socket-address { diff --git a/hermes/wasm/crates/wasi/wit/deps/sockets/tcp-create-socket.wit b/wasm/wasi/unsupported/deps/sockets/tcp-create-socket.wit similarity index 90% rename from hermes/wasm/crates/wasi/wit/deps/sockets/tcp-create-socket.wit rename to wasm/wasi/unsupported/deps/sockets/tcp-create-socket.wit index 768a07c85..c7ddf1f22 100644 --- a/hermes/wasm/crates/wasi/wit/deps/sockets/tcp-create-socket.wit +++ b/wasm/wasi/unsupported/deps/sockets/tcp-create-socket.wit @@ -6,9 +6,10 @@ interface tcp-create-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. /// /// This function does not require a network capability handle. This is considered to be safe because - /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. /// /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. diff --git a/hermes/wasm/crates/wasi/wit/deps/sockets/tcp.wit b/wasm/wasi/unsupported/deps/sockets/tcp.wit similarity index 86% rename from hermes/wasm/crates/wasi/wit/deps/sockets/tcp.wit rename to wasm/wasi/unsupported/deps/sockets/tcp.wit index 976b272c0..fbe023b64 100644 --- a/hermes/wasm/crates/wasi/wit/deps/sockets/tcp.wit +++ b/wasm/wasi/unsupported/deps/sockets/tcp.wit @@ -1,8 +1,8 @@ interface tcp { - use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream}; - use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; - use wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10.{duration}; + use wasi:io/streams@0.2.0.{input-stream, output-stream}; + use wasi:io/poll@0.2.0.{pollable}; + use wasi:clocks/monotonic-clock@0.2.0.{duration}; use network.{network, error-code, ip-socket-address, ip-address-family}; enum shutdown-type { @@ -25,15 +25,12 @@ interface tcp { /// network interface(s) to bind to. /// If the TCP/UDP port is zero, the socket will be bound to a random free port. /// - /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will - /// implicitly bind the socket. - /// /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. /// /// # Typical `start` 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, but the socket has `ipv6-only` enabled. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL) /// - `invalid-state`: The socket is already bound. (EINVAL) /// /// # Typical `finish` errors @@ -45,8 +42,9 @@ interface tcp { /// /// # 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 (i.e. the SO_REUSEADDR socket - /// option should be set implicitly on platforms that require it). + /// 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 /// - @@ -62,20 +60,13 @@ interface tcp { /// - the socket is transitioned into the Connection state /// - a pair of streams is returned that can be used to read & write to the connection /// - /// POSIX mentions: - /// > If connect() fails, the state of the socket is unspecified. Conforming applications should - /// > close the file descriptor and create a new socket before attempting to reconnect. - /// - /// WASI prescribes the following behavior: - /// - If `connect` fails because an input/state validation error, the socket should remain usable. - /// - If a connection was actually attempted but failed, the socket should become unusable for further network communication. - /// Besides `drop`, any method after such a failure may return an error. + /// After a failed connection attempt, the only valid action left is to + /// `drop` the socket. A single socket can not be used to connect more than once. /// /// # Typical `start` 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, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos) - /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) + /// - `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-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. @@ -130,7 +121,6 @@ interface tcp { /// /// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket: /// - `address-family` - /// - `ipv6-only` /// - `keep-alive-enabled` /// - `keep-alive-idle-time` /// - `keep-alive-interval` @@ -195,17 +185,6 @@ interface tcp { /// Equivalent to the SO_DOMAIN socket option. address-family: func() -> ip-address-family; - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # Typical errors - /// - `invalid-state`: (set) The socket is already bound. - /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. - /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - ipv6-only: func() -> result; - set-ipv6-only: func(value: bool) -> result<_, error-code>; - /// Hints the desired listen queue size. Implementations are free to ignore this. /// /// If the provided value is 0, an `invalid-argument` error is returned. @@ -304,12 +283,16 @@ interface tcp { /// Initiate a graceful shutdown. /// - /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read - /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. - /// Any data still in the receive queue at time of calling `shutdown` will be discarded. - /// - send: the socket is not expecting to send any more data to the peer. All subsequent write - /// operations on the `output-stream` associated with this socket will return an error. - /// - both: same effect as receive & send combined. + /// - `receive`: The socket is not expecting to receive any data from + /// the peer. The `input-stream` associated with this socket will be + /// closed. Any data still in the receive queue at time of calling + /// this method will be discarded. + /// - `send`: The socket has no more data to send to the peer. The `output-stream` + /// associated with this socket will be closed and a FIN packet will be sent. + /// - `both`: Same effect as `receive` & `send` combined. + /// + /// This function is idempotent. Shutting a down a direction more than once + /// has no effect and returns `ok`. /// /// The shutdown function does not close (drop) the socket. /// diff --git a/hermes/wasm/crates/wasi/wit/deps/sockets/udp-create-socket.wit b/wasm/wasi/unsupported/deps/sockets/udp-create-socket.wit similarity index 93% rename from hermes/wasm/crates/wasi/wit/deps/sockets/udp-create-socket.wit rename to wasm/wasi/unsupported/deps/sockets/udp-create-socket.wit index cc58234d8..0482d1fe7 100644 --- a/hermes/wasm/crates/wasi/wit/deps/sockets/udp-create-socket.wit +++ b/wasm/wasi/unsupported/deps/sockets/udp-create-socket.wit @@ -6,6 +6,7 @@ interface udp-create-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. /// /// This function does not require a network capability handle. This is considered to be safe because /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, diff --git a/hermes/wasm/crates/wasi/wit/deps/sockets/udp.wit b/wasm/wasi/unsupported/deps/sockets/udp.wit similarity index 93% rename from hermes/wasm/crates/wasi/wit/deps/sockets/udp.wit rename to wasm/wasi/unsupported/deps/sockets/udp.wit index c8dafadfc..b9826a6a5 100644 --- a/hermes/wasm/crates/wasi/wit/deps/sockets/udp.wit +++ b/wasm/wasi/unsupported/deps/sockets/udp.wit @@ -1,6 +1,6 @@ interface udp { - use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; + use wasi:io/poll@0.2.0.{pollable}; use network.{network, error-code, ip-socket-address, ip-address-family}; /// A received datagram. @@ -92,7 +92,6 @@ interface udp { /// /// # Typical errors /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) /// - `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-state`: The socket is not bound. @@ -142,17 +141,6 @@ interface udp { /// Equivalent to the SO_DOMAIN socket option. address-family: func() -> ip-address-family; - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # Typical errors - /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. - /// - `invalid-state`: (set) The socket is already bound. - /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - ipv6-only: func() -> result; - set-ipv6-only: func(value: bool) -> 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. @@ -248,7 +236,6 @@ interface udp { /// /// # Typical errors /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) /// - `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 `stream`. (EISCONN) diff --git a/hermes/wasm/crates/wasi/wit/deps/sockets/world.wit b/wasm/wasi/unsupported/deps/sockets/world.wit similarity index 81% rename from hermes/wasm/crates/wasi/wit/deps/sockets/world.wit rename to wasm/wasi/unsupported/deps/sockets/world.wit index 49ad8d3d9..f8bb92ae0 100644 --- a/hermes/wasm/crates/wasi/wit/deps/sockets/world.wit +++ b/wasm/wasi/unsupported/deps/sockets/world.wit @@ -1,4 +1,4 @@ -package wasi:sockets@0.2.0-rc-2023-11-10; +package wasi:sockets@0.2.0; world imports { import instance-network; diff --git a/hermes/wasm/crates/wasi/wit/deps/cli/environment.wit b/wasm/wasi/wit/deps/cli/environment.wit similarity index 100% rename from hermes/wasm/crates/wasi/wit/deps/cli/environment.wit rename to wasm/wasi/wit/deps/cli/environment.wit diff --git a/wasm/wasi/wit/deps/cli/imports.wit b/wasm/wasi/wit/deps/cli/imports.wit new file mode 100644 index 000000000..56d820a01 --- /dev/null +++ b/wasm/wasi/wit/deps/cli/imports.wit @@ -0,0 +1,22 @@ +package wasi:cli@0.2.0; + +world imports { + include wasi:clocks/imports@0.2.0; + include wasi:filesystem/imports@0.2.0; + // include wasi:sockets/imports@0.2.0; // Sockets are not available in hermes + include wasi:random/imports@0.2.0; + include wasi:io/imports@0.2.0; + + import environment; + //import exit; + import stdin; + import stdout; + import stderr; + + // Terminal IO not supported in Hermes. + //import terminal-input; + //import terminal-output; + //import terminal-stdin; + //import terminal-stdout; + //import terminal-stderr; +} diff --git a/hermes/wasm/crates/wasi/wit/deps/cli/stdio.wit b/wasm/wasi/wit/deps/cli/stdio.wit similarity index 50% rename from hermes/wasm/crates/wasi/wit/deps/cli/stdio.wit rename to wasm/wasi/wit/deps/cli/stdio.wit index 1b653b6e2..31ef35b5a 100644 --- a/hermes/wasm/crates/wasi/wit/deps/cli/stdio.wit +++ b/wasm/wasi/wit/deps/cli/stdio.wit @@ -1,17 +1,17 @@ interface stdin { - use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream}; + use wasi:io/streams@0.2.0.{input-stream}; get-stdin: func() -> input-stream; } interface stdout { - use wasi:io/streams@0.2.0-rc-2023-11-10.{output-stream}; + use wasi:io/streams@0.2.0.{output-stream}; get-stdout: func() -> output-stream; } interface stderr { - use wasi:io/streams@0.2.0-rc-2023-11-10.{output-stream}; + use wasi:io/streams@0.2.0.{output-stream}; get-stderr: func() -> output-stream; } diff --git a/hermes/wasm/crates/wasi/wit/deps/clocks/monotonic-clock.wit b/wasm/wasi/wit/deps/clocks/monotonic-clock.wit similarity index 92% rename from hermes/wasm/crates/wasi/wit/deps/clocks/monotonic-clock.wit rename to wasm/wasi/wit/deps/clocks/monotonic-clock.wit index 09ef32c36..4dfb7d3c6 100644 --- a/hermes/wasm/crates/wasi/wit/deps/clocks/monotonic-clock.wit +++ b/wasm/wasi/wit/deps/clocks/monotonic-clock.wit @@ -1,4 +1,4 @@ -package wasi:clocks@0.2.0-rc-2023-11-10; +package wasi:clocks@0.2.0; /// WASI Monotonic Clock is a clock API intended to let users measure elapsed /// time. /// @@ -10,7 +10,8 @@ package wasi:clocks@0.2.0-rc-2023-11-10; /// /// It is intended for measuring elapsed time. interface monotonic-clock { - use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; + // Hermes does not support `poll` + //use wasi:io/poll@0.2.0.{pollable}; /// An instant in time, in nanoseconds. An instant is relative to an /// unspecified initial value, and can only be compared to instances from @@ -30,16 +31,18 @@ interface monotonic-clock { /// corresponding to a clock tick. resolution: func() -> duration; + /* /// Create a `pollable` which will resolve once the specified instant /// occured. subscribe-instant: func( when: instant, ) -> pollable; - + /// Create a `pollable` which will resolve once the given duration has /// elapsed, starting at the time at which this function was called. /// occured. subscribe-duration: func( when: duration, ) -> pollable; + */ } diff --git a/hermes/wasm/crates/wasi/wit/deps/clocks/wall-clock.wit b/wasm/wasi/wit/deps/clocks/wall-clock.wit similarity index 97% rename from hermes/wasm/crates/wasi/wit/deps/clocks/wall-clock.wit rename to wasm/wasi/wit/deps/clocks/wall-clock.wit index 8abb9a0c0..440ca0f33 100644 --- a/hermes/wasm/crates/wasi/wit/deps/clocks/wall-clock.wit +++ b/wasm/wasi/wit/deps/clocks/wall-clock.wit @@ -1,4 +1,4 @@ -package wasi:clocks@0.2.0-rc-2023-11-10; +package wasi:clocks@0.2.0; /// 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. diff --git a/hermes/wasm/crates/wasi/wit/deps/clocks/world.wit b/wasm/wasi/wit/deps/clocks/world.wit similarity index 63% rename from hermes/wasm/crates/wasi/wit/deps/clocks/world.wit rename to wasm/wasi/wit/deps/clocks/world.wit index 8fa080f0e..c0224572a 100644 --- a/hermes/wasm/crates/wasi/wit/deps/clocks/world.wit +++ b/wasm/wasi/wit/deps/clocks/world.wit @@ -1,4 +1,4 @@ -package wasi:clocks@0.2.0-rc-2023-11-10; +package wasi:clocks@0.2.0; world imports { import monotonic-clock; diff --git a/hermes/wasm/crates/wasi/wit/deps/filesystem/preopens.wit b/wasm/wasi/wit/deps/filesystem/preopens.wit similarity index 80% rename from hermes/wasm/crates/wasi/wit/deps/filesystem/preopens.wit rename to wasm/wasi/wit/deps/filesystem/preopens.wit index 95ec67843..da801f6d6 100644 --- a/hermes/wasm/crates/wasi/wit/deps/filesystem/preopens.wit +++ b/wasm/wasi/wit/deps/filesystem/preopens.wit @@ -1,4 +1,4 @@ -package wasi:filesystem@0.2.0-rc-2023-11-10; +package wasi:filesystem@0.2.0; interface preopens { use types.{descriptor}; diff --git a/hermes/wasm/crates/wasi/wit/deps/filesystem/types.wit b/wasm/wasi/wit/deps/filesystem/types.wit similarity index 99% rename from hermes/wasm/crates/wasi/wit/deps/filesystem/types.wit rename to wasm/wasi/wit/deps/filesystem/types.wit index 059722ab8..11108fcda 100644 --- a/hermes/wasm/crates/wasi/wit/deps/filesystem/types.wit +++ b/wasm/wasi/wit/deps/filesystem/types.wit @@ -1,4 +1,4 @@ -package wasi:filesystem@0.2.0-rc-2023-11-10; +package wasi:filesystem@0.2.0; /// 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. @@ -24,8 +24,8 @@ package wasi:filesystem@0.2.0-rc-2023-11-10; /// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md interface types { - use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream, error}; - use wasi:clocks/wall-clock@0.2.0-rc-2023-11-10.{datetime}; + use wasi:io/streams@0.2.0.{input-stream, output-stream, error}; + use wasi:clocks/wall-clock@0.2.0.{datetime}; /// File size or length of a region within a file. type filesize = u64; diff --git a/hermes/wasm/crates/wasi/wit/deps/filesystem/world.wit b/wasm/wasi/wit/deps/filesystem/world.wit similarity index 56% rename from hermes/wasm/crates/wasi/wit/deps/filesystem/world.wit rename to wasm/wasi/wit/deps/filesystem/world.wit index 285e0bae9..663f57920 100644 --- a/hermes/wasm/crates/wasi/wit/deps/filesystem/world.wit +++ b/wasm/wasi/wit/deps/filesystem/world.wit @@ -1,4 +1,4 @@ -package wasi:filesystem@0.2.0-rc-2023-11-10; +package wasi:filesystem@0.2.0; world imports { import types; diff --git a/wasm/wasi/wit/deps/hermes-cron/api.wit b/wasm/wasi/wit/deps/hermes-cron/api.wit new file mode 100644 index 000000000..c776d38b8 --- /dev/null +++ b/wasm/wasi/wit/deps/hermes-cron/api.wit @@ -0,0 +1,179 @@ +/// # Cron API +/// +/// Allow time based scheduling of events. +/// +/// ## Permissions +/// +/// This API is permissionless. + +// cspell: words crontabs mkcron retrigger retriggering + +/// CRON API Interface - Imports ONLY +interface api { + + /// Get the `instant` type from the `wasi:clocks` module. + use wasi:clocks/monotonic-clock@0.2.0.{instant}; + + /// A Tag used to mark a delivered cron event. + type cron-event-tag = string; + + /// A cron schedule in crontab format. + type cron-sched = string; + + /// A tagged crontab entry + /// It is valid for multiple crontab entries at the same time to have different tags. + /// It is valid for crontab entries at different times to have the same tag. + /// BUT there can only ever be 1 crontab entry at a specified time with a specified tag. + /// ie, `when` + `tag` is uniquely identifying of every crontab entry. + /// See: [crontab.5 man page](https://www.man7.org/linux/man-pages/man5/crontab.5.html) for details on cron schedule format. + record cron-tagged { + /// The crontab entry in standard cron format. + /// The Time is ALWAYS relative to UTC and does not account for local time. + /// If Localtime adjustment is required it must be handled by the module. + when: cron-sched, + + /// The tag associated with the crontab entry. + tag: cron-event-tag + } + + /// A discreet time entry used to help convert numeric times into crontab entries. + variant cron-component { + // Maps to `*` in a cron schedule (ie, match all) + all, + // Match an absolute time/date + at(u8), + // Match an inclusive list of time/date values. + range(tuple), + } + + /// A list of cron time components + type cron-components = list; + + /// The actual individual cron-time entry + enum cron-time { + cron-component, + cron-components + } + + /// # Schedule Recurrent CRON event + /// + /// Cron events will be delivered to the `on-cron` event handler. + /// + /// ## Parameters + /// + /// - `entry`: The crontab entry to add. + /// - `when`: When the event triggers. Standard crontab format. + /// - `tag`: A tag which will accompany the triggered event. + /// - `retrigger`: + /// - `true`: The event will re-trigger every time the crontab entry matches until cancelled. + /// - `false`: The event will automatically cancel after it is generated once. + /// + /// ## Returns + /// + /// - `true`: Crontab added successfully. (Or the crontab event already exists) + /// - `false`: Crontab failed to be added. + /// + /// ## Note: + /// + /// If the crontab entry already exists, the retrigger flag can be changed by calling + /// this function. This could be useful where a retriggering crontab event is desired + /// to be stopped, but ONLY after it has triggered once more. + /// + add: func(entry: cron-tagged, retrigger: bool) -> bool; + + /// # Schedule A Single cron event after a fixed delay. + /// + /// Allows for easy timed wait events to be delivered without + /// requiring datetime calculations or formatting cron entries. + /// + /// ## Parameters + /// + /// - `duration`: How many nanoseconds to delay. The delay will be AT LEAST this long. + /// - `tag`: A tag which will accompany the triggered event. + /// + /// ## Returns + /// + /// - `true`: Crontab added successfully. + /// - `false`: Crontab failed to be added. + /// + /// ## Note: + /// + /// This is a convenience function which will automatically calculate the crontab + /// entry needed to trigger the event after the requested `duration`. + /// It is added as a non-retriggering event. + /// Listing the crontabs after this call will list the delay in addition to all other + /// crontab entries. + /// + delay: func(duration: instant, tag: cron-event-tag) -> bool; + + + /// # List currently active cron schedule. + /// + /// Allows for management of scheduled cron events. + /// + /// ## Parameters + /// + /// - `tag`: Optional, the tag to limit the list to. If `none` then all crons listed. + /// + /// ## Returns + /// + /// - A list of tuples containing the scheduled crontabs and their tags, along with the current retrigger flag. + /// The list is sorted from most crontab that will trigger soonest to latest. + /// Crontabs are only listed once, in the case where a crontab may be scheduled + /// may times before a later one. + /// - `0` - `cron-tagged` - The Tagged crontab event. + /// - `1` - `bool` - The state of the retrigger flag. + /// + ls: func(tag: option) -> list>; + + /// # Remove the requested crontab. + /// + /// Allows for management of scheduled cron events. + /// + /// ## Parameters + /// + /// - `when`: The crontab entry to add. Standard crontab format. + /// - `tag`: A tag which will accompany the triggered event. + /// + /// ## Returns + /// + /// - `true`: The requested crontab was deleted and will not trigger. + /// - `false`: The requested crontab does not exist. + /// + rm: func(entry: cron-tagged) -> bool; + + /// # Make a crontab entry from individual time values. + /// + /// Crates the properly formatted cron entry + /// from numeric cron time components. + /// Convenience function to make building cron strings simpler when they are + /// calculated from data. + /// + /// ## Parameters + /// + /// - `dow` - DayOfWeek (0-7, 0 or 7 = Sunday) + /// - `month` - Month of the year (1-12, 1 = January) + /// - `day` - Day in the month (1-31) + /// - `hour` - Hour in the day (0-23) + /// - `minute` - Minute in the hour (0-59) + /// + /// ## Returns + /// + /// - A matching `cron-sched` ready for use in the cron functions above. + /// + /// ## Note: + /// No checking is done to determine if the requested date is valid. + /// If a particular component is out of its allowable range it will be silently + /// clamped within the allowable range of each parameter. + /// Redundant entries will be removed. + /// - For example specifying a `month` as `3` and `2-4` will + /// remove the individual month and only produce the range. + mkcron: func(dow: cron-time, month: cron-time, day: cron-time, + hour: cron-time, minute: cron-time ) -> cron-sched; +} + +/// World just for the Hermes 'cron' API and Event. +world cron-api { + import api; +} + \ No newline at end of file diff --git a/wasm/wasi/wit/deps/hermes-cron/event.wit b/wasm/wasi/wit/deps/hermes-cron/event.wit new file mode 100644 index 000000000..10951161a --- /dev/null +++ b/wasm/wasi/wit/deps/hermes-cron/event.wit @@ -0,0 +1,40 @@ +/// # Cron API +/// +/// Event triggered on CRON schedule. +/// +/// ## Event Scheduling +/// +/// **Guarantee**: Cron events with the same tag will be delivered and executed in the order +/// they occur. +/// +/// **Guarantee**: Later cron events with the same tag will not begin processing until the +/// previous cron event with that tag has been fully processed by all processors of the event. +/// +/// **Warning**: Events with different tags can arrive out of sequence with respect to each other. +/// Sequence is only guaranteed by the tag. + +/// CRON API Interface - Export ONLY +interface event { + use api.{cron-event-tag, cron-tagged}; + + /// Triggered when a cron event fires. + /// + /// This event is only ever generated for the application that added + /// the cron job. + /// + /// The module must export this interface to use it. + /// + /// ## Parameters + /// + /// - `event` : The tagged cron event that was triggered. + /// - `last` : This cron event will not retrigger. + /// + /// Returns: + /// - `true` - retrigger. (Ignored if the cron event is `final`). + /// - `false` - stop the cron. + on-cron: func(event: cron-tagged, last: bool) -> bool; +} + +world cron-event { + export event; +} \ No newline at end of file diff --git a/wasm/wasi/wit/deps/hermes-cron/world.wit b/wasm/wasi/wit/deps/hermes-cron/world.wit new file mode 100644 index 000000000..7da22f02a --- /dev/null +++ b/wasm/wasi/wit/deps/hermes-cron/world.wit @@ -0,0 +1,6 @@ +package hermes:cron; + +world cron { + import api; + export event; +} diff --git a/hermes/wasm/crates/wasi/wit/deps/http/handler.wit b/wasm/wasi/wit/deps/http/handler.wit similarity index 100% rename from hermes/wasm/crates/wasi/wit/deps/http/handler.wit rename to wasm/wasi/wit/deps/http/handler.wit diff --git a/hermes/wasm/crates/wasi/wit/deps/http/proxy.wit b/wasm/wasi/wit/deps/http/proxy.wit similarity index 81% rename from hermes/wasm/crates/wasi/wit/deps/http/proxy.wit rename to wasm/wasi/wit/deps/http/proxy.wit index 0f466c93c..687c24d23 100644 --- a/hermes/wasm/crates/wasi/wit/deps/http/proxy.wit +++ b/wasm/wasi/wit/deps/http/proxy.wit @@ -1,4 +1,4 @@ -package wasi:http@0.2.0-rc-2023-12-05; +package wasi:http@0.2.0; /// The `wasi:http/proxy` world captures a widely-implementable intersection of /// hosts that includes HTTP forward and reverse proxies. Components targeting @@ -6,19 +6,19 @@ package wasi:http@0.2.0-rc-2023-12-05; /// outgoing HTTP requests. world proxy { /// HTTP proxies have access to time and randomness. - include wasi:clocks/imports@0.2.0-rc-2023-11-10; - import wasi:random/random@0.2.0-rc-2023-11-10; + include wasi:clocks/imports@0.2.0; + import wasi:random/random@0.2.0; /// 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.2.0-rc-2023-12-05; - import wasi:cli/stderr@0.2.0-rc-2023-12-05; + import wasi:cli/stdout@0.2.0; + import wasi:cli/stderr@0.2.0; /// 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.2.0-rc-2023-12-05; + import wasi:cli/stdin@0.2.0; /// This is the default handler to use when user code simply wants to make an /// HTTP request (e.g., via `fetch()`). diff --git a/hermes/wasm/crates/wasi/wit/deps/http/types.wit b/wasm/wasi/wit/deps/http/types.wit similarity index 98% rename from hermes/wasm/crates/wasi/wit/deps/http/types.wit rename to wasm/wasi/wit/deps/http/types.wit index 0f698e769..a54536d99 100644 --- a/hermes/wasm/crates/wasi/wit/deps/http/types.wit +++ b/wasm/wasi/wit/deps/http/types.wit @@ -2,10 +2,12 @@ /// HTTP Requests and Responses, both incoming and outgoing, as well as /// their headers, trailers, and bodies. interface types { - use wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10.{duration}; - use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream}; - use wasi:io/error@0.2.0-rc-2023-11-10.{error as io-error}; - use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; + use wasi:clocks/monotonic-clock@0.2.0.{duration}; + use wasi:io/streams@0.2.0.{input-stream, output-stream}; + use wasi:io/error@0.2.0.{error as io-error}; + + // Hermes doews not support `poll` + //use wasi:io/poll@0.2.0.{pollable}; /// This type corresponds to HTTP standard Methods. variant method { @@ -436,7 +438,7 @@ interface types { /// Returns a pollable which becomes ready when either the trailers have /// been received, or an error has occured. When this pollable is ready, /// the `get` method will return `some`. - subscribe: func() -> pollable; + //subscribe: func() -> pollable; // Hermes does NOT support `poll` /// Returns the contents of the trailers, or an error which occured, /// once the future is ready. @@ -545,11 +547,13 @@ interface types { /// This resource is returned by the `wasi:http/outgoing-handler` interface to /// provide the HTTP Response corresponding to the sent Request. resource future-incoming-response { + /* /// Returns a pollable which becomes ready when either the Response has /// been received, or an error has occured. When this pollable is ready, /// the `get` method will return `some`. subscribe: func() -> pollable; - + */ + /// Returns the incoming HTTP Response, or an error, once one is ready. /// /// The outer `option` represents future readiness. Users can wait on this diff --git a/hermes/wasm/crates/wasi/wit/deps/io/error.wit b/wasm/wasi/wit/deps/io/error.wit similarity index 97% rename from hermes/wasm/crates/wasi/wit/deps/io/error.wit rename to wasm/wasi/wit/deps/io/error.wit index 31918acbb..22e5b6489 100644 --- a/hermes/wasm/crates/wasi/wit/deps/io/error.wit +++ b/wasm/wasi/wit/deps/io/error.wit @@ -1,4 +1,4 @@ -package wasi:io@0.2.0-rc-2023-11-10; +package wasi:io@0.2.0; interface error { diff --git a/hermes/wasm/crates/wasi/wit/deps/io/streams.wit b/wasm/wasi/wit/deps/io/streams.wit similarity index 98% rename from hermes/wasm/crates/wasi/wit/deps/io/streams.wit rename to wasm/wasi/wit/deps/io/streams.wit index e7e1b689a..157d21ab6 100644 --- a/hermes/wasm/crates/wasi/wit/deps/io/streams.wit +++ b/wasm/wasi/wit/deps/io/streams.wit @@ -1,4 +1,4 @@ -package wasi:io@0.2.0-rc-2023-11-10; +package wasi:io@0.2.0; /// WASI I/O is an I/O abstraction API which is currently focused on providing /// stream types. @@ -7,7 +7,9 @@ package wasi:io@0.2.0-rc-2023-11-10; /// when it does, they are expected to subsume this API. interface streams { use error.{error}; - use poll.{pollable}; + + // Hermes does not support `poll` + //use poll.{pollable}; /// An error for input-stream and output-stream operations. variant stream-error { @@ -79,6 +81,7 @@ interface streams { len: u64, ) -> result; + /* /// Create a `pollable` which will resolve once either the specified stream /// has bytes available to read or the other end of the stream has been /// closed. @@ -86,6 +89,7 @@ interface streams { /// Implementations may trap if the `input-stream` is dropped before /// all derived `pollable`s created with this function are dropped. subscribe: func() -> pollable; + */ } @@ -164,6 +168,7 @@ interface streams { /// and stream is ready for writing again. blocking-flush: func() -> result<_, stream-error>; + /* /// Create a `pollable` which will resolve once the output-stream /// is ready for more writing, or an error has occured. When this /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an @@ -175,6 +180,7 @@ interface streams { /// Implementations may trap if the `output-stream` is dropped before /// all derived `pollable`s created with this function are dropped. subscribe: func() -> pollable; + */ /// Write zeroes to a stream. /// diff --git a/wasm/wasi/wit/deps/io/world.wit b/wasm/wasi/wit/deps/io/world.wit new file mode 100644 index 000000000..5347cd691 --- /dev/null +++ b/wasm/wasi/wit/deps/io/world.wit @@ -0,0 +1,6 @@ +package wasi:io@0.2.0; + +world imports { + import streams; + //import poll; +} diff --git a/hermes/wasm/crates/wasi/wit/deps/random/insecure-seed.wit b/wasm/wasi/wit/deps/random/insecure-seed.wit similarity index 96% rename from hermes/wasm/crates/wasi/wit/deps/random/insecure-seed.wit rename to wasm/wasi/wit/deps/random/insecure-seed.wit index f76e87dad..47210ac6b 100644 --- a/hermes/wasm/crates/wasi/wit/deps/random/insecure-seed.wit +++ b/wasm/wasi/wit/deps/random/insecure-seed.wit @@ -1,4 +1,4 @@ -package wasi:random@0.2.0-rc-2023-11-10; +package wasi:random@0.2.0; /// The insecure-seed interface for seeding hash-map DoS resistance. /// /// It is intended to be portable at least between Unix-family platforms and diff --git a/hermes/wasm/crates/wasi/wit/deps/random/insecure.wit b/wasm/wasi/wit/deps/random/insecure.wit similarity index 95% rename from hermes/wasm/crates/wasi/wit/deps/random/insecure.wit rename to wasm/wasi/wit/deps/random/insecure.wit index ec7b99737..c58f4ee85 100644 --- a/hermes/wasm/crates/wasi/wit/deps/random/insecure.wit +++ b/wasm/wasi/wit/deps/random/insecure.wit @@ -1,4 +1,4 @@ -package wasi:random@0.2.0-rc-2023-11-10; +package wasi:random@0.2.0; /// The insecure interface for insecure pseudo-random numbers. /// /// It is intended to be portable at least between Unix-family platforms and diff --git a/hermes/wasm/crates/wasi/wit/deps/random/random.wit b/wasm/wasi/wit/deps/random/random.wit similarity index 96% rename from hermes/wasm/crates/wasi/wit/deps/random/random.wit rename to wasm/wasi/wit/deps/random/random.wit index 7a7dfa27a..0c017f093 100644 --- a/hermes/wasm/crates/wasi/wit/deps/random/random.wit +++ b/wasm/wasi/wit/deps/random/random.wit @@ -1,4 +1,4 @@ -package wasi:random@0.2.0-rc-2023-11-10; +package wasi:random@0.2.0; /// WASI Random is a random data API. /// /// It is intended to be portable at least between Unix-family platforms and diff --git a/hermes/wasm/crates/wasi/wit/deps/random/world.wit b/wasm/wasi/wit/deps/random/world.wit similarity index 67% rename from hermes/wasm/crates/wasi/wit/deps/random/world.wit rename to wasm/wasi/wit/deps/random/world.wit index 49e5743b4..3da34914a 100644 --- a/hermes/wasm/crates/wasi/wit/deps/random/world.wit +++ b/wasm/wasi/wit/deps/random/world.wit @@ -1,4 +1,4 @@ -package wasi:random@0.2.0-rc-2023-11-10; +package wasi:random@0.2.0; world imports { import random; diff --git a/wasm/wasi/wit/hermes.wit b/wasm/wasi/wit/hermes.wit new file mode 100644 index 000000000..4de34765c --- /dev/null +++ b/wasm/wasi/wit/hermes.wit @@ -0,0 +1,44 @@ +/// This import every single API and exports every single entrypoint for a module. +/// Modules ONLY need to import API's they actually use. +/// Modules only need to implement the exports they require. +/// Take note of any permissions attached to a particular API before using it. +world hermes { + + /// All possible Imports + //import logging: hermes-api.logging + //import database: hermes-api.database + + //import monotonic-clock: wasi-clock.monotonic-clock + //import timezone: wasi-clock.timezone + //import wall-clock: wasi-clock.wall-clock + include hermes:cron/cron; + + /// All Possible Exports + //export init: hermes-event.init + //export cron-event: hermes-event.cron +} + +/* +/// World just for the Hermes `init` Event Handler. +world init { + export init: hermes-event.init +} + +/// World just for the Hermes `logging` API. +world logging { + import logging: hermes-api.logging +} + + +/// World just for the WASI clock API. +world wasi-clock { + import monotonic-clock: wasi-clock.monotonic-clock + import timezone: wasi-clock.timezone + import wall-clock: wasi-clock.wall-clock +} + +/// World for the database +world database { + import database: hermes-api.database +} +*/ \ No newline at end of file diff --git a/wasm/wasi/wit/test.wit b/wasm/wasi/wit/test.wit new file mode 100644 index 000000000..7a4a315de --- /dev/null +++ b/wasm/wasi/wit/test.wit @@ -0,0 +1,16 @@ +package wasmtime:wasi; + +// only used as part of `test-programs` +world test-hermes-reactor { + include wasi:cli/imports@0.2.0; + + export add-strings: func(s: list) -> u32; + export get-strings: func() -> list; + + use wasi:io/streams@0.2.0.{output-stream}; + + export write-strings-to: func(o: output-stream) -> result; + + use wasi:filesystem/types@0.2.0.{descriptor-stat}; + export pass-an-imported-record: func(d: descriptor-stat) -> string; +}