Stream: wasmtime

Topic: wasmtime-ruby


view this post on Zulip David Cristofaro (May 08 2020 at 01:22):

Hi everyone :wave:. I came here to share the Wasmtime Ruby gem I've been working on for a little while.

https://github.com/dtcristo/wasmtime-ruby

It's quite primitive at the moment, but I hope to wrap more of the Wasmtime API over time. I will aim to keep the Ruby API as close to the Wasmtime API as possible just like wasmtime-py.

I'm using the Rust embedding API directly, rather than the C API. Not sure if this is the best approach, but I'm not super comfortable with C and would rather write Rust :). But it works and the Rust support for Ruby native extensions seems good enough. I've opted for shipping pre-compiled binaries (just Linux and macOS right now) in the gem so users don't need Rust to use it.

I'm pretty excited for Interface Types support to drop and I'm following the issue closely. Supporting this will be important.

I'm keen for any feedback.

Wasmtime WebAssembly runtime in Ruby. Contribute to dtcristo/wasmtime-ruby development by creating an account on GitHub.
I'd like to open this as a sort of meta-issue about supporting WebAssembly Interface Types in the wasmtime crate and API. The current support via the crates/interface-types crate for the purpos...

view this post on Zulip David Cristofaro (May 08 2020 at 01:42):

Also, if you folks like it and once it gets more feature complete and has docs. I'd be happy to eventually migrate it to the bytecodealliance org. I've chosen the Apache-2.0 WITH LLVM-exception license for compatibility.

view this post on Zulip Alex Crichton (May 08 2020 at 15:03):

@David Cristofaro nice! At the very least we should definitely add to the wasmtime README once you feel the bindings are ready

view this post on Zulip Tomas Hromada (Oct 05 2024 at 07:50):

Hey team! I have a small function that manipulates strings in AssemblyScript, successfully built into a .wasm release package that I'm trying to call with Ruby.

However, using wasmtime-rb has been a little problematic for me. I want to pass a Ruby string in, and get a Ruby string out.

For getting the Ruby string out, I understood I need to read the instance memory bytes in the UTF-16 encoding, and whatever I return from the function I can read fine. However, I'm struggling with setting up the input correctly.

I tried to allocate my string to the instance memory (successfully), and run the function with a number pointing to the memory location (successfully) - but inside of the function it reports the AssemblyScript string as empty.

Here's the AssemblyScript:

export function testString(inputString: string): string {
  return `Str length: ${inputString.length}, string: >>${inputString}<<`;
}

And here's how I try to call it with Ruby:

require 'wasmtime'

# Helper function to read a null-terminated string from WASM memory
def read_string(memory, ptr)
  bytes = []
  loop do
    byte1 = memory.read(ptr, 1)
    byte2 = memory.read(ptr + 1, 1)
    break if byte1.nil? || byte2.nil? || (byte1.bytes.first == 0 && byte2.bytes.first == 0)
    bytes << byte1.bytes.first
    bytes << byte2.bytes.first
    ptr += 2
  end
  bytes.pack('C*').force_encoding('UTF-16LE').encode('UTF-8')
end

# Load the WebAssembly module bytes
wasm_bytes = File.read('build/release.wasm', mode: 'rb')

# Create an engine and store
engine = Wasmtime::Engine.new
store = Wasmtime::Store.new(engine)

# Compile the module
mod = Wasmtime::Module.new(engine, wasm_bytes)

# Instantiate the module with the imports
instance = Wasmtime::Instance.new(store, mod, [])

# Access the exported memory
instance_memory = instance.export('memory').to_memory

# Access the exported 'testString' function
# TODO: https://docs.wasmtime.dev/api/wasmtime/struct.ExternRef.html probably need to define the ExternRef to correctly work with host strings, which is what I want here?
test_string = instance.exports['testString'].to_func

input_string = "Hello, Wasm!"
# Write the string to WASM memory
# TODO: no idea which pointer is a good pointer; 0x00 didn't work, 0x1000 didn't work
string_ptr = 0x30
instance_memory.write(string_ptr, input_string)

p "Calling testString(#{string_ptr}, #{input_string.bytesize})"
result_ptr = test_string.call(string_ptr)

# Read the result string from WASM memory
p "Result pointer: #{result_ptr}, reading from memory"
p read_string(instance_memory, result_ptr)

I tried using custom memory instances too but that also didn't help. I read through the wasmtime-rb examples multiple times but still don't get how to pass in the string (or, probably any other more complex object) correctly.

Any tips appreciated! Thank you

view this post on Zulip Jeff Charles (Oct 07 2024 at 18:39):

Are you familiar with the memory layout used by classes in AssemblyScript? Also are you calling a function within the Wasm instance to allocate the memory? The code sample above looks to me like it's using an arbitrary index in linear memory.


Last updated: Oct 23 2024 at 20:03 UTC