use std::collections::{HashMap, HashSet};
use super::types::TypeRegistry;
use crate::ir::CallLowerCtx;
use crate::ir::SymbolTable;
use crate::types::Type;
mod builtins;
mod builtins_wasip2;
mod emit;
pub(super) mod eq_helpers;
mod from_mir;
pub(super) mod hash_helpers;
mod infer;
mod slots;
pub(super) use from_mir::emit_fn_body_via_mir;
pub use from_mir::{CoverageReport, coverage_report};
use slots::SlotTable;
pub(super) struct FnMap {
pub(super) by_id: std::collections::HashMap<crate::ir::FnId, FnEntry>,
pub(super) builtins: std::collections::HashMap<String, u32>,
pub(super) effects: std::collections::HashMap<String, u32>,
pub(super) map_helpers: std::collections::HashMap<String, super::maps::MapKVHelpers>,
pub(super) list_ops: std::collections::HashMap<String, super::lists::ListOps>,
pub(super) vfl_ops: std::collections::HashMap<String, super::lists::VectorFromListOps>,
pub(super) zip_ops: std::collections::HashMap<String, u32>,
pub(super) string_split_ops: Option<super::lists::StringSplitOps>,
pub(super) eq_helpers: std::collections::HashMap<String, u32>,
pub(super) funcref_table: std::collections::HashMap<String, u32>,
pub(super) call_indirect_types: std::collections::HashMap<String, u32>,
}
impl FnMap {
pub(super) fn list_ops_lookup(&self, canonical: &str) -> Option<&super::lists::ListOps> {
if let Some(o) = self.list_ops.get(canonical) {
return Some(o);
}
let bare = super::types::strip_inner_dotted_prefixes(canonical);
if bare != canonical {
self.list_ops.get(&bare)
} else {
None
}
}
pub(super) fn map_helpers_lookup(&self, canonical: &str) -> Option<&super::maps::MapKVHelpers> {
if let Some(o) = self.map_helpers.get(canonical) {
return Some(o);
}
let bare = super::types::strip_inner_dotted_prefixes(canonical);
if bare != canonical {
self.map_helpers.get(&bare)
} else {
None
}
}
pub(super) fn vfl_ops_lookup(
&self,
canonical: &str,
) -> Option<&super::lists::VectorFromListOps> {
if let Some(o) = self.vfl_ops.get(canonical) {
return Some(o);
}
let bare = super::types::strip_inner_dotted_prefixes(canonical);
if bare != canonical {
self.vfl_ops.get(&bare)
} else {
None
}
}
pub(super) fn zip_ops_lookup(&self, canonical: &str) -> Option<u32> {
if let Some(&o) = self.zip_ops.get(canonical) {
return Some(o);
}
let bare = super::types::strip_inner_dotted_prefixes(canonical);
if bare != canonical {
self.zip_ops.get(&bare).copied()
} else {
None
}
}
}
#[derive(Clone)]
pub(super) struct FnEntry {
pub(super) wasm_idx: u32,
#[allow(dead_code)]
pub(super) return_type: String,
}
#[derive(Default)]
pub(super) struct CallerFnCollector {
pub(super) idx_by_name: HashMap<String, u32>,
pub(super) names: Vec<String>,
}
impl CallerFnCollector {
pub(super) fn register(&mut self, name: &str) -> u32 {
if let Some(&i) = self.idx_by_name.get(name) {
return i;
}
let i = self.names.len() as u32;
self.names.push(name.to_string());
self.idx_by_name.insert(name.to_string(), i);
i
}
}
pub(super) struct EmitCtx<'a> {
pub(super) fn_map: &'a FnMap,
pub(super) self_wasm_idx: u32,
pub(super) self_fn_name: &'a str,
pub(super) return_type: &'a str,
pub(super) registry: &'a TypeRegistry,
pub(super) symbol_table: &'a SymbolTable,
pub(super) resolution: Option<&'a crate::ast::FnResolution>,
pub(super) aliased_slots: &'a [bool],
#[allow(dead_code)]
pub(super) repr: &'a crate::ir::mir::MirFnRepr,
pub(super) params: &'a [(String, Type)],
pub(super) binding_names: &'a HashSet<String>,
pub(super) effect_idx_lookup: &'a HashMap<String, u32>,
pub(super) caller_fn_collector: &'a std::cell::RefCell<CallerFnCollector>,
pub(super) wasip2_lowering: Option<&'a Wasip2Lowering>,
pub(super) mir_builtins: Option<&'a [String]>,
pub(super) mir_program: Option<&'a crate::ir::mir::MirProgram>,
pub(super) int_result_raw: std::cell::Cell<bool>,
}
pub(super) struct Wasip2Lowering {
pub(super) get_stdout_fn_idx: Option<u32>,
pub(super) get_stderr_fn_idx: Option<u32>,
pub(super) blocking_write_fn_idx: Option<u32>,
pub(super) stdout_handle_global: Option<u32>,
pub(super) stderr_handle_global: Option<u32>,
pub(super) str_to_lm_fn_idx: Option<u32>,
pub(super) println_to_lm_fn_idx: Option<u32>,
pub(super) clocks_now_fn_idx: Option<u32>,
pub(super) random_u64_fn_idx: Option<u32>,
pub(super) get_arguments_fn_idx: Option<u32>,
pub(super) cabi_realloc_fn_idx: Option<u32>,
pub(super) decode_list_string_fn_idx: Option<u32>,
pub(super) get_environment_fn_idx: Option<u32>,
pub(super) env_get_lookup_fn_idx: Option<u32>,
pub(super) fmt_iso8601_fn_idx: Option<u32>,
pub(super) console_read_line_fn_idx: Option<u32>,
pub(super) time_sleep_fn_idx: Option<u32>,
pub(super) disk_exists_fn_idx: Option<u32>,
pub(super) disk_read_text_fn_idx: Option<u32>,
pub(super) disk_write_text_fn_idx: Option<u32>,
pub(super) disk_append_text_fn_idx: Option<u32>,
pub(super) disk_delete_fn_idx: Option<u32>,
pub(super) disk_delete_dir_fn_idx: Option<u32>,
pub(super) disk_make_dir_fn_idx: Option<u32>,
pub(super) disk_list_dir_fn_idx: Option<u32>,
pub(super) http_get_fn_idx: Option<u32>,
pub(super) tcp_connect_fn_idx: Option<u32>,
pub(super) tcp_close_fn_idx: Option<u32>,
pub(super) tcp_write_line_fn_idx: Option<u32>,
pub(super) tcp_read_line_fn_idx: Option<u32>,
pub(super) tcp_send_fn_idx: Option<u32>,
pub(super) tcp_ping_fn_idx: Option<u32>,
#[allow(dead_code)]
pub(super) network_handle_global: Option<u32>,
#[allow(dead_code)]
pub(super) tcp_pool_global: Option<u32>,
#[allow(dead_code)]
pub(super) tcp_next_id_global: Option<u32>,
#[allow(dead_code)]
pub(super) tcp_slot_type_idx: Option<u32>,
#[allow(dead_code)]
pub(super) tcp_pool_type_idx: Option<u32>,
}
impl<'a> EmitCtx<'a> {
pub(super) fn self_local_slot(&self, name: &str) -> Option<u32> {
self.resolution
.as_ref()
.and_then(|r| r.local_slots.get(name).copied())
.map(|s| s as u32)
}
pub(super) fn is_aliased_slot(&self, slot: u16) -> bool {
self.aliased_slots
.get(slot as usize)
.copied()
.unwrap_or(false)
}
pub(super) fn fn_param_fn_sig(&self, name: &str) -> Option<String> {
let (_, ty) = self.params.iter().find(|(n, _)| n == name)?;
let Type::Fn(args, ret, _) = ty else {
return None;
};
let (params, results) = super::types::fn_sig_wasm(args, ret, Some(self.registry)).ok()?;
Some(super::types::fn_sig_key(¶ms, &results))
}
pub(super) fn slot_is_bare(&self, slot: u32) -> bool {
self.repr.slot_is_bare(crate::ir::mir::LocalId(slot))
}
pub(super) fn slot_is_bare_carrier(&self, slot: u32) -> bool {
self.repr
.slot_is_bare_carrier(crate::ir::mir::LocalId(slot))
}
pub(super) fn callee_param_is_bare(&self, target: crate::ir::FnId, i: usize) -> bool {
self.mir_program
.and_then(|p| p.fn_by_id(target))
.is_some_and(|f| f.repr.param_is_bare(i))
}
}
impl<'a> CallLowerCtx for EmitCtx<'a> {
fn is_local_value(&self, name: &str) -> bool {
self.params.iter().any(|(n, _)| n == name) || self.binding_names.contains(name)
}
fn is_user_type(&self, name: &str) -> bool {
self.registry.records.contains_key(name)
|| self.registry.variants.contains_key(name)
|| self
.registry
.variants
.values()
.flat_map(|vs| vs.iter())
.any(|info| info.parent == name)
}
fn resolve_module_call<'b>(&self, _dotted: &'b str) -> Option<(&'b str, &'b str)> {
None
}
}