Current section
Files
Jump to
Current section
Files
lib/execution/module_instance.ex
defmodule WaspVM.ModuleInstance do
alias WaspVM.ModuleInstance
alias WaspVM.Module
alias WaspVM.Memory
alias WaspVM.Store
require IEx
defstruct ref: nil,
funcaddrs: [],
tableaddrs: [],
memaddrs: [],
globaladdrs: [],
exports: [],
types: [],
resolved_imports: %{}
@moduledoc false
# Module instantiation as described at
# https://webassembly.github.io/spec/core/exec/modules.html#alloc-module
@spec new :: ModuleInstance
def new, do: %ModuleInstance{ref: make_ref()}
@spec instantiate(ModuleInstance, Module, Store) :: {ModuleInstance, Store}
def instantiate(instance, module, store) do
{memaddrs, store} =
module.memory
|> initialize_memories()
|> Enum.map_reduce(store, fn mem, s ->
# Initialize data segments into memory. We'll need to update this
# post-MVP, once module can have more than one memory.
mem = Enum.reduce(module.data, mem, fn segment, acc ->
Memory.put_at(acc, segment.offset, segment.data)
end)
{:ok, addr, updated_s} = Store.allocate_memory(s, mem)
{addr, updated_s}
end)
{funcaddrs, store} =
module
|> initialize_funcs(instance.ref)
|> Enum.map_reduce(store, fn func, s ->
{:ok, addr, updated_s} = Store.allocate_func(s, func)
{addr, updated_s}
end)
funcaddrs =
funcaddrs
|> Enum.with_index()
|> Enum.map(fn {k, v} -> {v, k} end)
|> Map.new()
{globaladdrs, store} =
Enum.map_reduce(module.globals, store, fn glob, s ->
{:ok, addr, updated_s} = Store.allocate_global(s, glob)
{addr, updated_s}
end)
# TODO: Implement table initialization
instance = Map.merge(instance, %{
memaddrs: memaddrs,
funcaddrs: funcaddrs,
globaladdrs: globaladdrs,
types: module.types,
resolved_imports: module.resolved_imports
})
# Exports need to happen after everything else is initialized
exports = Enum.map(module.exports, & generate_export(instance, &1))
instance = Map.put(instance, :exports, exports)
{instance, store}
end
@doc """
Initialize a new memory. Module can only define 1 memory in the MVP
so we're creating a list with 1 item, but in the future this may grow
"""
@spec initialize_memories(map | nil) :: list
def initialize_memories(nil), do: []
def initialize_memories(memory), do: [Memory.new(memory)]
@doc """
Initialize all function instances defined in a given module
"""
@spec initialize_funcs(Module, reference) :: list
def initialize_funcs(module, ref) do
host_funcs =
module.imports
|> Enum.filter(& &1.type == :typeidx)
|> Enum.sort(& &1.index <= &2.index)
|> Enum.map(fn imp ->
type = Enum.at(module.types, imp.index)
{:hostfunc, type, imp.module, imp.field, ref}
end)
funcs =
module.functions
|> Enum.with_index()
|> Enum.map(fn {func, idx} ->
typeidx = Enum.at(module.function_types, idx)
type = Enum.at(module.types, typeidx)
locals = Enum.flat_map(func.locals, & List.duplicate(0, &1.count))
{type, ref, func.body, locals}
end)
host_funcs ++ funcs
end
def generate_export(moduleinst, %{kind: :func, index: index, name: name}) do
%{^index => addr} = moduleinst.funcaddrs
{:func, name, addr}
end
def generate_export(moduleinst, %{kind: :mem, index: index, name: name}) do
addr = Enum.at(moduleinst.memaddrs, index)
{:mem, name, addr}
end
def generate_export(_moduleinst, %{kind: kind, index: _index, name: _name}) do
raise "Export not handled for kind: #{kind}"
end
end