Wasmex.Module (wasmex v0.8.0)

A compiled WebAssembly module.

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

Link to this section 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 a module from its binary representation.

Link to this section Types

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

Link to this section 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

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

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

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

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

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)

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

Deserializes a module from its binary representation.

This function is inherently unsafe as the provided binary

  1. is going to be deserialized directly into Rust objects
  2. contains the WASM function assembly bodies and, if intercepted, a malicious actor could inject code into executable memory.

Only pass binaries directly coming from serialize/1, never any user input. Best case is it crashing the NIF, worst case is malicious input doing... malicious things.

The deserialization must be done on the same CPU architecture as the serialization (e.g. don't serialize a x86_64-compiled module and deserialize it on ARM64).

example

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)