Current section
Files
Jump to
Current section
Files
lib/firebird/compiler/source_map.ex
defmodule Firebird.Compiler.SourceMap do
@moduledoc """
Source mapping for compiled WASM modules.
Tracks which Elixir functions map to which WASM exports,
enabling better error reporting and debugging.
## Usage
{:ok, source_map} = SourceMap.generate(module_ir, source_path)
# Look up Elixir source info for a WASM export
SourceMap.lookup(source_map, "fibonacci")
# => %{module: :Math, function: :fibonacci, arity: 1, source: "lib/math.ex"}
"""
alias Firebird.Compiler.IR
@type t :: %__MODULE__{
module: atom(),
source: String.t() | nil,
entries: [entry()],
compiled_at: DateTime.t()
}
@type entry :: %{
function: atom(),
arity: non_neg_integer(),
export_name: String.t(),
params: [atom()],
tail_recursive: boolean()
}
defstruct [:module, :source, :entries, :compiled_at]
@doc """
Generate a source map from a module IR.
## Parameters
- `module_ir` - The IR module
- `source` - Optional source file path
## Returns
- `{:ok, %SourceMap{}}` with mapping information
"""
@spec generate(IR.Module.t(), String.t() | nil) :: {:ok, t()}
def generate(%IR.Module{} = module_ir, source \\ nil) do
entries =
Enum.map(module_ir.functions, fn func ->
%{
function: func.name,
arity: func.arity,
export_name: Atom.to_string(func.name),
params: func.params,
tail_recursive: is_tail_loop?(func.body)
}
end)
source_map = %__MODULE__{
module: module_ir.name,
source: source,
entries: entries,
compiled_at: DateTime.utc_now()
}
{:ok, source_map}
end
@doc """
Look up source information for a WASM export name.
"""
@spec lookup(t(), String.t()) :: entry() | nil
def lookup(%__MODULE__{} = source_map, export_name) do
Enum.find(source_map.entries, fn entry ->
entry.export_name == export_name
end)
end
@doc """
List all exported function names.
"""
@spec exports(t()) :: [String.t()]
def exports(%__MODULE__{} = source_map) do
Enum.map(source_map.entries, & &1.export_name)
end
@doc """
Serialize source map to JSON-compatible format.
"""
@spec to_json(t()) :: map()
def to_json(%__MODULE__{} = sm) do
%{
"module" => Atom.to_string(sm.module),
"source" => sm.source,
"compiled_at" => DateTime.to_iso8601(sm.compiled_at),
"exports" =>
Enum.map(sm.entries, fn e ->
%{
"function" => Atom.to_string(e.function),
"arity" => e.arity,
"export_name" => e.export_name,
"params" => Enum.map(e.params, &Atom.to_string/1),
"tail_recursive" => e.tail_recursive
}
end)
}
end
defp is_tail_loop?({:tail_loop, _, _}), do: true
defp is_tail_loop?(_), do: false
end