#![allow(clippy::too_many_arguments)]
use crate::common::error::ErrorGen;
use crate::emitter::memory_allocator::MemoryAllocator;
use crate::emitter::tag_handler::get_tag_for;
use crate::generator::ast::Script;
use crate::lang_features::libraries::core::utils::utils_adapter::UtilsAdapter;
use crate::lang_features::libraries::core::utils::UtilsPackage;
use crate::lang_features::libraries::core::{
LibPackage, ASSUMED_LIB_MEM_NAME, WHAMM_CORE_LIB_NAME,
};
use crate::parser::types::Location;
use crate::verifier::types::{Record, SymbolTable};
use std::collections::HashSet;
use wirm::ir::id::FunctionID;
use wirm::wasmparser::{ExternalKind, MemoryType};
use wirm::{DataType, Module};
pub fn link_core_lib(
ast: &[Script],
app_wasm: &mut Module,
core_lib: &[u8],
mem_allocator: &mut MemoryAllocator,
utils: &mut UtilsPackage,
packages: &mut [&mut dyn LibPackage],
err: &mut ErrorGen,
) -> Vec<FunctionID> {
let mut injected_funcs = vec![];
let mut used_a_pkg = false;
let mut should_use = vec![];
for package in packages.iter_mut() {
package.visit_ast(ast);
package.set_adapter_usage(package.is_used());
package.set_global_adapter_usage(package.is_used_in_global_scope());
used_a_pkg |= package.is_used();
should_use.push(package.is_used());
}
if used_a_pkg {
let core_lib = Module::parse(core_lib, false, false).unwrap();
import_lib_package(
app_wasm,
&None,
WHAMM_CORE_LIB_NAME.to_string(),
&None,
&core_lib,
utils,
)
}
for (package, is_used) in packages.iter_mut().zip(should_use.iter()) {
if *is_used {
let core_lib = Module::parse(core_lib, false, false).unwrap();
if package.import_memory() {
let lib_mem_id = import_lib_memory(
app_wasm,
&None,
WHAMM_CORE_LIB_NAME,
&None,
get_lib_mem_name(&core_lib),
None,
);
package.set_lib_mem_id(lib_mem_id);
}
package.set_instr_mem_id(mem_allocator.mem_id as i32);
import_lib_package(
app_wasm,
&None,
WHAMM_CORE_LIB_NAME.to_string(),
&None,
&core_lib,
*package,
);
injected_funcs.extend(gen_package_helpers(
app_wasm,
mem_allocator,
&utils.adapter,
*package,
err,
));
}
}
injected_funcs
}
pub fn link_user_lib(
app_wasm: &mut Module,
loc: &Option<Location>,
lib_wasm: &Module,
lib_name: String,
lib_name_import_override: &Option<String>,
used_mem: bool,
used_lib_fns: &HashSet<String>,
table: &mut SymbolTable,
) -> Vec<FunctionID> {
let added = import_lib_fn_names(
app_wasm,
loc,
&lib_name,
lib_name_import_override,
lib_wasm,
used_lib_fns,
Some(table),
);
if used_mem {
import_lib_memory(
app_wasm,
loc,
&lib_name,
lib_name_import_override,
get_lib_mem_name(lib_wasm),
Some(table),
);
}
let mut injected_funcs = vec![];
for (_, fid) in added.iter() {
injected_funcs.push(FunctionID(*fid));
}
injected_funcs
}
pub(crate) fn get_lib_mem_name<'a>(lib_wasm: &'a Module) -> &'a str {
let mut lib_mem_name = ASSUMED_LIB_MEM_NAME;
for export in lib_wasm.exports.iter() {
if export.kind == ExternalKind::Memory {
lib_mem_name = export.name.as_str();
break;
}
}
lib_mem_name
}
fn import_lib_memory(
app_wasm: &mut Module,
loc: &Option<Location>,
lib_name: &str,
lib_name_import_override: &Option<String>,
lib_mem_name: &str,
mut table: Option<&mut SymbolTable>,
) -> i32 {
let import_module_name = if let Some(name_override) = lib_name_import_override {
name_override.as_str()
} else {
lib_name
};
let id = if let Some(table) = table.as_mut() {
let Some(Record::Library { mem_id, .. }) = table.lookup_lib_mut(lib_name) else {
panic!("unexpected type");
};
match mem_id {
Some(id) => *id,
None => {
let id = import_memory(
import_module_name,
lib_mem_name,
&format!("{lib_name}_lib_mem"),
loc,
app_wasm,
);
*mem_id = Some(id);
id
}
}
} else {
let id = import_memory(
import_module_name,
ASSUMED_LIB_MEM_NAME,
&format!("{lib_name}_lib_mem"),
loc,
app_wasm,
);
id
};
id as i32
}
fn gen_package_helpers(
app_wasm: &mut Module,
mem_allocator: &mut MemoryAllocator,
utils: &UtilsAdapter,
package: &mut dyn LibPackage,
err: &mut ErrorGen,
) -> Vec<FunctionID> {
package.define_helper_funcs(utils, mem_allocator, app_wasm, err)
}
fn import_lib_package(
app_wasm: &mut Module,
loc: &Option<Location>,
lib_name: String,
lib_name_import_override: &Option<String>,
lib_wasm: &Module,
package: &mut dyn LibPackage,
) {
let added = import_lib_fn_names(
app_wasm,
loc,
&lib_name,
lib_name_import_override,
lib_wasm,
&HashSet::from_iter(package.get_fn_names().iter().cloned()),
None,
);
for (name, fid) in added.iter() {
package.add_fid_to_adapter(name.as_str(), *fid);
}
}
fn import_lib_fn_names(
app_wasm: &mut Module,
loc: &Option<Location>,
lib_name: &str,
lib_name_import_override: &Option<String>,
lib_wasm: &Module,
lib_fns: &HashSet<String>,
mut table: Option<&mut SymbolTable>,
) -> Vec<(String, u32)> {
let mut injected_fns = vec![];
for export in lib_wasm.exports.iter() {
if let ExternalKind::Func = export.kind {
if lib_fns.contains(&export.name) {
let func = lib_wasm.functions.get(FunctionID(export.index));
if let Some(ty) = lib_wasm.types.get(func.get_type_id()) {
let import_name = if let Some(name_override) = lib_name_import_override {
name_override.as_str()
} else {
lib_name
};
let fn_name = export.name.as_str();
let fid = import_func(
import_name,
fn_name,
&ty.params().clone(),
&ty.results().clone(),
loc,
app_wasm,
);
if let Some(table) = table.as_mut() {
let Some(Record::LibFn { addr, .. }) =
table.lookup_lib_fn_mut(lib_name, fn_name)
else {
panic!("unexpected type");
};
*addr = Some(fid);
}
injected_fns.push((export.name.clone(), fid));
} else {
unreachable!(
"ImportLib: Could not add function \"{}\" as application import",
export.name
);
}
}
}
}
injected_fns
}
fn import_memory(
module_name: &str,
mem_name: &str,
use_name: &str,
loc: &Option<Location>,
app_wasm: &mut Module,
) -> u32 {
let (mem_id, imp_id) = app_wasm.add_import_memory_with_tag(
module_name.to_string(),
mem_name.to_string(),
MemoryType {
memory64: false,
shared: false,
initial: 0,
maximum: None,
page_size_log2: None,
},
get_tag_for(loc),
);
app_wasm.imports.set_name(use_name.to_string(), imp_id);
*mem_id
}
pub fn import_func(
module_name: &str,
fname: &str,
params: &[DataType],
results: &[DataType],
loc: &Option<Location>,
app_wasm: &mut Module,
) -> u32 {
let ty_id = app_wasm
.types
.add_func_type_with_tag(params, results, get_tag_for(loc));
let (fid, imp_id) = app_wasm.add_import_func_with_tag(
module_name.to_string(),
fname.to_string(),
ty_id,
get_tag_for(loc),
);
app_wasm.imports.set_name(fname.to_string(), imp_id);
*fid
}