Skip to content

Comments

wasmtime-cli: support run --invoke for components using wave#10054

Merged
pchickey merged 9 commits intomainfrom
pch/invoke_wave
Apr 11, 2025
Merged

wasmtime-cli: support run --invoke for components using wave#10054
pchickey merged 9 commits intomainfrom
pch/invoke_wave

Conversation

@pchickey
Copy link
Contributor

@pchickey pchickey commented Jan 20, 2025

This PR adds support for wasmtime run --invoke <wave-function-call> component.wasm to wasmtime-cli. In the existing invoke for modules, the user provides a bare function name, and only functions that do not take arguments are supported. For components, the argument to invoke is parsed into component vals using the existing wasm-wave parsing introduced in #8872. We make some effort to provide useful error messages when we can't parse the wave function call.

@github-actions github-actions bot added the wasmtime:api Related to the API of the `wasmtime` crate itself label Jan 20, 2025
@tpmccallum
Copy link
Contributor

Tried this locally and got the following:

cd /Users/tpmccallum
git clone https://github.com/bytecodealliance/wasmtime.git
cd wasmtime
git checkout main
git pull origin main
git checkout pch/invoke_wave
git merge main
cargo clean
cargo build --release

Repo I used for testing is at https://github.com/tpmccallum/testing_components/tree/main (specifically the compress component):

tpmccallum@192-168-1-17 compress % /Users/tpmccallum/wasmtime/target/release/wasmtime run --invoke compress target/wasm32-wasip1/debug/compress.wasm

Error: failed to run main module `target/wasm32-wasip1/debug/compress.wasm`

Caused by:
    0: parsing invoke "compress"
    1: unexpected end of input at 8..8

@pchickey
Copy link
Contributor Author

The wave syntax requires functions to be invoked with parens, so --invoke "compress()" should hopefully work. one of the big things missing is better error messages...

@tpmccallum
Copy link
Contributor

Thanks for the response @pchickey
That makes sense. Thank you, and I will try that next and report back here.

@tpmccallum
Copy link
Contributor

tpmccallum commented Mar 3, 2025

Hi @pchickey
Thanks for the additional info. I got this working - and wrote an article.
Thanks again!
Let me know if you need/want me to do any further testing on this. Super happy to assist.

@tpmccallum
Copy link
Contributor

tpmccallum commented Apr 1, 2025

Hi @pchickey,
I have implemented the error message, tested out a bunch of scenarios and also updated the documentation.
Please see the details below.

Run.rs

Original/Before (Original error message)

An example of a user typing --invoke "compress" (with quotes).

Command:

$ /Users/tpmccallum/test_wasmtime/wasmtime/target/release/wasmtime run --invoke "compress" /Users/tpmccallum/testing_components/compress/target/wasm32-wasip1/debug/compress.wasm  

Output:

Error: failed to run main module `/Users/tpmccallum/testing_components/compress/target/wasm32-wasip1/debug/compress.wasm`

Caused by:
    0: parsing invoke "compress"
    1: unexpected end of input at 8..8

An example of a user typing --invoke compress (no quotes):

Command:

/Users/tpmccallum/test_wasmtime/wasmtime/target/release/wasmtime run --invoke compress /Users/tpmccallum/testing_components/compress/target/wasm32-wasip1/debug/compress.wasm  

Output:

Error: failed to run main module `/Users/tpmccallum/testing_components/compress/target/wasm32-wasip1/debug/compress.wasm`

Caused by:
    0: parsing invoke "compress"
    1: unexpected end of input at 8..8

After (Confirming new error message that provides advice about quotes and parentheses)

