use std::{collections::BTreeSet, io::Result, ops::Range}; use parity_wasm::elements::{ External, ImportCountType, Instruction, Internal, Module, NameSection, ResizableLimits, }; use wasm_ast::{ builder::{Builder, TypeInfo}, node::{ AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Backward, Br, BrIf, BrTable, Call, CallIndirect, Else, Expression, Forward, Function, GetGlobal, GetLocal, If, Memorize, MemoryGrow, MemorySize, Recall, Return, Select, SetGlobal, SetLocal, Statement, Value, }, writer::{Transpiler, Writer}, }; use super::analyzer::{localize, memory}; fn aux_internal_index(internal: Internal) -> u32 { match internal { Internal::Function(v) | Internal::Table(v) | Internal::Memory(v) | Internal::Global(v) => v, } } fn new_limit_max(limits: &ResizableLimits) -> String { match limits.maximum() { Some(v) => v.to_string(), None => "0xFFFF".to_string(), } } fn write_table_init(limit: &ResizableLimits, w: Writer) -> Result<()> { let a = limit.initial(); let b = new_limit_max(limit); write!(w, "{{ min = {}, max = {}, data = {{}} }}", a, b) } fn write_memory_init(limit: &ResizableLimits, w: Writer) -> Result<()> { let a = limit.initial(); let b = new_limit_max(limit); write!(w, "rt.allocator.new({}, {})", a, b) } fn write_func_name(wasm: &Module, index: u32, offset: u32, w: Writer) -> Result<()> { let opt = wasm .names_section() .and_then(NameSection::functions) .and_then(|v| v.names().get(index)); write!(w, "FUNC_LIST")?; if let Some(name) = opt { write!(w, "--[[{}]]", name)?; } write!(w, "[{}] =", index + offset) } fn write_in_order(prefix: &str, len: u32, w: Writer) -> Result<()> { if len == 0 { return Ok(()); } write!(w, "{}_{}", prefix, 0)?; (1..len).try_for_each(|i| write!(w, ", {}_{}", prefix, i)) } fn write_f32(f: f32, w: Writer) -> Result<()> { let sign = if f.is_sign_negative() { "-" } else { "" }; if f.is_infinite() { write!(w, "{}math.huge ", sign) } else if f.is_nan() { write!(w, "{}0/0 ", sign) } else { write!(w, "{:e} ", f) } } fn write_f64(f: f64, w: Writer) -> Result<()> { let sign = if f.is_sign_negative() { "-" } else { "" }; if f.is_infinite() { write!(w, "{}math.huge ", sign) } else if f.is_nan() { write!(w, "{}0/0 ", sign) } else { write!(w, "{:e} ", f) } } fn write_list(name: &str, len: usize, w: Writer) -> Result<()> { let len = len.saturating_sub(1); write!(w, "local {} = table.create({})", name, len) } fn write_parameter_list(func: &Function, w: Writer) -> Result<()> { write!(w, "function(")?; write_in_order("param", func.num_param, w)?; write!(w, ")") } fn write_result_list(range: Range, w: Writer) -> Result<()> { if range.is_empty() { return Ok(()); } range.clone().try_for_each(|i| { if i != range.start { write!(w, ", ")?; } write!(w, "reg_{}", i) })?; write!(w, " = ") } fn write_variable_list(func: &Function, w: Writer) -> Result<()> { for data in &func.local_data { write!(w, "local ")?; write_in_order("loc", data.count(), w)?; write!(w, " = ")?; for i in 0..data.count() { if i != 0 { write!(w, ", ")?; } write!(w, "ZERO_{} ", data.value_type())?; } } if func.num_stack != 0 { write!(w, "local ")?; write_in_order("reg", func.num_stack, w)?; write!(w, " ")?; } Ok(()) } fn write_expression(code: &[Instruction], w: Writer) -> Result<()> { // FIXME: Badly generated WASM will produce the wrong constant. for inst in code { let result = match *inst { Instruction::I32Const(v) => write!(w, "{} ", v), Instruction::I64Const(v) => write!(w, "{} ", v), Instruction::F32Const(v) => write_f32(f32::from_bits(v), w), Instruction::F64Const(v) => write_f64(f64::from_bits(v), w), Instruction::GetGlobal(i) => write!(w, "GLOBAL_LIST[{}].value ", i), _ => { continue; } }; return result; } write!(w, "error(\"mundane expression\")") } fn br_target(level: usize, in_loop: bool, w: Writer) -> Result<()> { write!(w, "if desired then ")?; write!(w, "if desired == {} then ", level)?; write!(w, "desired = nil ")?; if in_loop { write!(w, "continue ")?; } write!(w, "end ")?; write!(w, "break ")?; write!(w, "end ") } #[derive(PartialEq, Eq)] enum Label { Forward, Backward, If, } #[derive(Default)] struct Visitor { label_list: Vec