bytecodealliance / wasmtime-cpp

Apache License 2.0
86 stars 18 forks source link

How to load WASM file? #29

Closed lykhouzov closed 2 years ago

lykhouzov commented 2 years ago

All examples use WAT files. is there a way to load a binary WASM file? I am a very newbie in c++, so maybe it is obvious how to do it, but I am getting errors

I changed readFile function from an example

#include <fstream>
#include <iostream>
#include <sstream>
#include <wasmtime.hh>

using namespace wasmtime;
std::vector<uint8_t> readWasmFile(const char *filename)
{
    std::ifstream file(filename, std::ios::in | std::ios::binary);

    std::vector<uint8_t> vec;
    vec.insert(vec.begin(),
               std::istream_iterator<uint8_t>(file),
               std::istream_iterator<uint8_t>());
    return vec;
}
in main(){
    auto buf = readWasmFile("examples/rust_wasm_app.wasm");
    auto module = Module::compile(engine, buf).unwrap();
...
}

it gives me an error while when I convert that wasm to wat and use the original function it works.

Could you add an example of loading wasm file or help me properly load it in my example?

alexcrichton commented 2 years ago

What you wrote should work, and compilation works with the *.wasm binary format. Can you clarify what error you were seeing? I don't know, for example, if the code you have is the corect way to read a file in C++.

lykhouzov commented 2 years ago

here is a main.cpp file

#include <fstream>
#include <iostream>
#include <sstream>
#include <wasmtime.hh>
#include <Player.h>
using namespace wasmtime;
std::vector<uint8_t> readWasmFile(const char *filename)
{
    std::ifstream file(filename, std::ios::in | std::ios::binary);

    std::vector<uint8_t> vec;
    vec.insert(vec.begin(),
               std::istream_iterator<uint8_t>(file),
               std::istream_iterator<uint8_t>());
    return vec;
}

std::string readWatFile(const char *name)
{
    std::ifstream watFile;
    watFile.open(name);
    std::stringstream strStream;
    strStream << watFile.rdbuf();
    return strStream.str();
}

class Wasm_Script
{
public:
    Wasm_Script()
    {
    }
    void OnLogin(Player *player)
    {
        std::cout << "Run OnLogin method of Wasm_Script\n";

        // First the wasm module needs to be compiled. This is done with a global
        // "compilation environment" within an `Engine`. Note that engines can be
        // further configured through `Config` if desired instead of using the
        // default like this is here.
        std::cout << "Compiling module\n";
        Engine engine;
        // auto buf = readWatFile("examples/rust_wasm_app.wat");
        auto buf = readWasmFile("examples/rust_wasm_app.wasm");
        // auto buf = readWatFile("examples/hello.wat");
        auto module = Module::compile(engine, buf).unwrap();

        // After a module is compiled we create a `Store` which will contain
        // instantiated modules and other items like host functions. A Store
        // contains an arbitrary piece of host information, and we use `MyState`
        // here.
        std::cout << "Initializing...\n";
        Store store(engine);

        // Our wasm module we'll be instantiating requires one imported function.
        // the function takes no parameters and returns no results. We create a host
        // implementation of that function here.
        std::cout << "Creating callback...\n";
        Func host_func = Func::wrap(store, []()
                                    { std::cout << "Calling back...\n"; });

        // Once we've got that all set up we can then move to the instantiation
        // phase, pairing together a compiled module as well as a set of imports.
        // Note that this is where the wasm `start` function, if any, would run.
        std::cout << "Instantiating module...\n";
        auto instance = Instance::create(store, module, {host_func}).unwrap();

        // Next we poke around a bit to extract the `run` function from the module.
        std::cout << "Extracting export...\n";
        auto run = std::get<Func>(*instance.get(store, "run"));

        // And last but not least we can call it!
        std::cout << "Calling export...\n";
        run.call(store, {}).unwrap();

        std::cout << "Done\n";
    }
};
int main()
{
    auto player = new Player("test");
    auto script = new Wasm_Script();
    script->OnLogin(player);
    return 0;
}

here is a rust lib compiled to webassemby with cargo build --target wasm32-wasi --release command

#[repr(C)]
pub struct Player {}
extern "C" {
    fn host_func();
}

#[no_mangle]
pub extern "C" fn run() -> i32 {
    unsafe { host_func() };
    42
}

here is a wat file which is made from wasm

(module
  (type $t0 (func))
  (type $t1 (func (result i32)))
  (import "env" "host_func" (func $host_func (type $t0)))
  (func $run (type $t1) (result i32)
    call $host_func
    i32.const 42)
  (func $dummy (type $t0))
  (func $__wasm_call_dtors (type $t0)
    call $dummy
    call $dummy)
  (func $run.command_export (type $t1) (result i32)
    call $run
    call $__wasm_call_dtors)
  (table $T0 1 1 funcref)
  (memory $memory 16)
  (global $g0 (mut i32) (i32.const 1048576))
  (global $__heap_base i32 (i32.const 1048576))
  (global $__data_end i32 (i32.const 1048576))
  (export "memory" (memory 0))
  (export "__heap_base" (global 1))
  (export "__data_end" (global 2))
  (export "run" (func $run.command_export)))

If I use readWasmFile with wasm file it throw an error

./main
Run OnLogin method of Wasm_Script
Compiling module
error: failed to parse WebAssembly module

Caused by:
    Invalid input WebAssembly code at offset 26: unexpected end-of-file
[1]    73349 abort      ./main

If I run it with wat file(commend readWasmFile and uncomment readWatFile) it works as expected.

lykhouzov commented 2 years ago

i've rewrite the readWasmFile function to

Span<uint8_t> readWasmFile(const char *filename)
{
    FILE *file;
    file = fopen(filename, "rb");
    if (!file)
    {
        printf("> Error loading module!\n");
        fclose(file);
    }
    fseek(file, 0L, SEEK_END);
    size_t file_size = ftell(file);
    fseek(file, 0L, SEEK_SET);
    printf("File was read...\n");
    wasm_byte_vec_t wasm_bytes;
    wasm_byte_vec_new_uninitialized(&wasm_bytes, file_size);
    if (fread(wasm_bytes.data, file_size, 1, file) != 1)
    {
        printf("> Error loading module!\n");
    }
    fclose(file);
    std::vector<uint8_t> vec;
    Span<uint8_t> raw(reinterpret_cast<uint8_t *>(wasm_bytes.data), wasm_bytes.size);
    vec.assign(raw.begin(), raw.end());
    wasm_byte_vec_delete(&wasm_bytes);
    return vec;
}

it works now. it looks like something wrong with writing the file data into a vector in that "short" way.

alexcrichton commented 2 years ago

Ok, I think the issue isn't with the bindings here so I'm going to close this in that case.

Wulfheart commented 5 months ago

@lykhouzov For me this does not work.