An example of a user typing `--invoke "compress" (with quotes).

Command:

$ /Users/tpmccallum/test_wasmtime/wasmtime/target/release/wasmtime run --invoke "compress" /Users/tpmccallum/testing_components/compress/target/wasm32-wasip1/debug/compress.wasm  

Output:

Error: failed to run main module `/Users/tpmccallum/testing_components/compress/target/wasm32-wasip1/debug/compress.wasm`

Caused by:
    0: failed to parse invoke 'compress': invoked function calls must include parentheses after the function name in quoted wave syntax (e.g., "compress()")
    1: unexpected end of input at 8..8

An example of a user typing --invoke compress (no quotes):

Command:

/Users/tpmccallum/test_wasmtime/wasmtime/target/release/wasmtime run --invoke compress /Users/tpmccallum/testing_components/compress/target/wasm32-wasip1/debug/compress.wasm  

Output:

Error: failed to run main module `/Users/tpmccallum/testing_components/compress/target/wasm32-wasip1/debug/compress.wasm`

Caused by:
    0: failed to parse invoke 'compress': invoked function calls must include parentheses after the function name in quoted wave syntax (e.g., "compress()")
    1: unexpected end of input at 8..8

After (Testing functionality of invoking a function with arguments inside the parentheses)

I also updated the Rust function to accept a string argument and then tested that the proper escaping of the string argument worked:

Command:

/Users/tpmccallum/test_wasmtime/wasmtime/target/release/wasmtime run --invoke "compress(\"hello\")" /Users/tpmccallum/testing_components/compress/target/wasm32-wasip1/debug/compress.wasm 

Output:

[40, 181, 47, 253, 0, 88, 41, 0, 0, 104, 101, 108, 108, 111]

Documentation (docs/cli-options.md)

I also went ahead and updated the documentation that talks about --invoke with all of the above information (quotes, parenthesis, etc.).

Workflow

Below is how I pushed my changes. I hope this is what you were after, if not, please let me know.

Created a new branch called invoke_wave_enhancements.

$ git branch
          invoke_wave
        * invoke_wave_enhancements
          main

$ git status
	modified:   docs/cli-options.md
	modified:   src/commands/run.rs

$ git commit -m "Finalized enhancements for --invoke: error messages"
        [invoke_wave_enhancements 4a93050af] Finalized enhancements for --invoke: error messages
        2 files changed, 37 insertions(+), 5 deletions(-)

$ git push origin invoke_wave_enhancements
        git push origin invoke_wave_enhancements
        Enumerating objects: 13, done.
        Counting objects: 100% (13/13), done.
        Delta compression using up to 16 threads
        Compressing objects: 100% (7/7), done.
        Writing objects: 100% (7/7), 1.84 KiB | 1.84 MiB/s, done.
        Total 7 (delta 6), reused 0 (delta 0), pack-reused 0
        remote: Resolving deltas: 100% (6/6), completed with 6 local objects.
        remote: 
        remote: Create a pull request for 'invoke_wave_enhancements' on GitHub by visiting:
        remote:      https://github.com/tpmccallum/wasmtime/pull/new/invoke_wave_enhancements
        remote: 
        To github.com:tpmccallum/wasmtime.git
         * [new branch]          invoke_wave_enhancements -> invoke_wave_enhancements

When creating the PR in the GitHub UI, the base repository is set to pchickey/wasmtime, but the drop-down for the base branch does not have a pch/invoke_wave option. Not sure if you want/need to push pch/invoke_wave to your fork so I can set it in the drop-down for the base?

At this stage, I used main as the base because the changed files look relevant to the --invoke work specifically. So perhaps all is fine and you will see my changes.

The PR is at tpmccallum/wasmtime:invoke_wave_enhancements.
I did not get to fixing the CI errors today, only the CLI output messages and the documentation.

Please let me know your thoughts, and I can get the CI done tomorrow. On that note, I was wondering if you wanted to rebase and bring the PR up to date with the current code base. Happy to be guided by you.

Chat soon
Thanks!

@tpmccallum
Copy link
Contributor

Update at #10511

@github-actions github-actions bot added the wasmtime:docs Issues related to Wasmtime's documentation label Apr 4, 2025
@pchickey pchickey marked this pull request as ready for review April 4, 2025 22:45
@pchickey pchickey requested review from a team as code owners April 4, 2025 22:45
@pchickey pchickey requested review from alexcrichton and removed request for a team April 4, 2025 22:45
Copy link
Contributor

@tpmccallum tpmccallum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was an excellent suggestion to use single quotes posted on the article about Invoke.
Using single quotes to surround the whole function call negates the need to escape double quotes around string arguments.

let invoke: &String = self.invoke.as_ref().unwrap();

// Check if input is wrapped in double quotes
let lacks_quotes = !invoke.starts_with('"') && !invoke.ends_with('"');
Copy link
Contributor

@tpmccallum tpmccallum Apr 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to revisit this logic (and will take some help if possible).
I discussed why Rust is not able to actually check for outer quotes in this comment .
But now that I realize we can use single quotes to surround the whole function there might be another way to attempt to detect the absence of surrounding quotes (that are passed, to Rust, from the CLI). I will try and get back to this in a few hours and do some local testing.

Long story short, if a user does not envelop the function with quotes, it trips over in the CLI, before the Rust even sees it:

$ wasmtime run --invoke compress(\"hello\") compress.wasm 

zsh: unknown file attribute: "
wasmtime run --invoke compress("hello") compress.wasm 

zsh: unknown file attribute: h

The following quoting examples do work, which is great.

$ wasmtime run --invoke "compress(\"hello\")" compress.wasm

[40, 181, 47, 253, 0, 88, 41, 0, 0, 104, 101, 108, 108, 111]
$ wasmtime run --invoke 'compress("hello")' compress.wasm

[40, 181, 47, 253, 0, 88, 41, 0, 0, 104, 101, 108, 108, 111]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updates are ready at
#10533

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some inline updates that promote the use of ' instead of ".

@tpmccallum
Copy link
Contributor

Hi @pchickey and @alexcrichton
Thanks for your patience. I have gotten around to some additional new work on a branch that handles the invoke quotes/parentheses and also takes care of the documentation about invoke's quotes and parentheses, etc.
#10533
Thanks so much.
Tim

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updates at #10533

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updates at #10533

Comment on lines 834 to 838
pub fn exports<'a>(
&'a self,
instance: Option<&'_ ComponentExportIndex>,
) -> Option<impl Iterator<Item = (&'a str, types::ComponentItem, ComponentExportIndex)> + use<'a>>
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm personally a bit hesitant to add too many flavors of accessors for exports, but would it be possible to use types::Component::exports here instead of adding these iterators?

canonicalize_nan64(val)
}

#[allow(clippy::cast_possible_truncation)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still necessary? This currently compiles on CI so the ignoring the warning may not be necessary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it is still necessary, yes.
For example, if I remove that line and then run the following:

rustfmt --edition 2021 crates/wasmtime/src/runtime/wave/core.rs
export CARGO_INCREMENTAL=0
export CARGO_PROFILE_DEV_DEBUG=0
export CARGO_PROFILE_TEST_DEBUG=0
export RUSTFLAGS="-D warnings"
export WIT_REQUIRE_SEMICOLONS=1
cargo clippy --workspace --all-targets

An error appears:

error: casting `u128` to `i64` may truncate the value
   --> crates/wasmtime/src/runtime/wave/core.rs:103:19
    |
103 |         let low = v as i64;
    |                   ^^^^^^^^
    |
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
    = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]`
