Skip to content

Component host functions #433

Open
Open
@jbourassa

Description

@jbourassa

Add host functions for components.

Wasmtime has 2 APIs for adding host functions to components:

  1. The typed API (e.g. func_wrap) which requires the signature be known at compile time.
  2. The dynamic API (e.g. func_new), where the params are in a &[Val] and return values are to be written to a &mut[Val].

I think (1) essentially requires a rust compiler thus out of question. We're left with (2). But how do we wrap such API in Ruby?

Option A: follow the crate's API

Params are Ruby values converted from Wasm, return values are tagged with their type:

# Imagine the following WIT definitions:

# f(param1: string) -> result<a>
#
# record a {
#   field1: u32,
#   field2: string,
# }

T = Wasmtime::Component::Type # Hypothetical API, does not exist yet

linker_istance.func_new("f") do |_store, param1|
  T::Result.wrap(
    Wasmtime::Component::Result.ok(
      T::Record.wrap(
        field1: T::U32.wrap(42),
        field2: T::String.wrap("foo"),
      )
    )
  )
end

With this approach, params are not type-checked. E.g. param1 could be of any type; whatever the caller decides to send. The host function could implement some limited type checking (limited because it can't distinguish between a u8 or u32, for example).

Option B: type the function

Similar to what's done for core Wasm functions, specify the return type when calling #func_new:

T = Wasmtime::Component::Type # Hypothetical API, does not exist yet

A = T::Record.new(
  "field1" => T::U32
  "field1" => T::String
)
ReturnType = T::Result.new(A)

linker_istance.func_new("f", ReturnType) do |_store, param1|
  record = { "field1" => 42, "field2" => "foo" }
  Wasmtime::Component::Result.ok(record)
end

This approach can be extended to support params type checking (possibly with a performance hit).

We might be able to generate those types directly by parsing a WIT file.


Both options A and B require mapping a Ruby object to a Wasm component type definition in a standalone way. By standalone I mean not using an index to point into a type in a component, i.e. not using the wasmtime::component::Type enum. The implication of this is the convert code needs to be duplicated or a new abstraction introduced.

There may be a better way. If you think of one, please share! Unless we find a better way, I suggest waiting until users really need this feature before implementing this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions