Wasmex.Module (wasmex v0.9.1)

A compiled WebAssembly module.

A Wasm Module contains stateless WebAssembly code that has already been compiled and can be instantiated multiple times.

Summary

Functions

Compiles a Wasm module from it's Wasm (a .wasm file) or WAT (a .wat file) representation.

Lists all exports of a Wasm module.

Lists all imports of a WebAssembly module grouped by their module namespace.

Returns the name of the current module if a name is given.

Serializes a compiled Wasm module into a binary.

Deserializes an in-memory compiled module previously created with Wasmex.Module.serialize/1 or Wasmex.Engine::precompile_module/2.

Types

@type t() :: %Wasmex.Module{reference: reference(), resource: binary()}

Functions

Link to this function

compile(store_or_caller, bytes)

@spec compile(Wasmex.StoreOrCaller.t(), binary()) :: {:ok, t()} | {:error, binary()}

Compiles a Wasm module from it's Wasm (a .wasm file) or WAT (a .wat file) representation.

Compiled modules can be instantiated using Wasmex.start_link/1 or Instance.new/3.

Since module compilation takes time and resources but instantiation is comparatively cheap, it may be a good idea to compile a module once and instantiate it often if you want to run a Wasm binary multiple times.

Example

Read a Wasm file and compile it into a Wasm module. Use the compiled module to start a running Wasmex.Instance.

iex> {:ok, store} = Wasmex.Store.new()
iex> {:ok, module} = Wasmex.Module.compile(store, File.read!(TestHelper.wasm_test_file_path()))
iex> {:ok, _pid} = Wasmex.start_link(%{store: store, module: module})

Modules can be compiled from WAT (WebAssembly Text) format:

iex> wat = "(module)" # minimal and not very useful
iex> {:ok, store} = Wasmex.Store.new()
iex> {:ok, %Wasmex.Module{}} = Wasmex.Module.compile(store, wat)
Link to this function

exports(module)

@spec exports(t()) :: %{required(String.t()) => any()}

Lists all exports of a Wasm module.

Returns a map which has the exports name (string) as key and export info-tuples as values. Info tuples always start with an atom indicating the exports type:

  • :fn (function)
  • :global
  • :table
  • :memory

Further parts of the info tuple vary depending on the type.

Example

List the exported function "hello_world()" of a Wasm module:

iex> {:ok, store} = Wasmex.Store.new()
iex> wat = "(module
...>          (func $helloWorld (result i32) (i32.const 42))
...>          (export \"hello_world\" (func $helloWorld))
...>        )"
iex> {:ok, module} = Wasmex.Module.compile(store, wat)
iex> Wasmex.Module.exports(module)
%{
  "hello_world" => {:fn, [], [:i32]},
}
Link to this function

imports(module)

@spec imports(t()) :: %{required(String.t()) => any()}

Lists all imports of a WebAssembly module grouped by their module namespace.

Returns a map of namespace names to namespaces with each namespace being a map again. A namespace is a map of imports with the import name as key and and info-tuple as value.

Info tuples always start with an atom indicating the imports type:

  • :fn (function)
  • :global
  • :table
  • :memory

Further parts of the info tuple vary depending on the type.

Example

Show that the Wasm module imports a function "inspect" from the "IO" namespace:

iex> {:ok, store} = Wasmex.Store.new()
iex> wat = "(module
...>          (import \"IO\" \"inspect\" (func $log (param i32)))
...>        )"
iex> {:ok, module} = Wasmex.Module.compile(store, wat)
iex> Wasmex.Module.imports(module)
%{
  "IO" => %{
    "inspect" => {:fn, [:i32], []},
  }
}
@spec name(t()) :: binary() | nil

Returns the name of the current module if a name is given.

This name is normally set in the Wasm bytecode by some compilers.

Example

iex> {:ok, store} = Wasmex.Store.new()
iex> wat = "(module $hiFromTheDocs)" # minimal and not very useful Wasm module
iex> {:ok, module} = Wasmex.Module.compile(store, wat)
iex> Wasmex.Module.name(module)
"hiFromTheDocs"
Link to this function

serialize(module)

@spec serialize(t()) :: {:ok, binary()} | {:error, binary()}

Serializes a compiled Wasm module into a binary.

The generated binary can be deserialized back into a module using unsafe_deserialize/1. It is unsafe do alter the binary in any way. See unsafe_deserialize/1 for safety considerations.

Example

Serializes a compiled module:

iex> {:ok, store} = Wasmex.Store.new()
iex> {:ok, module} = Wasmex.Module.compile(store, File.read!(TestHelper.wasm_test_file_path()))
iex> {:ok, serialized} = Wasmex.Module.serialize(module)
iex> is_binary(serialized)
true
Link to this function

unsafe_deserialize(bytes, engine \\ nil)

@spec unsafe_deserialize(binary(), Wasmex.Engine.t() | nil) ::
  {:ok, t()} | {:error, binary()}

Deserializes an in-memory compiled module previously created with Wasmex.Module.serialize/1 or Wasmex.Engine::precompile_module/2.

This function will deserialize the binary blobs emitted by Wasmex.Module.serialize/1 and Wasmex.Engine::precompile_module/2 back into an in-memory Wasmex.Module that's ready to be instantiated.

Unsafety

This function is marked as unsafe because if fed invalid input or used improperly this could lead to memory safety vulnerabilities. This method should not, for example, be exposed to arbitrary user input.

The structure of the binary blob read here is only lightly validated internally in wasmtime. This is intended to be an efficient "rehydration" for a Wasmex.Module which has very few runtime checks beyond deserialization. Arbitrary input could, for example, replace valid compiled code with any other valid compiled code, meaning that this can trivially be used to execute arbitrary code otherwise.

For these reasons this function is unsafe. This function is only designed to receive the previous input from Wasmex.Module.serialize/1 and Wasmex.Engine::precompile_module/2. If the exact output of those functions (unmodified) is passed to this function then calls to this function can be considered safe. It is the caller's responsibility to provide the guarantee that only previously-serialized bytes are being passed in here.

Note that this function is designed to be safe receiving output from any compiled version of wasmtime itself. This means that it is safe to feed output from older versions of Wasmtime into this function, in addition to newer versions of wasmtime (from the future!). These inputs will deterministically and safely produce an error. This function only successfully accepts inputs from the same version of wasmtime. (this means that if you cache blobs across versions of wasmtime you can be safely guaranteed that future versions of wasmtime will reject old cache entries).

It is best to deserialize modules using an Engine with the same configuration as the one used to serialize/precompile it.

Example

Serializes a compiled module and deserializes it again:

iex> {:ok, store} = Wasmex.Store.new()
iex> {:ok, module} = Wasmex.Module.compile(store, File.read!(TestHelper.wasm_test_file_path()))
iex> {:ok, serialized} = Wasmex.Module.serialize(module)
iex> {:ok, %Wasmex.Module{}} = Wasmex.Module.unsafe_deserialize(serialized)