use crate::descriptor::{Descriptor, GenericImportKey};
use crate::interpreter::Interpreter;
use anyhow::{bail, Error};
use std::borrow::Cow;
use std::collections::hash_map::HashMap;
use walrus::{CustomSection, FunctionId, Module, TypedCustomSectionId};
#[derive(Default, Debug)]
pub struct WasmBindgenDescriptorsSection {
pub descriptors: HashMap<String, Descriptor>,
pub generic_imports: HashMap<(GenericImportKey, Descriptor), Vec<FunctionId>>,
}
pub type WasmBindgenDescriptorsSectionId = TypedCustomSectionId<WasmBindgenDescriptorsSection>;
pub fn execute(module: &mut Module) -> Result<WasmBindgenDescriptorsSectionId, Error> {
let mut section = WasmBindgenDescriptorsSection::default();
let mut interpreter = Interpreter::new(module)?;
section.execute_exports(module, &mut interpreter)?;
section.execute_generic_imports(module, &mut interpreter)?;
Ok(module.customs.add(section))
}
impl WasmBindgenDescriptorsSection {
fn execute_exports(
&mut self,
module: &mut Module,
interpreter: &mut Interpreter,
) -> Result<(), Error> {
let mut to_remove = Vec::new();
if let Some(id) = interpreter.skip_interpret() {
to_remove.push(id);
}
for export in module.exports.iter() {
let prefix = "__wbindgen_describe_";
if !export.name.starts_with(prefix) {
continue;
}
let id = match export.item {
walrus::ExportItem::Function(id) => id,
_ => panic!("{} export not a function", export.name),
};
let d = interpreter.interpret_descriptor(id, module);
let name = &export.name[prefix.len()..];
let descriptor = Descriptor::decode(d);
self.descriptors.insert(name.to_string(), descriptor);
to_remove.push(export.id());
}
for id in to_remove {
module.exports.delete(id);
}
Ok(())
}
fn execute_generic_imports(
&mut self,
module: &mut Module,
interpreter: &mut Interpreter,
) -> Result<(), Error> {
use walrus::ir::*;
let wbindgen_describe_generic_import = match interpreter.describe_generic_import_id() {
Some(i) => i,
None => return Ok(()),
};
let mut generic_funcs = Vec::new();
for (func_id, local) in module.funcs.iter_local() {
let mut find = FindDescribeGenericImport {
wbindgen_describe_generic_import,
calls: 0,
};
dfs_in_order(&mut find, local, local.entry_block());
if find.calls > 0 {
generic_funcs.push((func_id, find.calls));
}
}
for (func_id, calls) in generic_funcs {
if calls > 1 {
bail!(
"function {} contains {calls} calls to \
`__wbindgen_describe_generic_import`, but exactly one was expected. \
Each monomorphisation must live in its own `#[inline(never)]` shim; \
if two were merged into one Wasm function only the first would be \
bound. This is a wasm-bindgen bug, please report it.",
describe_func(module, func_id),
);
}
let descriptor = interpreter.interpret_descriptor(func_id, module);
let (key, descriptor) = Descriptor::decode_generic_import(descriptor);
self.generic_imports
.entry((key, descriptor))
.or_default()
.push(func_id);
}
return Ok(());
fn describe_func(module: &Module, id: FunctionId) -> String {
match &module.funcs.get(id).name {
Some(name) => format!("`{name}`"),
None => format!("#{:?}", id.index()),
}
}
struct FindDescribeGenericImport {
wbindgen_describe_generic_import: FunctionId,
calls: usize,
}
impl Visitor<'_> for FindDescribeGenericImport {
fn visit_call(&mut self, call: &Call) {
if call.func == self.wbindgen_describe_generic_import {
self.calls += 1;
}
}
fn visit_return_call(&mut self, call: &ReturnCall) {
if call.func == self.wbindgen_describe_generic_import {
self.calls += 1;
}
}
}
}
}
impl CustomSection for WasmBindgenDescriptorsSection {
fn name(&self) -> &str {
"wasm-bindgen descriptors"
}
fn data(&self, _: &walrus::IdsToIndices) -> Cow<'_, [u8]> {
panic!("shouldn't emit custom sections just yet");
}
}