Skip to main content

lib/quickbeam/wasm/server.ex

defmodule QuickBEAM.WASM.Server do
  @moduledoc false

  use GenServer

  defstruct [:module_ref, :instance_ref]

  def start_link(opts) do
    {gen_opts, wasm_opts} = Keyword.split(opts, [:name])
    GenServer.start_link(__MODULE__, wasm_opts, gen_opts)
  end

  @impl true
  def init(opts) do
    wasm_bytes = Keyword.fetch!(opts, :module)
    stack_size = Keyword.get(opts, :stack_size, 65_536)
    heap_size = Keyword.get(opts, :heap_size, 65_536)

    with {:ok, mod_ref} <- QuickBEAM.Native.wasm_compile(wasm_bytes),
         {:ok, inst_ref} <- QuickBEAM.Native.wasm_start(mod_ref, stack_size, heap_size) do
      {:ok, %__MODULE__{module_ref: mod_ref, instance_ref: inst_ref}}
    else
      {:error, reason} -> {:stop, reason}
    end
  end

  @impl true
  def handle_call({:call, func_name, params}, _from, state) do
    {:reply, QuickBEAM.Native.wasm_call(state.instance_ref, func_name, params), state}
  end

  def handle_call(:memory_size, _from, state) do
    {:reply, QuickBEAM.Native.wasm_memory_size(state.instance_ref), state}
  end

  def handle_call({:memory_grow, delta}, _from, state) do
    {:reply, QuickBEAM.Native.wasm_memory_grow(state.instance_ref, delta), state}
  end

  def handle_call({:read_memory, offset, length}, _from, state) do
    {:reply, QuickBEAM.Native.wasm_read_memory(state.instance_ref, offset, length), state}
  end

  def handle_call({:write_memory, offset, data}, _from, state) do
    {:reply, QuickBEAM.Native.wasm_write_memory(state.instance_ref, offset, data), state}
  end

  @impl true
  def terminate(_reason, state) do
    if state.instance_ref, do: QuickBEAM.Native.wasm_stop(state.instance_ref)
    :ok
  end
end