pub mod catch_handler;
pub mod externref;
pub mod multi_value;
pub mod threads;
use walrus::ir::{Try, TryTable, Visitor};
pub fn has_local_exception_tags(module: &walrus::Module) -> bool {
module
.tags
.iter()
.any(|t| matches!(t.kind, walrus::TagKind::Local))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExceptionHandlingVersion {
None,
Legacy,
Modern,
}
pub fn detect_exception_handling_version(module: &walrus::Module) -> ExceptionHandlingVersion {
struct EhDetector {
has_try: bool,
has_try_table: bool,
}
impl<'instr> Visitor<'instr> for EhDetector {
fn visit_try(&mut self, _: &Try) {
self.has_try = true;
}
fn visit_try_table(&mut self, _: &TryTable) {
self.has_try_table = true;
}
}
let mut detector = EhDetector {
has_try: false,
has_try_table: false,
};
for func in module.funcs.iter() {
if let walrus::FunctionKind::Local(local) = &func.kind {
walrus::ir::dfs_in_order(&mut detector, local, local.entry_block());
}
if detector.has_try && detector.has_try_table {
break;
}
}
match (detector.has_try_table, detector.has_try) {
(true, _) => ExceptionHandlingVersion::Modern,
(false, true) => ExceptionHandlingVersion::Legacy,
(false, false) => ExceptionHandlingVersion::None,
}
}
pub(crate) fn unstart_start_function(module: &mut walrus::Module) -> bool {
let start = match module.start.take() {
Some(id) => id,
None => return false,
};
module.exports.add("__wbindgen_start", start);
true
}
#[cfg(test)]
mod tests {
use super::*;
use walrus::ModuleConfig;
fn parse_wat(wat: &str) -> walrus::Module {
let wasm = wat::parse_str(wat).unwrap();
ModuleConfig::new()
.generate_producers_section(false)
.parse(&wasm)
.unwrap()
}
#[test]
fn detect_eh_version_none() {
let wat = r#"
(module
(func $foo
i32.const 1
drop
)
(export "foo" (func $foo))
)
"#;
let module = parse_wat(wat);
assert_eq!(
detect_exception_handling_version(&module),
ExceptionHandlingVersion::None
);
}
#[test]
fn detect_eh_version_legacy() {
let wat = r#"
(module
(func $foo
try
i32.const 1
drop
catch_all
end
)
(export "foo" (func $foo))
)
"#;
let module = parse_wat(wat);
assert_eq!(
detect_exception_handling_version(&module),
ExceptionHandlingVersion::Legacy
);
}
#[test]
fn detect_eh_version_modern() {
let wat = r#"
(module
(func $foo
(block $catch
(try_table (catch_all $catch)
i32.const 1
drop
)
)
)
(export "foo" (func $foo))
)
"#;
let module = parse_wat(wat);
assert_eq!(
detect_exception_handling_version(&module),
ExceptionHandlingVersion::Modern
);
}
#[test]
fn detect_eh_version_both_prefers_modern() {
let wat = r#"
(module
(func $foo
try
i32.const 1
drop
catch_all
end
)
(func $bar
(block $catch
(try_table (catch_all $catch)
i32.const 1
drop
)
)
)
(export "foo" (func $foo))
(export "bar" (func $bar))
)
"#;
let module = parse_wat(wat);
assert_eq!(
detect_exception_handling_version(&module),
ExceptionHandlingVersion::Modern
);
}
}