use std::{
collections::HashMap,
path::{Path, PathBuf},
};
use eyre::{Result, WrapErr};
use split_point::SplitModuleIdentifier;
mod dep_graph;
mod emit;
mod graph_utils;
mod js;
mod magic_constants;
mod read;
mod reloc;
mod split_point;
mod util;
#[non_exhaustive]
pub struct Options<'a> {
pub input_wasm: &'a [u8],
pub output_dir: &'a Path,
pub main_out_path: &'a Path,
pub link_name: &'a str,
pub main_module: &'a str,
pub verbose: bool,
#[doc(hidden)]
pub strict_tests: bool,
}
impl<'wasm> Options<'wasm> {
pub fn new(input_wasm: &'wasm [u8]) -> Self {
Self {
input_wasm,
output_dir: Path::new("wasm_split"),
main_out_path: Path::new("wasm_split/main.wasm"),
link_name: "./__wasm_split.js",
main_module: "./main.js",
verbose: false,
strict_tests: false,
}
}
}
#[non_exhaustive]
pub struct SplitWasm {
pub split_modules: Vec<PathBuf>,
pub prefetch_map: HashMap<String, Vec<String>>,
}
pub fn transform(opts: Options) -> Result<SplitWasm> {
let strictness = if opts.strict_tests {
read::Strictness::IntegrationTesting
} else {
read::Strictness::Lenient
};
let module = crate::read::InputModule::parse(opts.input_wasm, strictness)?;
if opts.verbose {
module.reloc_info.print_relocs();
}
let deps = dep_graph::get_dependencies(&module)?;
let split_points = split_point::get_split_points(&module)?;
let split_program_info =
split_point::compute_split_modules(&module, &deps.graph, split_points)?;
if split_point::trace_enabled(opts.verbose) {
for (name, split_deps) in split_program_info.output_modules.iter() {
split_deps.print(format!("{:?}", name).as_str(), &module);
}
}
let link_module = opts.link_name;
let emit_state = emit::EmitState::new(
&opts,
&module,
&split_program_info,
link_module,
&deps.stub_fns,
)?;
let wasm_modules = emit::emit_modules(
&split_program_info,
&emit_state,
|output_module_index, identifier, data| {
let output_path = match identifier {
SplitModuleIdentifier::Main => opts.main_out_path.to_path_buf(),
_ => opts
.output_dir
.join(identifier.filename(output_module_index) + ".wasm"),
};
(identifier, output_path, data)
},
)?;
let js_link_module = js::link_module(opts.main_module, &split_program_info, &emit_state)?;
std::fs::create_dir_all(opts.output_dir)?;
let mut split_modules = vec![];
for (identifier, output_path, data) in wasm_modules {
std::fs::write(&output_path, &data)
.with_context(|| format!("Error emitting {:?}", identifier))?;
if !matches!(identifier, SplitModuleIdentifier::Main) {
split_modules.push(output_path);
}
}
let prefetch_map = js_link_module.emit(&opts.output_dir.join(Path::new(link_module)))?;
Ok(SplitWasm {
split_modules,
prefetch_map,
})
}