help: ... or use `try_from` and handle the error accordingly
    |
103 -         let low = v as i64;
103 +         let low = i64::try_from(v);
    |

error: could not compile `wasmtime` (lib) due to 1 previous error

If I add the #[allow(clippy::cast_possible_truncation)] back on line 100 the error goes away.
I originally noticed the issue in the GitHub UI. Please see screen capture below:

Screenshot 2025-04-08 at 10 42 38

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok makes sense, I think it's because the wave feature must not be enabled on the clippy run which would explain this a well

Comment on lines +567 to +560
match matches.len() {
0 => bail!("No export named `{name}` in component."),
1 => {}
_ => bail!("Multiple exports named `{name}`: {matches:?}. FIXME: support some way to disambiguate names"),
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW I think the long string above is causing rustfmt to bail out here.

pchickey and others added 5 commits April 11, 2025 10:14
* wip

* wasmtime::component::Component: add iterator of exports

* components_rec

* exports_rec gives fully qualified name

* invoke works!!!

* code motion

* more context in errors

* fix test of invoke

* Finalized enhancements for --invoke: error messages

* Testing and documenting --invoke

* Update if else re: invoke

* Updating to fix truncation possibilities in unwrap_tuple

* Add clippy annotation to resolve CI error and leave original code (that makes 2 i64 out of 1 i128 and discards extra bits).

* Format (rustfmt --edition 2021) Rust files in this PR.

* Removing duplicate code missed from previous conflict resolution

* Add more verbose documentation

* Add more verbose documentation

---------

Co-authored-by: Pat Hickey <pat@moreproductive.org>
* Convert docs and error trapping to single quote approach

* Adjust the error message a little
@pchickey
Copy link
Contributor Author

@alexcrichton I rewrote the search for component exports to live in the wasmtime-cli and be much dumber/simpler than the borrowing version that was a method on Component.

@alexcrichton alexcrichton added this pull request to the merge queue Apr 11, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Apr 11, 2025
@pchickey pchickey added this pull request to the merge queue Apr 11, 2025
Merged via the queue into main with commit 9ca321e Apr 11, 2025
43 checks passed
@pchickey pchickey deleted the pch/invoke_wave branch April 11, 2025 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

wasmtime:api Related to the API of the `wasmtime` crate itself wasmtime:docs Issues related to Wasmtime's documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants