Stream: wit-bindgen

Topic: [rust] wasmtime & custom wit type (record)


view this post on Zulip yonil (Feb 22 2023 at 00:22):

Hi,

i'm having difficulty troubleshooting the following case:

i have a simple WIT definition shared between a basic guest and a basic host.

WIT:

default world demo {
  record polarity-scores {
    positive: float64,
    negative: float64,
    neutral: float64,
    compound: float64
  }

  export sentiment: func(input:list<string>) -> list<polarity-scores>
}

the guest code compiles successfully:

wit_bindgen::generate!({
    world: "demo",
    path: "./wit",
});

struct DemoImpl;

impl Demo for DemoImpl {
    fn sentiment(input: Vec<String>) -> Vec<Polarityscores> {
        lazy_static::lazy_static! {
            static ref ANALYZER: vader_sentiment::SentimentIntensityAnalyzer<'static> =
                vader_sentiment::SentimentIntensityAnalyzer::new();
        }

        input
            .iter()
            .map(|s| {
                let scores = ANALYZER.polarity_scores(s.as_str());
                Polarityscores {
                    positive: scores["pos"],
                    negative: scores["neg"],
                    neutral: scores["neu"],
                    compound: scores["compound"],
                }
            })
            .collect()
    }
}

export_demo!(DemoImpl);

and the host code compiles successfully:

use anyhow::Result;
use host::{add_to_linker, WasiCtx};
use stopwatch::Stopwatch;
use wasi_cap_std_sync::WasiCtxBuilder;
use wasmtime::{
    component::{Component, Linker},
    Config, Engine, Store, WasmBacktraceDetails,
};

wasmtime::component::bindgen!({
    world: "demo",
    path: "../guest/wit",
    async: true,
});

#[tokio::main]
async fn main() -> Result<()> {
    println!("starting");
    let wasm = "<...>/guest.wasm";

    let mut config = Config::new();
    config.cache_config_load_default().unwrap();
    config.wasm_backtrace_details(WasmBacktraceDetails::Enable);
    config.wasm_component_model(true);
    config.async_support(true);

    let engine = Engine::new(&config).unwrap();

    let mut linker = Linker::new(&engine);
    add_to_linker(&mut linker, |x| x).unwrap();

    let stopwatch = Stopwatch::start_new();
    println!("calling Component::from_file()");
    let component = Component::from_file(&engine, wasm).unwrap(); // <--- this fails :-(
    println!(
        "completed Component::from_file() in {} millis",
        stopwatch.elapsed().as_millis()
    );

    let comments = vec![
        "I love rust!",
        "ham is not a good sandwich",
        "do you think we should go to the store today?",
        "meetings are the favorite part of my day",
        "bob is your uncle",
        "I really hate the beach on a rainy day...",
        "hello bob! you are looking great today!",
    ];

    let occurrences = 1;
    let mut input1 = Vec::with_capacity(occurrences * comments.len());
    for _i in 0..occurrences {
        for comment in comments.iter() {
            input1.push(comment.to_string());
        }
    }

    let iterations = 1;
    for _i in 0..iterations {
        let stopwatch = Stopwatch::start_new();
        let mut store = Store::new(
            &engine,
            WasiCtxBuilder::new()
                .inherit_stdin()
                .inherit_stdout()
                .build(),
        );

        let (_demo, instance) = Demo::instantiate_async(&mut store, &component, &linker)
            .await
            .unwrap();

        let func = instance
            .get_typed_func::<(&[String],), (Vec<PolarityScores>,), &mut Store<WasiCtx>>(
                &mut store,
                "sentiment",
            )?;

        let (results,) = func.call_async(&mut store, (input1.as_slice(),)).await?;

        println!(
            "sentiment(): processed {} values in {} millis",
            input1.len(),
            stopwatch.elapsed().as_millis(),
        );

        for (i, r) in results.iter().enumerate() {
            println!("input: '{}', output: '{:?}'", input1[i], r);
        }
    }

    Ok(())
}
[package]
name = "host"
version = "0.1.0"
edition = "2021"

[dependencies]
stopwatch = "0.0.7"
anyhow = "1.0.69"
tokio = { version = "1.25", features = ["full"] }
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", features = ["component-model"], branch = 'release-6.0.0' }
host = { git = "https://github.com/bytecodealliance/preview2-prototyping" }
wasi-cap-std-sync = { git = "https://github.com/bytecodealliance/preview2-prototyping" }

however when running the host it fails to load the component with the following error:

thread 'main' panicked at 'index out of bounds: the len is 9 but the index is 9', <...>\.cargo\git\checkouts\wasmtime-41807828cb3a7a7e\f3dfad2\crates\environ\src\component\types.rs:425:9

if i change the exported function (including guest implementation) to return a simple type (e.g. float64 instead of the record polarity-scores) everything works as expected.

any ideas why my record definition is causing issues?

view this post on Zulip Ramon Klass (Feb 22 2023 at 00:31):

https://bytecodealliance.zulipchat.com/#narrow/stream/206238-general/topic/Guest.20missing.20imports/near/329245992

I think this is a case of incomplete and buggy, the prototyping repo is really only intended for people who want to dig in the code and implement all the missing parts, it's not ready for end use

view this post on Zulip Alex Crichton (Feb 22 2023 at 00:53):

I think you're running into a bug fixed in https://github.com/bytecodealliance/wasmtime/pull/5777, can you try the main branch of Wasmtime?

This commit fixes a panic related to type imports where an import of a type didn't correctly declare the new type index on the Wasmtime side of things. Additionally this plumbs more support through...

view this post on Zulip bachrc (Feb 22 2023 at 00:56):

This is actually not possible to get wasmtime from main, and pulling host on the prototype :(

view this post on Zulip Alex Crichton (Feb 22 2023 at 00:57):

yep, it'll require editing code as opposed to using everything as-is

view this post on Zulip yonil (Feb 22 2023 at 02:16):

for my current needs, i can workaround this by using basic types instead of a record.
looking forward to leveraging the fix you mentioned in the future

(BTW - is there any estimated timeline or plan you can link to, for getting the officially released wit-bindgen& wasmtime crates in crates.io, with this scenario being functional?)

view this post on Zulip bachrc (Feb 22 2023 at 17:47):

Alex Crichton said:

yep, it'll require editing code as opposed to using everything as-is

Are you talking about forking yhe repo? Because for now, to make a WASI Component work, there is no other way than to import the host from preview prototyping?

view this post on Zulip Ramon Klass (Feb 22 2023 at 18:10):

you can depend on a local checkout and edit around in there, you only need a fork at the point where you might want to make a PR with your changes


Last updated: Dec 23 2024 at 13:07 UTC