├── .gitignore ├── README.md ├── Cargo.toml ├── src ├── precedence.rs ├── expr_builder.rs ├── reorder_analysis.rs ├── main.rs └── code_builder.rs ├── LICENSE └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | /wasm 5 | /wasm-test-bed 6 | /tests 7 | /meters 8 | /game-of-life 9 | /debug.txt 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wasm-to-rust 2 | 3 | The wasm-to-rust tool allows compilation from WebAssembly to Rust source code. 4 | This is still very much a work in progress so not every WebAssembly binary successfully compiles to valid Rust source code. 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-to-rust" 3 | version = "0.1.0" 4 | authors = ["Christopher Serr "] 5 | 6 | [dependencies] 7 | parity-wasm = "0.27.2" 8 | structopt = "0.2.5" 9 | unicode-xid = "0.1.0" 10 | unidecode = "0.3.0" -------------------------------------------------------------------------------- /src/precedence.rs: -------------------------------------------------------------------------------- 1 | // Based on https://doc.rust-lang.org/reference/expressions.html#expression-precedence 2 | pub const MIN: usize = 0; 3 | pub const PATH: usize = 0; 4 | pub const METHOD_CALL: usize = 1; 5 | // pub const FIELD: usize = 2; 6 | pub const FUNCTION_CALL: usize = 3; 7 | // pub const ARRAY_INDEXING: usize = 3; 8 | // pub const QUESTION_MARK: usize = 4; 9 | pub const UNARY: usize = 5; 10 | pub const AS: usize = 6; 11 | // pub const COLON: usize = 6; 12 | pub const MUL: usize = 7; 13 | pub const DIV: usize = 7; 14 | // pub const REM: usize = 7; 15 | pub const ADD: usize = 8; 16 | pub const SUB: usize = 8; 17 | // pub const SHIFT: usize = 9; 18 | pub const BIT_AND: usize = 10; 19 | pub const BIT_XOR: usize = 11; 20 | pub const BIT_OR: usize = 12; 21 | pub const COMPARISON: usize = 13; 22 | // pub const LOGIC_AND: usize = 14; 23 | // pub const LOGIC_OR: usize = 15; 24 | pub const MAX: usize = 999; 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Christopher Serr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/expr_builder.rs: -------------------------------------------------------------------------------- 1 | use precedence; 2 | 3 | #[derive(Debug)] 4 | pub struct ExprBuilder { 5 | stack: Vec<(usize, String)>, 6 | } 7 | 8 | impl ExprBuilder { 9 | pub fn new() -> Self { 10 | Self { stack: Vec::new() } 11 | } 12 | 13 | pub fn unary(&mut self, precedence: usize, f: F) 14 | where 15 | F: FnOnce(&Formatted) -> String, 16 | { 17 | let a = self.stack.pop().unwrap(); 18 | let expr = f(&format(precedence, a, false)); 19 | self.stack.push((precedence, expr)); 20 | } 21 | 22 | pub fn unary_individual(&mut self, precedence_a: usize, precedence_result: usize, f: F) 23 | where 24 | F: FnOnce(&Formatted) -> String, 25 | { 26 | let a = self.stack.pop().unwrap(); 27 | let expr = f(&format(precedence_a, a, false)); 28 | self.stack.push((precedence_result, expr)); 29 | } 30 | 31 | pub fn binary(&mut self, precedence: usize, f: F) 32 | where 33 | F: FnOnce(&Formatted, &Formatted) -> String, 34 | { 35 | let b = self.stack.pop().unwrap(); 36 | let a = self.stack.pop().unwrap(); 37 | let expr = f(&format(precedence, a, true), &format(precedence, b, false)); 38 | self.stack.push((precedence, expr)); 39 | } 40 | 41 | pub fn binary_individual( 42 | &mut self, 43 | precedence_a: usize, 44 | precedence_b: usize, 45 | precedence_result: usize, 46 | f: F, 47 | ) where 48 | F: FnOnce(&Formatted, &Formatted) -> String, 49 | { 50 | let b = self.stack.pop().unwrap(); 51 | let a = self.stack.pop().unwrap(); 52 | let expr = f( 53 | &format(precedence_a, a, true), 54 | &format(precedence_b, b, false), 55 | ); 56 | self.stack.push((precedence_result, expr)); 57 | } 58 | 59 | pub fn binary_lr(&mut self, precedence: usize, f: F) 60 | where 61 | F: FnOnce(&Formatted, &Formatted) -> String, 62 | { 63 | let b = self.stack.pop().unwrap(); 64 | let a = self.stack.pop().unwrap(); 65 | let expr = f( 66 | &format(precedence, a, true), 67 | &format( 68 | if b.0 == precedence { 69 | precedence::MIN 70 | } else { 71 | precedence 72 | }, 73 | b, 74 | false, 75 | ), 76 | ); 77 | self.stack.push((precedence, expr)); 78 | } 79 | 80 | pub fn method_call_one_arg(&mut self, precedence: usize, f: F) 81 | where 82 | F: FnOnce(&Formatted, &str) -> String, 83 | { 84 | let (_, b) = self.stack.pop().unwrap(); 85 | let a = self.stack.pop().unwrap(); 86 | let expr = f(&format(precedence, a, false), &b); 87 | self.stack.push((precedence, expr)); 88 | } 89 | 90 | pub fn push(&mut self, v: (usize, String)) { 91 | self.stack.push(v); 92 | } 93 | 94 | pub fn pop(&mut self) -> Option<(usize, String)> { 95 | self.stack.pop() 96 | } 97 | 98 | pub fn pop_formatted(&mut self, precedence: usize) -> Option { 99 | format(precedence, self.stack.pop()?, false).into() 100 | } 101 | 102 | pub fn len(&self) -> usize { 103 | self.stack.len() 104 | } 105 | 106 | pub fn inner(&mut self) -> &mut Vec<(usize, String)> { 107 | &mut self.stack 108 | } 109 | } 110 | 111 | fn format( 112 | outer_precedence: usize, 113 | (inner_precedence, s): (usize, String), 114 | is_left: bool, 115 | ) -> Formatted { 116 | Formatted { 117 | outer_precedence, 118 | inner_precedence, 119 | is_left, 120 | s, 121 | } 122 | } 123 | 124 | pub struct Formatted { 125 | outer_precedence: usize, 126 | inner_precedence: usize, 127 | is_left: bool, 128 | s: String, 129 | } 130 | 131 | use std::fmt; 132 | 133 | impl fmt::Display for Formatted { 134 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 135 | if self.inner_precedence > self.outer_precedence 136 | || (self.inner_precedence == precedence::COMPARISON 137 | && self.outer_precedence == precedence::COMPARISON) 138 | || (self.is_left && self.outer_precedence == precedence::COMPARISON 139 | && self.inner_precedence == precedence::AS) 140 | { 141 | write!(f, "({})", self.s) 142 | } else { 143 | write!(f, "{}", self.s) 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/reorder_analysis.rs: -------------------------------------------------------------------------------- 1 | use parity_wasm::elements::{BlockType, 2 | Opcode::{self, *}, 3 | Type, 4 | TypeSection}; 5 | use {BlockKind, Function}; 6 | 7 | pub fn can_local_be_reordered( 8 | local_to_load: u32, 9 | blocks: &[BlockKind], 10 | functions: &[Function], 11 | types: &TypeSection, 12 | remaining_ops: &[Opcode], 13 | ) -> bool { 14 | let mut position_on_stack = 0isize; 15 | let mut stack_frames = blocks 16 | .iter() 17 | .map(|b| match *b { 18 | BlockKind::Function { evaluates_to_value } => (-1, evaluates_to_value, true), 19 | BlockKind::Block { ref dst_var, .. } | BlockKind::If { ref dst_var, .. } => { 20 | (-1, dst_var.is_some(), true) 21 | } 22 | | BlockKind::Loop { ref dst_var, .. } => (-1, dst_var.is_some(), false), 23 | }) 24 | .collect::>(); 25 | 26 | for opcode in remaining_ops { 27 | match *opcode { 28 | Unreachable => {} 29 | Nop => {} 30 | Block(block_type) => { 31 | let evaluates_to_value = if let BlockType::Value(_) = block_type { 32 | true 33 | } else { 34 | false 35 | }; 36 | stack_frames.push((position_on_stack, evaluates_to_value, true)); 37 | } 38 | Loop(block_type) => { 39 | let evaluates_to_value = if let BlockType::Value(_) = block_type { 40 | true 41 | } else { 42 | false 43 | }; 44 | stack_frames.push((position_on_stack, evaluates_to_value, false)); 45 | } 46 | If(block_type) => { 47 | position_on_stack -= 1; 48 | if position_on_stack < 0 { 49 | return true; 50 | } 51 | let evaluates_to_value = if let BlockType::Value(_) = block_type { 52 | true 53 | } else { 54 | false 55 | }; 56 | stack_frames.push((position_on_stack, evaluates_to_value, true)); 57 | } 58 | Else => { 59 | let &(pos, _, _) = stack_frames.last().unwrap(); 60 | position_on_stack = pos; 61 | if position_on_stack < 0 { 62 | return true; 63 | } 64 | } 65 | End => { 66 | let (pos, evaluates_to_value, _) = stack_frames.pop().unwrap(); 67 | position_on_stack = pos; 68 | if position_on_stack < 0 { 69 | return true; 70 | } 71 | if evaluates_to_value { 72 | position_on_stack += 1; 73 | } 74 | } 75 | Br(relative_depth) => { 76 | let last = stack_frames.len() - 1; 77 | let &(_, evaluates_to_value, breaks_to_value) = 78 | stack_frames.get(last - relative_depth as usize).unwrap(); 79 | 80 | if evaluates_to_value && breaks_to_value { 81 | position_on_stack -= 1; 82 | if position_on_stack < 0 { 83 | return true; 84 | } 85 | } 86 | } 87 | BrIf(relative_depth) => { 88 | position_on_stack -= 1; 89 | if position_on_stack < 0 { 90 | return true; 91 | } 92 | 93 | let last = stack_frames.len() - 1; 94 | let &(_, evaluates_to_value, breaks_to_value) = 95 | stack_frames.get(last - relative_depth as usize).unwrap(); 96 | 97 | if evaluates_to_value && breaks_to_value { 98 | position_on_stack -= 1; 99 | if position_on_stack < 0 { 100 | return true; 101 | } 102 | } 103 | } 104 | BrTable(..) => { 105 | position_on_stack -= 1; 106 | if position_on_stack < 0 { 107 | return true; 108 | } 109 | 110 | // TODO Branch with value 111 | } 112 | Return => { 113 | // TODO Weird logic 114 | if position_on_stack >= 0 { 115 | position_on_stack -= 1; 116 | if position_on_stack < 0 { 117 | return true; 118 | } 119 | } 120 | } 121 | Call(fn_index) => { 122 | let function = &functions[fn_index as usize]; 123 | let fn_type = function.ty; 124 | 125 | // Params 126 | position_on_stack -= fn_type.params().len() as isize; 127 | if position_on_stack < 0 { 128 | return true; 129 | } 130 | 131 | // Return type 132 | if fn_type.return_type().is_some() { 133 | position_on_stack += 1; 134 | } 135 | } 136 | CallIndirect(type_index, _) => { 137 | let Type::Function(ref fn_type) = types.types()[type_index as usize]; 138 | 139 | // Fn Ptr 140 | position_on_stack -= 1; 141 | if position_on_stack < 0 { 142 | return true; 143 | } 144 | 145 | // Params 146 | position_on_stack -= fn_type.params().len() as isize; 147 | if position_on_stack < 0 { 148 | return true; 149 | } 150 | 151 | // Return type 152 | if fn_type.return_type().is_some() { 153 | position_on_stack += 1; 154 | } 155 | } 156 | Drop => { 157 | position_on_stack -= 1; 158 | if position_on_stack < 0 { 159 | return true; 160 | } 161 | } 162 | Select => { 163 | position_on_stack -= 3; 164 | if position_on_stack < 0 { 165 | return true; 166 | } 167 | position_on_stack += 1; 168 | } 169 | GetLocal(_) => { 170 | // TODO We could merge the loads possibly 171 | position_on_stack += 1; 172 | } 173 | SetLocal(i) => { 174 | position_on_stack -= 1; 175 | if position_on_stack < 0 { 176 | return true; 177 | } 178 | 179 | if i == local_to_load { 180 | return false; 181 | } 182 | } 183 | TeeLocal(i) => { 184 | position_on_stack -= 1; 185 | if position_on_stack < 0 { 186 | return true; 187 | } 188 | 189 | if i == local_to_load { 190 | return false; 191 | } 192 | 193 | position_on_stack += 1; 194 | } 195 | GetGlobal(_) => { 196 | position_on_stack += 1; 197 | } 198 | SetGlobal(_) => { 199 | position_on_stack -= 1; 200 | if position_on_stack < 0 { 201 | return true; 202 | } 203 | } 204 | 205 | // 2 committing, 0 in, 0 out 206 | I32Store(..) | I64Store(..) | F32Store(..) | F64Store(..) | I32Store8(..) 207 | | I32Store16(..) | I64Store8(..) | I64Store16(..) | I64Store32(..) => { 208 | position_on_stack -= 2; 209 | if position_on_stack < 0 { 210 | return true; 211 | } 212 | } 213 | 214 | // 1 committing, 0 in, 1 out 215 | I32Load(..) | I64Load(..) | F32Load(..) | F64Load(..) | I32Load8S(..) 216 | | I32Load8U(..) | I32Load16S(..) | I32Load16U(..) | I64Load8S(..) | I64Load8U(..) 217 | | I64Load16S(..) | I64Load16U(..) | I64Load32S(..) | I64Load32U(..) 218 | | GrowMemory(..) | F32Nearest | F64Nearest => { 219 | if position_on_stack == 0 { 220 | return true; 221 | } 222 | } 223 | 224 | // 0 committing, 0 in, 1 out 225 | CurrentMemory(..) | I32Const(..) | I64Const(..) | F32Const(..) | F64Const(..) => { 226 | position_on_stack += 1; 227 | } 228 | 229 | // 0 committing, 1 in, 1 out 230 | I32Eqz | I64Eqz | I32Clz | I32Ctz | I32Popcnt | I64Clz | I64Ctz | I64Popcnt 231 | | F32Abs | F32Neg | F32Ceil | F32Floor | F32Trunc | F32Sqrt | F64Abs | F64Neg 232 | | F64Ceil | F64Floor | F64Trunc | F64Sqrt | I32WrapI64 | I32TruncSF32 233 | | I32TruncUF32 | I32TruncSF64 | I32TruncUF64 | I64ExtendSI32 | I64ExtendUI32 234 | | I64TruncSF32 | I64TruncUF32 | I64TruncSF64 | I64TruncUF64 | F32ConvertSI32 235 | | F32ConvertUI32 | F32ConvertSI64 | F32ConvertUI64 | F32DemoteF64 | F64ConvertSI32 236 | | F64ConvertUI32 | F64ConvertSI64 | F64ConvertUI64 | F64PromoteF32 237 | | I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 | F64ReinterpretI64 => {} 238 | 239 | // 0 committing, 2 in, 1 out 240 | I32Eq | I32Ne | I32LtS | I32LtU | I32GtS | I32GtU | I32LeS | I32LeU | I32GeS 241 | | I32GeU | I64Eq | I64Ne | I64LtS | I64LtU | I64GtS | I64GtU | I64LeS | I64LeU 242 | | I64GeS | I64GeU | F32Eq | F32Ne | F32Lt | F32Gt | F32Le | F32Ge | F64Eq | F64Ne 243 | | F64Lt | F64Gt | F64Le | F64Ge | I32Add | I32Sub | I32Mul | I32DivS | I32DivU 244 | | I32RemS | I32RemU | I32And | I32Or | I32Xor | I32Shl | I32ShrS | I32ShrU 245 | | I32Rotl | I32Rotr | I64Add | I64Sub | I64Mul | I64DivS | I64DivU | I64RemS 246 | | I64RemU | I64And | I64Or | I64Xor | I64Shl | I64ShrS | I64ShrU | I64Rotl 247 | | I64Rotr | F32Add | F32Sub | F32Mul | F32Div | F32Min | F32Max | F32Copysign 248 | | F64Add | F64Sub | F64Mul | F64Div | F64Min | F64Max | F64Copysign => { 249 | position_on_stack -= 1; 250 | } 251 | } 252 | } 253 | 254 | panic!("Unexpected end of code") 255 | } 256 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "ansi_term" 3 | version = "0.11.0" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | dependencies = [ 6 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 7 | ] 8 | 9 | [[package]] 10 | name = "atty" 11 | version = "0.2.8" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | dependencies = [ 14 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 15 | "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 16 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "bitflags" 21 | version = "1.0.1" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | 24 | [[package]] 25 | name = "byteorder" 26 | version = "1.2.1" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | 29 | [[package]] 30 | name = "cfg-if" 31 | version = "0.1.2" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | 34 | [[package]] 35 | name = "clap" 36 | version = "2.31.1" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | dependencies = [ 39 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 40 | "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 41 | "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 42 | "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 43 | "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 44 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 45 | "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 46 | ] 47 | 48 | [[package]] 49 | name = "fuchsia-zircon" 50 | version = "0.3.3" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | dependencies = [ 53 | "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 54 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 55 | ] 56 | 57 | [[package]] 58 | name = "fuchsia-zircon-sys" 59 | version = "0.3.3" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | 62 | [[package]] 63 | name = "libc" 64 | version = "0.2.36" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | 67 | [[package]] 68 | name = "log" 69 | version = "0.3.9" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | dependencies = [ 72 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 73 | ] 74 | 75 | [[package]] 76 | name = "log" 77 | version = "0.4.1" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | dependencies = [ 80 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 81 | ] 82 | 83 | [[package]] 84 | name = "owning_ref" 85 | version = "0.3.3" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | dependencies = [ 88 | "stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 89 | ] 90 | 91 | [[package]] 92 | name = "parity-wasm" 93 | version = "0.27.2" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | dependencies = [ 96 | "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 97 | "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 98 | "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", 99 | ] 100 | 101 | [[package]] 102 | name = "parking_lot" 103 | version = "0.5.4" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | dependencies = [ 106 | "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 107 | "parking_lot_core 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 108 | ] 109 | 110 | [[package]] 111 | name = "parking_lot_core" 112 | version = "0.2.13" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | dependencies = [ 115 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 116 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 117 | "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 118 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 119 | ] 120 | 121 | [[package]] 122 | name = "proc-macro2" 123 | version = "0.2.3" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | dependencies = [ 126 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 127 | ] 128 | 129 | [[package]] 130 | name = "quote" 131 | version = "0.4.2" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | dependencies = [ 134 | "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 135 | ] 136 | 137 | [[package]] 138 | name = "rand" 139 | version = "0.4.2" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | dependencies = [ 142 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 143 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 144 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 145 | ] 146 | 147 | [[package]] 148 | name = "redox_syscall" 149 | version = "0.1.37" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | 152 | [[package]] 153 | name = "redox_termios" 154 | version = "0.1.1" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | dependencies = [ 157 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 158 | ] 159 | 160 | [[package]] 161 | name = "smallvec" 162 | version = "0.6.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | 165 | [[package]] 166 | name = "stable_deref_trait" 167 | version = "1.0.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | 170 | [[package]] 171 | name = "strsim" 172 | version = "0.7.0" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | 175 | [[package]] 176 | name = "structopt" 177 | version = "0.2.5" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | dependencies = [ 180 | "clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)", 181 | "structopt-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 182 | ] 183 | 184 | [[package]] 185 | name = "structopt-derive" 186 | version = "0.2.5" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | dependencies = [ 189 | "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 190 | "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 191 | "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", 192 | ] 193 | 194 | [[package]] 195 | name = "syn" 196 | version = "0.12.14" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | dependencies = [ 199 | "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 200 | "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 201 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 202 | ] 203 | 204 | [[package]] 205 | name = "termion" 206 | version = "1.5.1" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | dependencies = [ 209 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 210 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 211 | "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 212 | ] 213 | 214 | [[package]] 215 | name = "textwrap" 216 | version = "0.9.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | dependencies = [ 219 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 220 | ] 221 | 222 | [[package]] 223 | name = "unicode-width" 224 | version = "0.1.4" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | 227 | [[package]] 228 | name = "unicode-xid" 229 | version = "0.1.0" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | 232 | [[package]] 233 | name = "unidecode" 234 | version = "0.3.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | 237 | [[package]] 238 | name = "vec_map" 239 | version = "0.8.0" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | 242 | [[package]] 243 | name = "wasm-to-rust" 244 | version = "0.1.0" 245 | dependencies = [ 246 | "parity-wasm 0.27.2 (registry+https://github.com/rust-lang/crates.io-index)", 247 | "structopt 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 248 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 249 | "unidecode 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 250 | ] 251 | 252 | [[package]] 253 | name = "winapi" 254 | version = "0.3.4" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | dependencies = [ 257 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 258 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 259 | ] 260 | 261 | [[package]] 262 | name = "winapi-i686-pc-windows-gnu" 263 | version = "0.4.0" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | 266 | [[package]] 267 | name = "winapi-x86_64-pc-windows-gnu" 268 | version = "0.4.0" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | 271 | [metadata] 272 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 273 | "checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4" 274 | "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" 275 | "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" 276 | "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" 277 | "checksum clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dc18f6f4005132120d9711636b32c46a233fad94df6217fa1d81c5e97a9f200" 278 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 279 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 280 | "checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121" 281 | "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" 282 | "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" 283 | "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" 284 | "checksum parity-wasm 0.27.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3c21b85fed4e8490e716b7fcb247185ec201f28845be6e749ab864401463c7" 285 | "checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd" 286 | "checksum parking_lot_core 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "538ef00b7317875071d5e00f603f24d16f0b474c1a5fc0ccb8b454ca72eafa79" 287 | "checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" 288 | "checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408" 289 | "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" 290 | "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" 291 | "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 292 | "checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9" 293 | "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" 294 | "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" 295 | "checksum structopt 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cdcd60acd187dfe9278b61fe39e5316a70131add6183c3f93cd35e7bd29034f6" 296 | "checksum structopt-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "27383e9f86153114f28a8d2f9dd07497718b249d7fe5d39130b1aa1a54fdc55b" 297 | "checksum syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8c5bc2d6ff27891209efa5f63e9de78648d7801f085e4653701a692ce938d6fd" 298 | "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" 299 | "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" 300 | "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" 301 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 302 | "checksum unidecode 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "402bb19d8e03f1d1a7450e2bd613980869438e0666331be3e073089124aa1adc" 303 | "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" 304 | "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" 305 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 306 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 307 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate parity_wasm; 2 | #[macro_use] 3 | extern crate structopt; 4 | extern crate unicode_xid; 5 | extern crate unidecode; 6 | 7 | use parity_wasm::deserialize_file; 8 | use parity_wasm::elements::{ 9 | External, FunctionType, ImportCountType, Internal, NameSection, Opcode, Section, Type, 10 | ValueType, 11 | }; 12 | use std::collections::BTreeMap; 13 | use std::fs::File; 14 | use std::io::{BufWriter, Write}; 15 | use std::path::PathBuf; 16 | use structopt::StructOpt; 17 | use unicode_xid::UnicodeXID; 18 | use unidecode::unidecode_char; 19 | 20 | mod code_builder; 21 | mod expr_builder; 22 | mod precedence; 23 | mod reorder_analysis; 24 | 25 | #[derive(StructOpt)] 26 | struct Opt { 27 | #[structopt( 28 | short = "n", 29 | long = "use-name-section", 30 | help = "Use the names in the name section for the internal function names" 31 | )] 32 | use_name_section: bool, 33 | #[structopt( 34 | short = "c", 35 | long = "public-call-indirect", 36 | help = "Make indirect calling available in the API" 37 | )] 38 | public_call_indirect: bool, 39 | #[structopt(help = "Input file", parse(from_os_str))] 40 | input: PathBuf, 41 | #[structopt( 42 | help = "Output file, stored next to wasm file if not specified", parse(from_os_str) 43 | )] 44 | output: Option, 45 | } 46 | 47 | pub struct Function<'a> { 48 | name: String, 49 | ty: &'a FunctionType, 50 | ty_index: u32, 51 | real_name: Option<&'a String>, 52 | make_public: bool, 53 | } 54 | 55 | pub struct Global<'a> { 56 | is_mutable: bool, 57 | is_pub: bool, 58 | name: String, 59 | ty: &'static str, 60 | const_expr: &'a [Opcode], 61 | } 62 | 63 | #[derive(Debug)] 64 | pub enum BlockKind { 65 | Function { 66 | evaluates_to_value: bool, 67 | }, 68 | Block { 69 | label: usize, 70 | dst_var: Option, 71 | }, 72 | If { 73 | label: usize, 74 | dst_var: Option, 75 | is_breakable: bool, 76 | }, 77 | Loop { 78 | label: usize, 79 | dst_var: Option, 80 | }, 81 | } 82 | 83 | fn to_rs_type(t: ValueType) -> &'static str { 84 | match t { 85 | ValueType::I32 => "i32", 86 | ValueType::I64 => "i64", 87 | ValueType::F32 => "f32", 88 | ValueType::F64 => "f64", 89 | } 90 | } 91 | 92 | use std::fmt; 93 | 94 | struct Indentation(usize); 95 | 96 | impl fmt::Display for Indentation { 97 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 98 | for _ in 0..self.0 { 99 | write!(f, " ")? 100 | } 101 | Ok(()) 102 | } 103 | } 104 | 105 | fn mangle_fn_name(name: &str) -> String { 106 | let mut s = String::new(); 107 | for (i, mut c) in name.chars().enumerate() { 108 | if i == 0 { 109 | if UnicodeXID::is_xid_start(c) { 110 | s.push(c); 111 | continue; 112 | } 113 | s.push('_'); 114 | } 115 | 116 | if UnicodeXID::is_xid_continue(c) { 117 | s.push(c); 118 | continue; 119 | } 120 | 121 | let decoded = unidecode_char(c); 122 | if decoded == "[?]" { 123 | if s.chars().last() != Some('_') { 124 | s.push('_'); 125 | } 126 | continue; 127 | } 128 | 129 | for c in decoded.chars() { 130 | if UnicodeXID::is_xid_continue(c) { 131 | s.push(c); 132 | } else { 133 | if s.chars().last() != Some('_') { 134 | s.push('_'); 135 | } 136 | } 137 | } 138 | } 139 | s 140 | } 141 | 142 | fn call_indirect_name(f: &FunctionType) -> String { 143 | let mut s = String::from("call_indirect_"); 144 | for param in f.params() { 145 | s.push_str(match *param { 146 | ValueType::I32 => "i", 147 | ValueType::I64 => "l", 148 | ValueType::F32 => "f", 149 | ValueType::F64 => "d", 150 | }); 151 | } 152 | s.push_str(match f.return_type() { 153 | Some(ValueType::I32) => "_i", 154 | Some(ValueType::I64) => "_l", 155 | Some(ValueType::F32) => "_f", 156 | Some(ValueType::F64) => "_d", 157 | None => "_v", 158 | }); 159 | s 160 | } 161 | 162 | fn main() { 163 | let opt = Opt::from_args(); 164 | let input = opt.input; 165 | let output = opt.output.unwrap_or_else(|| input.with_extension("rs")); 166 | 167 | let module = deserialize_file(input).unwrap(); 168 | let module = module.parse_names().unwrap_or_else(|(_, m)| m); 169 | 170 | let mut writer = BufWriter::new(File::create(output).expect("Couldn't create output file")); 171 | 172 | let import_count = module.import_count(ImportCountType::Function); 173 | let code = module.code_section().unwrap(); 174 | let fns = module.function_section().unwrap(); 175 | let types = module.type_section().unwrap(); 176 | let exports = module.export_section().unwrap(); 177 | let function_names = module 178 | .sections() 179 | .iter() 180 | .filter_map(|s| match *s { 181 | Section::Name(NameSection::Function(ref s)) => Some(s), 182 | _ => None, 183 | }) 184 | .next(); 185 | 186 | let mut functions = Vec::new(); 187 | 188 | if let Some(imports) = module.import_section() { 189 | for import in imports.entries() { 190 | // TODO Handle modules 191 | if let &External::Function(ty_index) = import.external() { 192 | let typ: &Type = &types.types()[ty_index as usize]; 193 | let fn_type = match *typ { 194 | Type::Function(ref t) => t, 195 | }; 196 | functions.push(Function { 197 | name: import.field().to_owned(), 198 | ty: fn_type, 199 | ty_index, 200 | real_name: None, 201 | make_public: false, 202 | }); 203 | } 204 | } 205 | } 206 | 207 | for function in fns.entries() { 208 | let ty_index = function.type_ref(); 209 | let Type::Function(ref fn_type) = types.types()[ty_index as usize]; 210 | let mut real_name = function_names.and_then(|f| f.names().get(functions.len() as _)); 211 | let mut name = format!("func{}", functions.len()); 212 | if opt.use_name_section { 213 | if let Some(real_name) = real_name.take() { 214 | name = real_name.to_string(); 215 | while functions.iter().any(|f| f.name == name) { 216 | name.push_str("_"); 217 | } 218 | } 219 | } 220 | functions.push(Function { 221 | name, 222 | ty: fn_type, 223 | ty_index, 224 | real_name, 225 | make_public: false, 226 | }); 227 | } 228 | 229 | for function in &mut functions { 230 | function.name = mangle_fn_name(&function.name); 231 | } 232 | 233 | writeln!( 234 | writer, 235 | "#![allow( 236 | unreachable_code, dead_code, unused_assignments, unused_mut, unused_variables, non_snake_case, 237 | non_upper_case_globals, unconditional_recursion, path_statements 238 | )] 239 | 240 | pub const PAGE_SIZE: usize = 64 << 10; 241 | 242 | pub trait Imports {{ 243 | type Memory: Memory;" 244 | ).unwrap(); 245 | 246 | for function in &functions[..import_count] { 247 | write!( 248 | writer, 249 | " fn {}(&mut self, context: &mut Context", 250 | function.name 251 | ).unwrap(); 252 | for (i, ¶m) in function.ty.params().iter().enumerate() { 253 | write!(writer, ", var{}: {}", i, to_rs_type(param)).unwrap(); 254 | } 255 | write!(writer, ")").unwrap(); 256 | if let Some(ret_ty) = function.ty.return_type() { 257 | write!(writer, " -> {}", to_rs_type(ret_ty)).unwrap(); 258 | } 259 | writeln!(writer, ";").unwrap(); 260 | } 261 | 262 | let mut globals = Vec::new(); 263 | 264 | if let Some(imports) = module.import_section() { 265 | for import in imports.entries() { 266 | if let &External::Global(ty) = import.external() { 267 | let name = import.field().to_string(); 268 | globals.push(Global { 269 | is_mutable: ty.is_mutable(), 270 | is_pub: true, // Doesn't really apply 271 | name, 272 | ty: to_rs_type(ty.content_type()), 273 | const_expr: &[], 274 | }); 275 | } 276 | } 277 | } 278 | 279 | let imported_globals_count = globals.len(); 280 | 281 | if let Some(global_section) = module.global_section() { 282 | for entry in global_section.entries() { 283 | let ty = entry.global_type(); 284 | let const_expr = entry.init_expr().code(); 285 | let is_mutable = 286 | ty.is_mutable() || is_const_expr_immutable_instead_of_const(const_expr); 287 | let name = if is_mutable { 288 | format!("global{}", globals.len()) 289 | } else { 290 | format!("GLOBAL{}", globals.len()) 291 | }; 292 | globals.push(Global { 293 | is_mutable, 294 | is_pub: false, 295 | name, 296 | ty: to_rs_type(ty.content_type()), 297 | const_expr, 298 | }); 299 | } 300 | } 301 | 302 | for export in exports.entries() { 303 | if let &Internal::Global(global_index) = export.internal() { 304 | let global = &mut globals[global_index as usize]; 305 | global.name = export.field().to_string(); 306 | global.is_pub = true; 307 | } 308 | } 309 | 310 | for global in &globals[..imported_globals_count] { 311 | // if global.is_mutable { 312 | writeln!( 313 | writer, 314 | " fn {}(&mut self, context: &mut Context) -> &mut {};", 315 | global.name, global.ty 316 | ).unwrap(); 317 | // } else { 318 | // writeln!(writer, " const {}: {};", global.name, global.ty); 319 | // } 320 | } 321 | 322 | writeln!( 323 | writer, 324 | "{}", 325 | r#"} 326 | 327 | pub trait Memory { 328 | fn load8(&mut self, addr: usize) -> u8; 329 | fn load16(&mut self, addr: usize) -> u16; 330 | fn load32(&mut self, addr: usize) -> u32; 331 | fn load64(&mut self, addr: usize) -> u64; 332 | 333 | fn store8(&mut self, addr: usize, val: u8); 334 | fn store16(&mut self, addr: usize, val: u16); 335 | fn store32(&mut self, addr: usize, val: u32); 336 | fn store64(&mut self, addr: usize, val: u64); 337 | 338 | fn store_slice(&mut self, addr: usize, val: &'static [u8]); 339 | 340 | fn grow(&mut self, pages: usize) -> i32; 341 | fn size(&mut self) -> i32; 342 | } 343 | 344 | pub struct Instance, M: Memory> { 345 | pub imports: I, 346 | pub context: Context, 347 | } 348 | 349 | pub struct Context { 350 | pub memory: M,"# 351 | ).unwrap(); 352 | 353 | for global in &globals[imported_globals_count..] { 354 | if global.is_mutable { 355 | write!(writer, " ").unwrap(); 356 | if global.is_pub { 357 | write!(writer, "pub ").unwrap(); 358 | } 359 | writeln!(writer, "{}: {},", global.name, global.ty).unwrap(); 360 | } 361 | } 362 | 363 | let mut has_dynamic_element_section_offset = false; 364 | if let Some(entry) = module.elements_section().and_then(|e| e.entries().get(0)) { 365 | let const_expr = entry.offset().code(); 366 | if is_const_expr_immutable_instead_of_const(const_expr) { 367 | writeln!(writer, " element_section_offset: i32,").unwrap(); 368 | has_dynamic_element_section_offset = true; 369 | } 370 | } 371 | 372 | if globals[imported_globals_count..] 373 | .iter() 374 | .any(|g| !g.is_mutable) 375 | { 376 | writeln!( 377 | writer, 378 | "{}", 379 | r#"} 380 | 381 | pub mod consts {"# 382 | ).unwrap(); 383 | 384 | for global in &globals[imported_globals_count..] { 385 | if !global.is_mutable { 386 | write!(writer, " pub").unwrap(); 387 | if !global.is_pub { 388 | write!(writer, "(super)").unwrap(); 389 | } 390 | write!(writer, " const {}: {} = ", global.name, global.ty).unwrap(); 391 | write_const_expr( 392 | &mut writer, 393 | global.const_expr, 394 | &globals, 395 | imported_globals_count, 396 | "", 397 | "", 398 | "", 399 | ); 400 | writeln!(writer, ";").unwrap(); 401 | } 402 | } 403 | } 404 | 405 | writeln!( 406 | writer, 407 | "{}", 408 | r#"} 409 | 410 | impl, M: Memory> Instance { 411 | pub fn new(imports: I, mut memory: M) -> Self {"# 412 | ).unwrap(); 413 | 414 | if let Some(memory) = module.memory_section().and_then(|m| m.entries().first()) { 415 | writeln!( 416 | writer, 417 | r#" let current_pages = memory.size() as usize; 418 | if current_pages < {0} {{ 419 | memory.grow({0} - current_pages); 420 | assert_eq!(memory.size(), {0}, "Not enough memory pages allocated"); 421 | }}"#, 422 | memory.limits().initial() 423 | ).unwrap(); 424 | } 425 | 426 | writeln!( 427 | writer, 428 | "{}", 429 | r#" let mut instance = Self { 430 | imports, 431 | context: Context { 432 | memory,"# 433 | ).unwrap(); 434 | 435 | for global in &globals[imported_globals_count..] { 436 | if global.is_mutable { 437 | write!(writer, " {}: ", global.name).unwrap(); 438 | if is_const_expr_immutable_instead_of_const(global.const_expr) { 439 | write!(writer, "Default::default()").unwrap(); 440 | } else { 441 | write_const_expr( 442 | &mut writer, 443 | global.const_expr, 444 | &globals, 445 | imported_globals_count, 446 | "consts::", 447 | "", 448 | "", 449 | ); 450 | } 451 | writeln!(writer, ",").unwrap(); 452 | } 453 | } 454 | 455 | if has_dynamic_element_section_offset { 456 | writeln!(writer, " element_section_offset: 0,").unwrap(); 457 | } 458 | 459 | writeln!( 460 | writer, 461 | "{}", 462 | r#" }, 463 | };"# 464 | ).unwrap(); 465 | 466 | for global in &globals[imported_globals_count..] { 467 | if global.is_mutable && is_const_expr_immutable_instead_of_const(global.const_expr) { 468 | write!(writer, " instance.context.{} = ", global.name).unwrap(); 469 | write_const_expr( 470 | &mut writer, 471 | global.const_expr, 472 | &globals, 473 | imported_globals_count, 474 | "consts::", 475 | "instance.imports", 476 | "&mut instance.context", 477 | ); 478 | writeln!(writer, ";").unwrap(); 479 | } 480 | } 481 | 482 | let mut indirect_fns = BTreeMap::new(); 483 | 484 | if let Some(entry) = module.elements_section().and_then(|e| e.entries().get(0)) { 485 | let const_expr = entry.offset().code(); 486 | let offset = if has_dynamic_element_section_offset { 487 | write!(writer, " instance.context.element_section_offset = ").unwrap(); 488 | write_const_expr( 489 | &mut writer, 490 | const_expr, 491 | &globals, 492 | imported_globals_count, 493 | "consts::", 494 | "instance.imports", 495 | "&mut instance.context", 496 | ); 497 | writeln!(writer, ";").unwrap(); 498 | 0 499 | } else { 500 | assert!(const_expr.len() == 2); 501 | match const_expr[0] { 502 | Opcode::I32Const(c) => c, 503 | _ => panic!("Unexpected Element Section Offset {:#?}", const_expr), 504 | } 505 | }; 506 | for (fn_ptr, &fn_index) in entry.members().iter().enumerate() { 507 | let type_index = functions[fn_index as usize].ty_index; 508 | indirect_fns 509 | .entry(type_index) 510 | .or_insert_with(Vec::new) 511 | .push(((fn_ptr as i32 + offset) as u32, fn_index)); 512 | } 513 | } 514 | 515 | if let Some(data_section) = module.data_section() { 516 | for entry in data_section.entries() { 517 | let offset = entry.offset().code(); 518 | assert!(offset.len() == 2); 519 | if let Opcode::I32Const(c) = offset[0] { 520 | if entry.value().windows(2).all(|a| a[0] == a[1]) { 521 | writeln!( 522 | writer, 523 | r#" instance.context.memory.store_slice({}, &[{}; {}]);"#, 524 | c, 525 | entry.value().first().cloned().unwrap_or_default(), 526 | entry.value().len() 527 | ).unwrap(); 528 | } else { 529 | write!( 530 | writer, 531 | r#" instance.context.memory.store_slice({}, b""#, 532 | c 533 | ).unwrap(); 534 | for &b in entry.value() { 535 | match b { 536 | b'\0' => write!(writer, r#"\0"#).unwrap(), 537 | b'"' => write!(writer, r#"\""#).unwrap(), 538 | b'\\' => write!(writer, r#"\\"#).unwrap(), 539 | b'\r' => write!(writer, r#"\r"#).unwrap(), 540 | b'\n' => write!(writer, r#"\n"#).unwrap(), 541 | b'\t' => write!(writer, r#"\t"#).unwrap(), 542 | 0x00...0x7F => { 543 | write!(writer, "{}", std::char::from_u32(b as _).unwrap()).unwrap() 544 | } 545 | _ => write!(writer, r#"\x{:X}"#, b).unwrap(), 546 | } 547 | } 548 | writeln!(writer, r#"");"#,).unwrap(); 549 | } 550 | } else { 551 | panic!("Data Segment with init expression mismatch"); 552 | } 553 | } 554 | } 555 | 556 | if let Some(start) = module.start_section() { 557 | let name = &functions[start as usize].name; 558 | writeln!( 559 | writer, 560 | " instance.context.{}(&mut instance.imports);", 561 | name 562 | ).unwrap(); 563 | } 564 | 565 | writeln!( 566 | writer, 567 | "{}", 568 | r#" instance 569 | }"# 570 | ).unwrap(); 571 | 572 | for export in exports.entries() { 573 | if let &Internal::Function(fn_index) = export.internal() { 574 | let function = &functions[fn_index as usize]; 575 | write!( 576 | writer, 577 | " pub fn {}(&mut self", 578 | mangle_fn_name(export.field()) 579 | ).unwrap(); 580 | for (i, ¶m) in function.ty.params().iter().enumerate() { 581 | write!(writer, ", var{}: {}", i, to_rs_type(param)).unwrap(); 582 | } 583 | write!(writer, ")").unwrap(); 584 | if let Some(ret_ty) = function.ty.return_type() { 585 | write!(writer, " -> {}", to_rs_type(ret_ty)).unwrap(); 586 | } 587 | writeln!(writer, " {{").unwrap(); 588 | write!( 589 | writer, 590 | " self.context.{}(&mut self.imports", 591 | function.name 592 | ).unwrap(); 593 | for (i, _) in function.ty.params().iter().enumerate() { 594 | write!(writer, ", var{}", i).unwrap(); 595 | } 596 | writeln!(writer, ")").unwrap(); 597 | writeln!(writer, " }}").unwrap(); 598 | } 599 | } 600 | 601 | if opt.public_call_indirect { 602 | for (&type_index, _) in &indirect_fns { 603 | let Type::Function(ref fn_type) = types.types()[type_index as usize]; 604 | let fn_name = call_indirect_name(fn_type); 605 | write!(writer, " pub fn {}", fn_name).unwrap(); 606 | write!(writer, "(&mut self, ptr: i32").unwrap(); 607 | for (i, ¶m) in fn_type.params().iter().enumerate() { 608 | write!(writer, ", var{}: {}", i, to_rs_type(param)).unwrap(); 609 | } 610 | write!(writer, ")").unwrap(); 611 | if let Some(ret_ty) = fn_type.return_type() { 612 | write!(writer, " -> {}", to_rs_type(ret_ty)).unwrap(); 613 | } 614 | write!( 615 | writer, 616 | " {{ 617 | self.context.{}(&mut self.imports, ptr", 618 | fn_name 619 | ).unwrap(); 620 | for (i, _) in fn_type.params().iter().enumerate() { 621 | write!(writer, ", var{}", i).unwrap(); 622 | } 623 | writeln!( 624 | writer, 625 | r#") 626 | }}"# 627 | ).unwrap(); 628 | } 629 | } 630 | 631 | writeln!( 632 | writer, 633 | "{}", 634 | r#"} 635 | 636 | impl Context {"# 637 | ).unwrap(); 638 | 639 | for export in exports.entries() { 640 | if let &Internal::Function(fn_index) = export.internal() { 641 | let function = &mut functions[fn_index as usize]; 642 | if function.name == export.field() { 643 | function.make_public = true; 644 | continue; 645 | } 646 | write!( 647 | writer, 648 | " pub fn {}>(&mut self, imports: &mut I", 649 | mangle_fn_name(export.field()) 650 | ).unwrap(); 651 | for (i, ¶m) in function.ty.params().iter().enumerate() { 652 | write!(writer, ", var{}: {}", i, to_rs_type(param)).unwrap(); 653 | } 654 | write!(writer, ")").unwrap(); 655 | if let Some(ret_ty) = function.ty.return_type() { 656 | write!(writer, " -> {}", to_rs_type(ret_ty)).unwrap(); 657 | } 658 | writeln!(writer, " {{").unwrap(); 659 | write!(writer, " self.{}(imports", function.name).unwrap(); 660 | for (i, _) in function.ty.params().iter().enumerate() { 661 | write!(writer, ", var{}", i).unwrap(); 662 | } 663 | writeln!(writer, ")").unwrap(); 664 | writeln!(writer, " }}").unwrap(); 665 | } 666 | } 667 | 668 | for (i, (body, func)) in code.bodies().iter().zip(fns.entries()).enumerate() { 669 | let type_index = func.type_ref(); 670 | let typ = &types.types()[type_index as usize]; 671 | let fn_type = match *typ { 672 | Type::Function(ref t) => t, 673 | }; 674 | let fn_index = import_count + i; 675 | let function = &functions[fn_index]; 676 | // TODO Ensure there's no collisions with the exports 677 | if let Some(real_name) = function.real_name { 678 | writeln!(writer, " // {}", real_name).unwrap(); 679 | } 680 | write!(writer, " ").unwrap(); 681 | if function.make_public { 682 | write!(writer, "pub ").unwrap(); 683 | } 684 | write!( 685 | writer, 686 | "fn {}>(&mut self, imports: &mut I", 687 | function.name 688 | ).unwrap(); 689 | for (i, ¶m) in fn_type.params().iter().enumerate() { 690 | write!(writer, ", mut var{}: {}", i, to_rs_type(param)).unwrap(); 691 | } 692 | write!(writer, ")").unwrap(); 693 | if let Some(ret_ty) = fn_type.return_type() { 694 | write!(writer, " -> {}", to_rs_type(ret_ty)).unwrap(); 695 | } 696 | writeln!(writer, " {{").unwrap(); 697 | 698 | let mut expr_index = fn_type.params().len(); 699 | for local in body.locals() { 700 | let ty = to_rs_type(local.value_type()); 701 | let decimals = if ty.starts_with("f") { ".0" } else { "" }; 702 | for _ in 0..local.count() { 703 | writeln!( 704 | writer, 705 | " let mut var{}: {} = 0{};", 706 | expr_index, ty, decimals 707 | ).unwrap(); 708 | expr_index += 1; 709 | } 710 | } 711 | 712 | code_builder::build( 713 | &mut writer, 714 | expr_index, 715 | fn_type.return_type().is_some(), 716 | import_count, 717 | imported_globals_count, 718 | &functions, 719 | &mut indirect_fns, 720 | &globals, 721 | types, 722 | body.code().elements(), 723 | 2, 724 | ); 725 | } 726 | 727 | for (type_index, fns) in indirect_fns { 728 | let Type::Function(ref fn_type) = types.types()[type_index as usize]; 729 | write!(writer, " ").unwrap(); 730 | if opt.public_call_indirect { 731 | write!(writer, "pub ").unwrap(); 732 | } 733 | write!( 734 | writer, 735 | "fn {}>", 736 | call_indirect_name(fn_type), 737 | ).unwrap(); 738 | write!(writer, "(&mut self, imports: &mut I, ptr: i32").unwrap(); 739 | for (i, ¶m) in fn_type.params().iter().enumerate() { 740 | write!(writer, ", var{}: {}", i, to_rs_type(param)).unwrap(); 741 | } 742 | write!(writer, ")").unwrap(); 743 | if let Some(ret_ty) = fn_type.return_type() { 744 | write!(writer, " -> {}", to_rs_type(ret_ty)).unwrap(); 745 | } 746 | write!( 747 | writer, 748 | " {{ 749 | match ptr" 750 | ).unwrap(); 751 | if has_dynamic_element_section_offset { 752 | write!(writer, " - self.element_section_offset").unwrap(); 753 | } 754 | writeln!(writer, " {{").unwrap(); 755 | for (fn_ptr, fn_index) in fns { 756 | let function = &functions[fn_index as usize]; 757 | write!(writer, " {} => ", fn_ptr).unwrap(); 758 | let is_imported = (fn_index as usize) < import_count; 759 | if is_imported { 760 | write!(writer, "imports.").unwrap(); 761 | } else { 762 | write!(writer, "self.").unwrap(); 763 | } 764 | write!(writer, "{}(", function.name).unwrap(); 765 | if is_imported { 766 | write!(writer, "self").unwrap(); 767 | } else { 768 | write!(writer, "imports").unwrap(); 769 | } 770 | for i in 0..function.ty.params().len() { 771 | write!(writer, ", var{}", i).unwrap(); 772 | } 773 | if let Some(real_name) = function.real_name { 774 | writeln!(writer, "), // {}", real_name).unwrap(); 775 | } else { 776 | writeln!(writer, "),").unwrap(); 777 | } 778 | } 779 | writeln!( 780 | writer, 781 | r#" _ => panic!("Invalid Function Pointer"), 782 | }} 783 | }}"# 784 | ).unwrap(); 785 | } 786 | 787 | writeln!(writer, "}}").unwrap(); 788 | } 789 | 790 | fn write_const_expr( 791 | writer: &mut W, 792 | opcodes: &[Opcode], 793 | globals: &[Global], 794 | imported_globals_count: usize, 795 | consts_path: &str, 796 | imports_path: &str, 797 | context_path: &str, 798 | ) { 799 | assert!( 800 | opcodes.len() == 2, 801 | "Invalid Constant Expression {:#?}", 802 | opcodes 803 | ); 804 | match opcodes[0] { 805 | Opcode::I32Const(c) => write!(writer, "{}", c).unwrap(), 806 | Opcode::I64Const(c) => write!(writer, "{}", c).unwrap(), 807 | Opcode::F32Const(c) => write!(writer, "{}", c).unwrap(), 808 | Opcode::F64Const(c) => write!(writer, "{}", c).unwrap(), 809 | Opcode::GetGlobal(i) => { 810 | let global = &globals[i as usize]; 811 | let name = &global.name; 812 | if (i as usize) < imported_globals_count { 813 | write!(writer, "*{}.{}({})", imports_path, name, context_path).unwrap(); 814 | } else if global.is_mutable { 815 | write_const_expr( 816 | writer, 817 | global.const_expr, 818 | globals, 819 | imported_globals_count, 820 | consts_path, 821 | imports_path, 822 | context_path, 823 | ); 824 | } else { 825 | write!(writer, "{}{}", consts_path, name).unwrap(); 826 | } 827 | } 828 | _ => panic!("Invalid Constant Expression {:#?}", opcodes), 829 | } 830 | } 831 | 832 | fn is_const_expr_immutable_instead_of_const(opcodes: &[Opcode]) -> bool { 833 | assert!( 834 | opcodes.len() == 2, 835 | "Invalid Constant Expression {:#?}", 836 | opcodes 837 | ); 838 | match opcodes[0] { 839 | Opcode::I32Const(_) | Opcode::I64Const(_) | Opcode::F32Const(_) | Opcode::F64Const(_) => { 840 | false 841 | } 842 | _ => true, // This could actually be fully const, but it's hard to tell this early 843 | } 844 | } 845 | -------------------------------------------------------------------------------- /src/code_builder.rs: -------------------------------------------------------------------------------- 1 | use expr_builder::ExprBuilder; 2 | use parity_wasm::elements::{BlockType, Opcode, Type, TypeSection}; 3 | use reorder_analysis::can_local_be_reordered; 4 | use std::collections::BTreeMap; 5 | use std::io::Write; 6 | use {call_indirect_name, precedence, to_rs_type, BlockKind, Function, Global, Indentation}; 7 | 8 | fn is_breakable_if(remaining_ops: &[Opcode]) -> bool { 9 | let mut stack = 0; 10 | 11 | for opcode in remaining_ops { 12 | use parity_wasm::elements::Opcode::*; 13 | match *opcode { 14 | Block(_) | If(_) | Loop(_) => { 15 | stack += 1; 16 | } 17 | End => { 18 | if stack == 0 { 19 | return false; 20 | } 21 | stack -= 1; 22 | } 23 | Br(relative_depth) | BrIf(relative_depth) => { 24 | if relative_depth == stack { 25 | return true; 26 | } 27 | } 28 | BrTable(ref table, default_depth) => { 29 | if table.iter().any(|&i| i == stack) || default_depth == stack { 30 | return true; 31 | } 32 | } 33 | _ => {} 34 | } 35 | } 36 | panic!("Unclosed block") 37 | } 38 | 39 | pub fn build( 40 | writer: &mut W, 41 | mut expr_index: usize, 42 | evaluates_to_value: bool, 43 | import_count: usize, 44 | imported_globals_count: usize, 45 | functions: &[Function], 46 | indirect_fns: &mut BTreeMap>, 47 | globals: &[Global], 48 | types: &TypeSection, 49 | code: &[Opcode], 50 | base_indentation: usize, 51 | ) { 52 | let mut expr_builder = ExprBuilder::new(); 53 | let mut blocks = Vec::new(); 54 | let mut indentation = Indentation(base_indentation); 55 | let mut loop_count = 0; 56 | 57 | blocks.push(BlockKind::Function { evaluates_to_value }); 58 | 59 | let mut code = code.into_iter(); 60 | while let Some(opcode) = code.next() { 61 | // writeln!( 62 | // writer, 63 | // "{}// opcode: {:?} stack: {:?} block types: {:?}", 64 | // indentation, opcode, expr_builder, blocks 65 | // ).unwrap(); 66 | use parity_wasm::elements::Opcode::*; 67 | match *opcode { 68 | Unreachable => { 69 | writeln!(writer, "{}unreachable!();", indentation).unwrap(); 70 | } 71 | Nop => { 72 | // TODO Activate this again 73 | // assert!(expr_builder.is_empty()); 74 | } 75 | Block(block_type) => { 76 | let dst_var = if let BlockType::Value(ty) = block_type { 77 | let var_name = format!("var{}", expr_index); 78 | writeln!( 79 | writer, 80 | "{}let {}: {};", 81 | indentation, 82 | var_name, 83 | to_rs_type(ty) 84 | ).unwrap(); 85 | expr_index += 1; 86 | Some(var_name) 87 | } else { 88 | None 89 | }; 90 | 91 | writeln!(writer, "{}'label{}: loop {{", indentation, loop_count).unwrap(); 92 | blocks.push(BlockKind::Block { 93 | label: loop_count, 94 | dst_var, 95 | }); 96 | loop_count += 1; 97 | indentation.0 += 1; 98 | } 99 | Loop(block_type) => { 100 | let dst_var = if let BlockType::Value(ty) = block_type { 101 | let var_name = format!("var{}", expr_index); 102 | writeln!( 103 | writer, 104 | "{}let {}: {};", 105 | indentation, 106 | var_name, 107 | to_rs_type(ty) 108 | ).unwrap(); 109 | expr_index += 1; 110 | Some(var_name) 111 | } else { 112 | None 113 | }; 114 | 115 | writeln!(writer, "{}'label{}: loop {{", indentation, loop_count).unwrap(); 116 | blocks.push(BlockKind::Loop { 117 | label: loop_count, 118 | dst_var, 119 | }); 120 | loop_count += 1; 121 | indentation.0 += 1; 122 | } 123 | If(block_type) => { 124 | let dst_var = if let BlockType::Value(ty) = block_type { 125 | let var_name = format!("var{}", expr_index); 126 | writeln!( 127 | writer, 128 | "{}let {}: {};", 129 | indentation, 130 | var_name, 131 | to_rs_type(ty) 132 | ).unwrap(); 133 | expr_index += 1; 134 | Some(var_name) 135 | } else { 136 | None 137 | }; 138 | 139 | let is_breakable = is_breakable_if(code.as_slice()); 140 | 141 | if is_breakable { 142 | writeln!(writer, "{}'label{}: loop {{", indentation, loop_count).unwrap(); 143 | } 144 | blocks.push(BlockKind::If { 145 | label: loop_count, 146 | dst_var, 147 | is_breakable, 148 | }); 149 | if is_breakable { 150 | loop_count += 1; 151 | indentation.0 += 1; 152 | } 153 | 154 | let expr = expr_builder.pop_formatted(precedence::COMPARISON).unwrap(); 155 | writeln!(writer, "{}if {} != 0 {{", indentation, expr).unwrap(); 156 | indentation.0 += 1; 157 | } 158 | Else => { 159 | if let Some(&BlockKind::If { ref dst_var, .. }) = blocks.last() { 160 | if let &Some(ref dst_var) = dst_var { 161 | let (_, expr) = expr_builder.pop().unwrap(); 162 | writeln!(writer, "{}{} = {};", indentation, dst_var, expr).unwrap(); 163 | } 164 | indentation.0 -= 1; 165 | writeln!(writer, "{}}} else {{", indentation).unwrap(); 166 | indentation.0 += 1; 167 | } else { 168 | panic!("Else can only be used with an if"); 169 | } 170 | } 171 | End => { 172 | match blocks.pop().expect("End used outside of a block") { 173 | BlockKind::Block { dst_var, .. } | BlockKind::Loop { dst_var, .. } => { 174 | if let Some(dst_var) = dst_var { 175 | if let Some((_, expr)) = expr_builder.pop() { 176 | writeln!(writer, "{}{} = {};", indentation, dst_var, expr).unwrap(); 177 | expr_builder.push((precedence::PATH, dst_var)); 178 | } else { 179 | writeln!(writer, "{}// There should've been an expression value here, but this may be unreachable", indentation).unwrap(); 180 | writeln!(writer, "{}unreachable!();", indentation).unwrap(); 181 | } 182 | } 183 | writeln!(writer, "{}break;", indentation).unwrap(); 184 | } 185 | BlockKind::If { 186 | dst_var, 187 | is_breakable, 188 | .. 189 | } => { 190 | if let Some(dst_var) = dst_var { 191 | if let Some((_, expr)) = expr_builder.pop() { 192 | writeln!(writer, "{}{} = {};", indentation, dst_var, expr).unwrap(); 193 | expr_builder.push((precedence::PATH, dst_var)); 194 | } else { 195 | writeln!(writer, "{}// There should've been an expression value here, but this may be unreachable", indentation).unwrap(); 196 | writeln!(writer, "{}unreachable!();", indentation).unwrap(); 197 | } 198 | } 199 | if is_breakable { 200 | indentation.0 -= 1; 201 | writeln!(writer, "{}}}", indentation).unwrap(); 202 | writeln!(writer, "{}break;", indentation).unwrap(); 203 | } 204 | } 205 | BlockKind::Function { evaluates_to_value } => { 206 | if evaluates_to_value { 207 | if let Some((_, expr)) = expr_builder.pop() { 208 | writeln!(writer, "{}{}", indentation, expr).unwrap(); 209 | } else { 210 | writeln!(writer, "{}// There should've been an expression value here, but this may be unreachable", indentation).unwrap(); 211 | writeln!(writer, "{}unreachable!();", indentation).unwrap(); 212 | } 213 | } 214 | } 215 | } 216 | 217 | indentation.0 -= 1; 218 | writeln!(writer, "{}}}", indentation).unwrap(); 219 | } 220 | Br(relative_depth) => { 221 | let block = blocks 222 | .iter() 223 | .rev() 224 | .nth(relative_depth as usize) 225 | .expect("Branch Index out of Bounds"); 226 | 227 | match *block { 228 | BlockKind::Function { evaluates_to_value } => { 229 | if evaluates_to_value { 230 | let (_, expr) = expr_builder.pop().unwrap(); 231 | writeln!(writer, "{}return {};", indentation, expr).unwrap(); 232 | } else { 233 | writeln!(writer, "{}return;", indentation).unwrap(); 234 | } 235 | } 236 | BlockKind::Block { ref dst_var, label } 237 | | BlockKind::If { 238 | ref dst_var, label, .. 239 | } => { 240 | if let &Some(ref dst_var) = dst_var { 241 | let (_, expr) = expr_builder.pop().unwrap(); 242 | writeln!(writer, "{}{} = {};", indentation, dst_var, expr).unwrap(); 243 | } 244 | writeln!(writer, "{}break 'label{};", indentation, label).unwrap(); 245 | } 246 | BlockKind::Loop { label, .. } => { 247 | writeln!(writer, "{}continue 'label{};", indentation, label).unwrap(); 248 | } 249 | } 250 | } 251 | BrIf(relative_depth) => { 252 | let cond_expr = expr_builder.pop_formatted(precedence::COMPARISON).unwrap(); 253 | let block = blocks 254 | .iter() 255 | .rev() 256 | .nth(relative_depth as usize) 257 | .expect("Branch Index out of Bounds"); 258 | 259 | let evaluates_to_value = match *block { 260 | BlockKind::Block { ref dst_var, .. } | BlockKind::If { ref dst_var, .. } => { 261 | dst_var.is_some() 262 | } 263 | BlockKind::Function { evaluates_to_value } => evaluates_to_value, 264 | BlockKind::Loop { .. } => false, 265 | }; 266 | 267 | // TODO This evaluates cond and val out of order. So this relies 268 | // on them not having any side effects 269 | 270 | let tmp_var = if evaluates_to_value { 271 | let tmp_var = format!("var{}", expr_index); 272 | { 273 | let &(_, ref tmp_var_val) = expr_builder.inner().last().unwrap(); 274 | writeln!(writer, "{}let {} = {};", indentation, tmp_var, tmp_var_val) 275 | .unwrap(); 276 | } 277 | expr_index += 1; 278 | expr_builder.push((precedence::PATH, tmp_var.clone())); 279 | Some(tmp_var) 280 | } else { 281 | None 282 | }; 283 | 284 | writeln!(writer, "{}if {} != 0 {{", indentation, cond_expr).unwrap(); 285 | 286 | indentation.0 += 1; 287 | 288 | match *block { 289 | BlockKind::Block { label, ref dst_var } 290 | | BlockKind::If { 291 | label, ref dst_var, .. 292 | } => { 293 | if let Some(tmp_var) = tmp_var { 294 | writeln!( 295 | writer, 296 | "{}{} = {};", 297 | indentation, 298 | dst_var.as_ref().unwrap(), 299 | tmp_var, 300 | ).unwrap(); 301 | } 302 | writeln!(writer, "{}break 'label{};", indentation, label).unwrap(); 303 | } 304 | BlockKind::Loop { label, .. } => { 305 | writeln!(writer, "{}continue 'label{};", indentation, label).unwrap(); 306 | } 307 | BlockKind::Function { .. } => { 308 | if let Some(tmp_var) = tmp_var { 309 | writeln!(writer, "{}return {};", indentation, tmp_var).unwrap(); 310 | } else { 311 | writeln!(writer, "{}return;", indentation).unwrap(); 312 | } 313 | } 314 | } 315 | 316 | indentation.0 -= 1; 317 | 318 | writeln!(writer, "{}}}", indentation).unwrap(); 319 | } 320 | BrTable(ref table, default_depth) => { 321 | let (_, expr) = expr_builder.pop().unwrap(); 322 | // TODO Branch with value 323 | writeln!(writer, "{}match {} {{", indentation, expr).unwrap(); 324 | indentation.0 += 1; 325 | for (index, &relative_depth) in table.iter().enumerate() { 326 | let block = blocks 327 | .iter() 328 | .rev() 329 | .nth(relative_depth as usize) 330 | .expect("Branch Index out of Bounds"); 331 | 332 | match *block { 333 | BlockKind::Block { label, .. } | BlockKind::If { label, .. } => { 334 | writeln!(writer, "{}{} => break 'label{},", indentation, index, label) 335 | .unwrap(); 336 | } 337 | BlockKind::Loop { label, .. } => { 338 | writeln!( 339 | writer, 340 | "{}{} => continue 'label{},", 341 | indentation, index, label 342 | ).unwrap(); 343 | } 344 | BlockKind::Function { .. } => { 345 | writeln!(writer, "{}_ => return,", indentation).unwrap(); 346 | } 347 | } 348 | } 349 | 350 | let block = blocks 351 | .iter() 352 | .rev() 353 | .nth(default_depth as usize) 354 | .expect("Branch Index out of Bounds"); 355 | 356 | match *block { 357 | BlockKind::Block { label, .. } | BlockKind::If { label, .. } => { 358 | writeln!(writer, "{}_ => break 'label{},", indentation, label).unwrap(); 359 | } 360 | BlockKind::Loop { label, .. } => { 361 | writeln!(writer, "{}_ => continue 'label{},", indentation, label).unwrap(); 362 | } 363 | BlockKind::Function { .. } => { 364 | writeln!(writer, "{}_ => return,", indentation).unwrap(); 365 | } 366 | } 367 | 368 | indentation.0 -= 1; 369 | writeln!(writer, "{}}}", indentation).unwrap(); 370 | } 371 | Return => { 372 | if let Some((_, expr)) = expr_builder.pop() { 373 | writeln!(writer, "{}return {};", indentation, expr).unwrap(); 374 | } else { 375 | writeln!(writer, "{}return;", indentation).unwrap(); 376 | } 377 | } 378 | Call(fn_index) => { 379 | let function = &functions[fn_index as usize]; 380 | let name = &function.name; 381 | let fn_type = function.ty; 382 | let real_name = function.real_name; 383 | write!(writer, "{}", indentation).unwrap(); 384 | if fn_type.return_type().is_some() { 385 | write!(writer, "let var{} = ", expr_index).unwrap(); 386 | } 387 | let is_imported = (fn_index as usize) < import_count; 388 | if is_imported { 389 | write!(writer, "imports.").unwrap(); 390 | } else { 391 | write!(writer, "self.").unwrap(); 392 | } 393 | write!(writer, "{}(", name).unwrap(); 394 | if is_imported { 395 | write!(writer, "self").unwrap(); 396 | } else { 397 | write!(writer, "imports").unwrap(); 398 | } 399 | let index = expr_builder.len() - fn_type.params().len(); 400 | for (_, expr) in expr_builder.inner().drain(index..) { 401 | write!(writer, ", {}", expr).unwrap(); 402 | } 403 | if let Some(real_name) = real_name { 404 | writeln!(writer, "); // {}", real_name).unwrap(); 405 | } else { 406 | writeln!(writer, ");").unwrap(); 407 | } 408 | if fn_type.return_type().is_some() { 409 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 410 | expr_index += 1; 411 | } 412 | } 413 | CallIndirect(type_index, _) => { 414 | let Type::Function(ref fn_type) = types.types()[type_index as usize]; 415 | indirect_fns.entry(type_index).or_insert_with(Vec::new); 416 | 417 | write!(writer, "{}", indentation).unwrap(); 418 | if fn_type.return_type().is_some() { 419 | write!(writer, "let var{} = ", expr_index).unwrap(); 420 | } 421 | let (_, fn_ptr) = expr_builder.pop().unwrap(); 422 | write!( 423 | writer, 424 | "self.{}(imports, {}", 425 | call_indirect_name(fn_type), 426 | fn_ptr 427 | ).unwrap(); 428 | let index = expr_builder.len() - fn_type.params().len(); 429 | for (_, expr) in expr_builder.inner().drain(index..) { 430 | write!(writer, ", {}", expr).unwrap(); 431 | } 432 | writeln!(writer, ");").unwrap(); 433 | if fn_type.return_type().is_some() { 434 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 435 | expr_index += 1; 436 | } 437 | } 438 | Drop => { 439 | let (_, a) = expr_builder.pop().unwrap(); 440 | // We actually write out the expression, as it may have side 441 | // effects. Atm we contain pretty much every side effect as its 442 | // own non-dropped statement. However there are also side 443 | // effects in just expressions, like a division by 0, which 444 | // causes a panic. We want to preserve these semantics. 445 | writeln!(writer, "{}{};", indentation, a).unwrap(); 446 | } 447 | Select => { 448 | let c = expr_builder.pop_formatted(precedence::COMPARISON).unwrap(); 449 | let (_, b) = expr_builder.pop().unwrap(); 450 | let (_, a) = expr_builder.pop().unwrap(); 451 | // Just like with drop, we have to evaluate both branches early, 452 | // as they may have panics that wouldn't happen if there branch 453 | // doesn't get chosen. 454 | expr_builder.push(( 455 | precedence::MAX, 456 | format!( 457 | "{{ let a = {}; let b = {}; if {} != 0 {{ a }} else {{ b }} }}", 458 | a, b, c 459 | ), 460 | )); 461 | } 462 | GetLocal(i) => { 463 | if can_local_be_reordered(i, &blocks, functions, types, code.as_slice()) { 464 | let dst = format!("var{}", i); 465 | expr_builder.push((precedence::PATH, dst)); 466 | } else { 467 | let dst = format!("var{}", expr_index); 468 | writeln!(writer, "{}let {} = var{};", indentation, dst, i).unwrap(); 469 | expr_index += 1; 470 | expr_builder.push((precedence::PATH, dst)); 471 | } 472 | } 473 | SetLocal(i) => { 474 | let (_, expr) = expr_builder.pop().unwrap(); 475 | writeln!(writer, "{}var{} = {};", indentation, i, expr).unwrap(); 476 | } 477 | TeeLocal(i) => { 478 | let (_, expr) = expr_builder.pop().unwrap(); 479 | writeln!(writer, "{}var{} = {};", indentation, i, expr).unwrap(); 480 | 481 | if can_local_be_reordered(i, &blocks, functions, types, code.as_slice()) { 482 | let dst = format!("var{}", i); 483 | expr_builder.push((precedence::PATH, dst)); 484 | } else { 485 | let dst = format!("var{}", expr_index); 486 | writeln!(writer, "{}let {} = var{};", indentation, dst, i).unwrap(); 487 | expr_index += 1; 488 | expr_builder.push((precedence::PATH, dst)); 489 | } 490 | } 491 | GetGlobal(i) => { 492 | let global = &globals[i as usize]; 493 | let name = &global.name; 494 | if (i as usize) < imported_globals_count { 495 | let dst = format!("var{}", expr_index); 496 | writeln!( 497 | writer, 498 | "{}let {} = *imports.{}(self);", 499 | indentation, dst, name 500 | ).unwrap(); 501 | expr_index += 1; 502 | expr_builder.push((precedence::PATH, dst)); 503 | } else if global.is_mutable { 504 | let dst = format!("var{}", expr_index); 505 | writeln!(writer, "{}let {} = self.{};", indentation, dst, name).unwrap(); 506 | expr_index += 1; 507 | expr_builder.push((precedence::PATH, dst)); 508 | } else { 509 | expr_builder.push((precedence::PATH, format!("consts::{}", name))); 510 | } 511 | } 512 | SetGlobal(i) => { 513 | let (_, expr) = expr_builder.pop().unwrap(); 514 | let global = &globals[i as usize]; 515 | let name = &global.name; 516 | assert!(global.is_mutable); 517 | if (i as usize) < imported_globals_count { 518 | writeln!(writer, "{}*imports.{}(self) = {};", indentation, name, expr).unwrap(); 519 | } else { 520 | writeln!(writer, "{}self.{} = {};", indentation, name, expr).unwrap(); 521 | } 522 | } 523 | I32Load(_log_align, offset) => { 524 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 525 | writeln!( 526 | writer, 527 | "{}let var{} = self.memory.load32({} as usize{}) as i32;", 528 | indentation, 529 | expr_index, 530 | addr, 531 | if offset != 0 { 532 | format!(" + {}", offset) 533 | } else { 534 | String::new() 535 | } 536 | ).unwrap(); 537 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 538 | expr_index += 1; 539 | } 540 | I64Load(_log_align, offset) => { 541 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 542 | writeln!( 543 | writer, 544 | "{}let var{} = self.memory.load64({} as usize{}) as i64;", 545 | indentation, 546 | expr_index, 547 | addr, 548 | if offset != 0 { 549 | format!(" + {}", offset) 550 | } else { 551 | String::new() 552 | } 553 | ).unwrap(); 554 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 555 | expr_index += 1; 556 | } 557 | F32Load(_log_align, offset) => { 558 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 559 | writeln!( 560 | writer, 561 | "{}let var{} = f32::from_bits(self.memory.load32({} as usize{}));", 562 | indentation, 563 | expr_index, 564 | addr, 565 | if offset != 0 { 566 | format!(" + {}", offset) 567 | } else { 568 | String::new() 569 | } 570 | ).unwrap(); 571 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 572 | expr_index += 1; 573 | } 574 | F64Load(_log_align, offset) => { 575 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 576 | writeln!( 577 | writer, 578 | "{}let var{} = f64::from_bits(self.memory.load64({} as usize{}));", 579 | indentation, 580 | expr_index, 581 | addr, 582 | if offset != 0 { 583 | format!(" + {}", offset) 584 | } else { 585 | String::new() 586 | } 587 | ).unwrap(); 588 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 589 | expr_index += 1; 590 | } 591 | I32Load8S(_log_align, offset) => { 592 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 593 | writeln!( 594 | writer, 595 | "{}let var{} = self.memory.load8({} as usize{}) as i8 as i32;", 596 | indentation, 597 | expr_index, 598 | addr, 599 | if offset != 0 { 600 | format!(" + {}", offset) 601 | } else { 602 | String::new() 603 | } 604 | ).unwrap(); 605 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 606 | expr_index += 1; 607 | } 608 | I32Load8U(_log_align, offset) => { 609 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 610 | writeln!( 611 | writer, 612 | "{}let var{} = self.memory.load8({} as usize{}) as i32;", 613 | indentation, 614 | expr_index, 615 | addr, 616 | if offset != 0 { 617 | format!(" + {}", offset) 618 | } else { 619 | String::new() 620 | } 621 | ).unwrap(); 622 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 623 | expr_index += 1; 624 | } 625 | I32Load16S(_log_align, offset) => { 626 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 627 | writeln!( 628 | writer, 629 | "{}let var{} = self.memory.load16({} as usize{}) as i16 as i32;", 630 | indentation, 631 | expr_index, 632 | addr, 633 | if offset != 0 { 634 | format!(" + {}", offset) 635 | } else { 636 | String::new() 637 | } 638 | ).unwrap(); 639 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 640 | expr_index += 1; 641 | } 642 | I32Load16U(_log_align, offset) => { 643 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 644 | writeln!( 645 | writer, 646 | "{}let var{} = self.memory.load16({} as usize{}) as i32;", 647 | indentation, 648 | expr_index, 649 | addr, 650 | if offset != 0 { 651 | format!(" + {}", offset) 652 | } else { 653 | String::new() 654 | } 655 | ).unwrap(); 656 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 657 | expr_index += 1; 658 | } 659 | I64Load8S(_log_align, offset) => { 660 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 661 | writeln!( 662 | writer, 663 | "{}let var{} = self.memory.load8({} as usize{}) as i8 as i64;", 664 | indentation, 665 | expr_index, 666 | addr, 667 | if offset != 0 { 668 | format!(" + {}", offset) 669 | } else { 670 | String::new() 671 | } 672 | ).unwrap(); 673 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 674 | expr_index += 1; 675 | } 676 | I64Load8U(_log_align, offset) => { 677 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 678 | writeln!( 679 | writer, 680 | "{}let var{} = self.memory.load8({} as usize{}) as i64;", 681 | indentation, 682 | expr_index, 683 | addr, 684 | if offset != 0 { 685 | format!(" + {}", offset) 686 | } else { 687 | String::new() 688 | } 689 | ).unwrap(); 690 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 691 | expr_index += 1; 692 | } 693 | I64Load16S(_log_align, offset) => { 694 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 695 | writeln!( 696 | writer, 697 | "{}let var{} = self.memory.load16({} as usize{}) as i16 as i64;", 698 | indentation, 699 | expr_index, 700 | addr, 701 | if offset != 0 { 702 | format!(" + {}", offset) 703 | } else { 704 | String::new() 705 | } 706 | ).unwrap(); 707 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 708 | expr_index += 1; 709 | } 710 | I64Load16U(_log_align, offset) => { 711 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 712 | writeln!( 713 | writer, 714 | "{}let var{} = self.memory.load16({} as usize{}) as i64;", 715 | indentation, 716 | expr_index, 717 | addr, 718 | if offset != 0 { 719 | format!(" + {}", offset) 720 | } else { 721 | String::new() 722 | } 723 | ).unwrap(); 724 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 725 | expr_index += 1; 726 | } 727 | I64Load32S(_log_align, offset) => { 728 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 729 | writeln!( 730 | writer, 731 | "{}let var{} = self.memory.load32({} as usize{}) as i32 as i64;", 732 | indentation, 733 | expr_index, 734 | addr, 735 | if offset != 0 { 736 | format!(" + {}", offset) 737 | } else { 738 | String::new() 739 | } 740 | ).unwrap(); 741 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 742 | expr_index += 1; 743 | } 744 | I64Load32U(_log_align, offset) => { 745 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 746 | writeln!( 747 | writer, 748 | "{}let var{} = self.memory.load32({} as usize{}) as i64;", 749 | indentation, 750 | expr_index, 751 | addr, 752 | if offset != 0 { 753 | format!(" + {}", offset) 754 | } else { 755 | String::new() 756 | } 757 | ).unwrap(); 758 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 759 | expr_index += 1; 760 | } 761 | I32Store(_log_align, offset) => { 762 | let value = expr_builder.pop_formatted(precedence::AS).unwrap(); 763 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 764 | writeln!( 765 | writer, 766 | "{}self.memory.store32({} as usize{}, {} as u32);", 767 | indentation, 768 | addr, 769 | if offset != 0 { 770 | format!(" + {}", offset) 771 | } else { 772 | String::new() 773 | }, 774 | value 775 | ).unwrap(); 776 | } 777 | I64Store(_log_align, offset) => { 778 | let value = expr_builder.pop_formatted(precedence::AS).unwrap(); 779 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 780 | writeln!( 781 | writer, 782 | "{}self.memory.store64({} as usize{}, {} as u64);", 783 | indentation, 784 | addr, 785 | if offset != 0 { 786 | format!(" + {}", offset) 787 | } else { 788 | String::new() 789 | }, 790 | value 791 | ).unwrap(); 792 | } 793 | F32Store(_log_align, offset) => { 794 | let value = expr_builder.pop_formatted(precedence::METHOD_CALL).unwrap(); 795 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 796 | writeln!( 797 | writer, 798 | "{}self.memory.store32({} as usize{}, {}.to_bits());", 799 | indentation, 800 | addr, 801 | if offset != 0 { 802 | format!(" + {}", offset) 803 | } else { 804 | String::new() 805 | }, 806 | value 807 | ).unwrap(); 808 | } 809 | F64Store(_log_align, offset) => { 810 | let value = expr_builder.pop_formatted(precedence::METHOD_CALL).unwrap(); 811 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 812 | writeln!( 813 | writer, 814 | "{}self.memory.store64({} as usize{}, {}.to_bits());", 815 | indentation, 816 | addr, 817 | if offset != 0 { 818 | format!(" + {}", offset) 819 | } else { 820 | String::new() 821 | }, 822 | value 823 | ).unwrap(); 824 | } 825 | I32Store8(_log_align, offset) => { 826 | let value = expr_builder.pop_formatted(precedence::AS).unwrap(); 827 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 828 | writeln!( 829 | writer, 830 | "{}self.memory.store8({} as usize{}, {} as u8);", 831 | indentation, 832 | addr, 833 | if offset != 0 { 834 | format!(" + {}", offset) 835 | } else { 836 | String::new() 837 | }, 838 | value 839 | ).unwrap(); 840 | } 841 | I32Store16(_log_align, offset) => { 842 | let value = expr_builder.pop_formatted(precedence::AS).unwrap(); 843 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 844 | writeln!( 845 | writer, 846 | "{}self.memory.store16({} as usize{}, {} as u16);", 847 | indentation, 848 | addr, 849 | if offset != 0 { 850 | format!(" + {}", offset) 851 | } else { 852 | String::new() 853 | }, 854 | value 855 | ).unwrap(); 856 | } 857 | I64Store8(_log_align, offset) => { 858 | let value = expr_builder.pop_formatted(precedence::AS).unwrap(); 859 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 860 | writeln!( 861 | writer, 862 | "{}self.memory.store8({} as usize{}, {} as u8);", 863 | indentation, 864 | addr, 865 | if offset != 0 { 866 | format!(" + {}", offset) 867 | } else { 868 | String::new() 869 | }, 870 | value 871 | ).unwrap(); 872 | } 873 | I64Store16(_log_align, offset) => { 874 | let value = expr_builder.pop_formatted(precedence::AS).unwrap(); 875 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 876 | writeln!( 877 | writer, 878 | "{}self.memory.store16({} as usize{}, {} as u16);", 879 | indentation, 880 | addr, 881 | if offset != 0 { 882 | format!(" + {}", offset) 883 | } else { 884 | String::new() 885 | }, 886 | value 887 | ).unwrap(); 888 | } 889 | I64Store32(_log_align, offset) => { 890 | let value = expr_builder.pop_formatted(precedence::AS).unwrap(); 891 | let addr = expr_builder.pop_formatted(precedence::AS).unwrap(); 892 | writeln!( 893 | writer, 894 | "{}self.memory.store32({} as usize{}, {} as u32);", 895 | indentation, 896 | addr, 897 | if offset != 0 { 898 | format!(" + {}", offset) 899 | } else { 900 | String::new() 901 | }, 902 | value 903 | ).unwrap(); 904 | } 905 | CurrentMemory(_) => { 906 | let dst = format!("var{}", expr_index); 907 | writeln!(writer, "{}let {} = self.memory.size();", indentation, dst).unwrap(); 908 | expr_index += 1; 909 | expr_builder.push((precedence::PATH, dst)); 910 | } 911 | GrowMemory(_) => { 912 | let pages = expr_builder.pop_formatted(precedence::AS).unwrap(); 913 | let dst = format!("var{}", expr_index); 914 | writeln!( 915 | writer, 916 | "{}let {} = self.memory.grow({} as usize);", 917 | indentation, dst, pages 918 | ).unwrap(); 919 | expr_builder.push((precedence::PATH, dst)); 920 | expr_index += 1; 921 | } 922 | I32Const(c) => { 923 | let precedence = if c < 0 { 924 | precedence::UNARY 925 | } else { 926 | precedence::PATH 927 | }; 928 | expr_builder.push((precedence, format!("{}i32", c))); 929 | } 930 | I64Const(c) => { 931 | let precedence = if c < 0 { 932 | precedence::UNARY 933 | } else { 934 | precedence::PATH 935 | }; 936 | expr_builder.push((precedence, format!("{}i64", c))); 937 | } 938 | F32Const(c) => { 939 | expr_builder.push(( 940 | precedence::FUNCTION_CALL, 941 | format!("f32::from_bits({:#X})", c as u32), 942 | )); 943 | } 944 | F64Const(c) => { 945 | expr_builder.push(( 946 | precedence::FUNCTION_CALL, 947 | format!("f64::from_bits({:#X})", c as u64), 948 | )); 949 | } 950 | I32Eqz => { 951 | expr_builder.unary_individual(precedence::COMPARISON, precedence::AS, |a| { 952 | format!("({} == 0) as i32", a) 953 | }); 954 | } 955 | I32Eq => { 956 | expr_builder.binary_individual( 957 | precedence::COMPARISON, 958 | precedence::COMPARISON, 959 | precedence::AS, 960 | |a, b| format!("({} == {}) as i32", a, b), 961 | ); 962 | } 963 | I32Ne => { 964 | expr_builder.binary_individual( 965 | precedence::COMPARISON, 966 | precedence::COMPARISON, 967 | precedence::AS, 968 | |a, b| format!("({} != {}) as i32", a, b), 969 | ); 970 | } 971 | I32LtS => { 972 | expr_builder.binary_individual( 973 | precedence::COMPARISON, 974 | precedence::COMPARISON, 975 | precedence::AS, 976 | |a, b| format!("({} < {}) as i32", a, b), 977 | ); 978 | } 979 | I32LtU => { 980 | expr_builder.binary(precedence::AS, |a, b| { 981 | format!("(({} as u32) < {} as u32) as i32", a, b) 982 | }); 983 | } 984 | I32GtS => { 985 | expr_builder.binary_individual( 986 | precedence::COMPARISON, 987 | precedence::COMPARISON, 988 | precedence::AS, 989 | |a, b| format!("({} > {}) as i32", a, b), 990 | ); 991 | } 992 | I32GtU => { 993 | expr_builder.binary(precedence::AS, |a, b| { 994 | format!("({} as u32 > {} as u32) as i32", a, b) 995 | }); 996 | } 997 | I32LeS => { 998 | expr_builder.binary_individual( 999 | precedence::COMPARISON, 1000 | precedence::COMPARISON, 1001 | precedence::AS, 1002 | |a, b| format!("({} <= {}) as i32", a, b), 1003 | ); 1004 | } 1005 | I32LeU => { 1006 | expr_builder.binary(precedence::AS, |a, b| { 1007 | format!("({} as u32 <= {} as u32) as i32", a, b) 1008 | }); 1009 | } 1010 | I32GeS => { 1011 | expr_builder.binary_individual( 1012 | precedence::COMPARISON, 1013 | precedence::COMPARISON, 1014 | precedence::AS, 1015 | |a, b| format!("({} >= {}) as i32", a, b), 1016 | ); 1017 | } 1018 | I32GeU => { 1019 | expr_builder.binary(precedence::AS, |a, b| { 1020 | format!("({} as u32 >= {} as u32) as i32", a, b) 1021 | }); 1022 | } 1023 | I64Eqz => { 1024 | expr_builder.unary_individual(precedence::COMPARISON, precedence::AS, |a| { 1025 | format!("({} == 0) as i32", a) 1026 | }); 1027 | } 1028 | I64Eq => { 1029 | expr_builder.binary_individual( 1030 | precedence::COMPARISON, 1031 | precedence::COMPARISON, 1032 | precedence::AS, 1033 | |a, b| format!("({} == {}) as i32", a, b), 1034 | ); 1035 | } 1036 | I64Ne => { 1037 | expr_builder.binary_individual( 1038 | precedence::COMPARISON, 1039 | precedence::COMPARISON, 1040 | precedence::AS, 1041 | |a, b| format!("({} != {}) as i32", a, b), 1042 | ); 1043 | } 1044 | I64LtS => { 1045 | expr_builder.binary_individual( 1046 | precedence::COMPARISON, 1047 | precedence::COMPARISON, 1048 | precedence::AS, 1049 | |a, b| format!("({} < {}) as i32", a, b), 1050 | ); 1051 | } 1052 | I64LtU => { 1053 | expr_builder.binary(precedence::AS, |a, b| { 1054 | format!("(({} as u64) < {} as u64) as i32", a, b) 1055 | }); 1056 | } 1057 | I64GtS => { 1058 | expr_builder.binary_individual( 1059 | precedence::COMPARISON, 1060 | precedence::COMPARISON, 1061 | precedence::AS, 1062 | |a, b| format!("({} > {}) as i32", a, b), 1063 | ); 1064 | } 1065 | I64GtU => { 1066 | expr_builder.binary(precedence::AS, |a, b| { 1067 | format!("({} as u64 > {} as u64) as i32", a, b) 1068 | }); 1069 | } 1070 | I64LeS => { 1071 | expr_builder.binary_individual( 1072 | precedence::COMPARISON, 1073 | precedence::COMPARISON, 1074 | precedence::AS, 1075 | |a, b| format!("({} <= {}) as i32", a, b), 1076 | ); 1077 | } 1078 | I64LeU => { 1079 | expr_builder.binary(precedence::AS, |a, b| { 1080 | format!("({} as u64 <= {} as u64) as i32", a, b) 1081 | }); 1082 | } 1083 | I64GeS => { 1084 | expr_builder.binary_individual( 1085 | precedence::COMPARISON, 1086 | precedence::COMPARISON, 1087 | precedence::AS, 1088 | |a, b| format!("({} >= {}) as i32", a, b), 1089 | ); 1090 | } 1091 | I64GeU => { 1092 | expr_builder.binary(precedence::AS, |a, b| { 1093 | format!("({} as u64 >= {} as u64) as i32", a, b) 1094 | }); 1095 | } 1096 | F32Eq => { 1097 | expr_builder.binary_individual( 1098 | precedence::COMPARISON, 1099 | precedence::COMPARISON, 1100 | precedence::AS, 1101 | |a, b| format!("({} == {}) as i32", a, b), 1102 | ); 1103 | } 1104 | F32Ne => { 1105 | expr_builder.binary_individual( 1106 | precedence::COMPARISON, 1107 | precedence::COMPARISON, 1108 | precedence::AS, 1109 | |a, b| format!("({} != {}) as i32", a, b), 1110 | ); 1111 | } 1112 | F32Lt => { 1113 | expr_builder.binary_individual( 1114 | precedence::COMPARISON, 1115 | precedence::COMPARISON, 1116 | precedence::AS, 1117 | |a, b| format!("({} < {}) as i32", a, b), 1118 | ); 1119 | } 1120 | F32Gt => { 1121 | expr_builder.binary_individual( 1122 | precedence::COMPARISON, 1123 | precedence::COMPARISON, 1124 | precedence::AS, 1125 | |a, b| format!("({} > {}) as i32", a, b), 1126 | ); 1127 | } 1128 | F32Le => { 1129 | expr_builder.binary_individual( 1130 | precedence::COMPARISON, 1131 | precedence::COMPARISON, 1132 | precedence::AS, 1133 | |a, b| format!("({} <= {}) as i32", a, b), 1134 | ); 1135 | } 1136 | F32Ge => { 1137 | expr_builder.binary_individual( 1138 | precedence::COMPARISON, 1139 | precedence::COMPARISON, 1140 | precedence::AS, 1141 | |a, b| format!("({} >= {}) as i32", a, b), 1142 | ); 1143 | } 1144 | F64Eq => { 1145 | expr_builder.binary_individual( 1146 | precedence::COMPARISON, 1147 | precedence::COMPARISON, 1148 | precedence::AS, 1149 | |a, b| format!("({} == {}) as i32", a, b), 1150 | ); 1151 | } 1152 | F64Ne => { 1153 | expr_builder.binary_individual( 1154 | precedence::COMPARISON, 1155 | precedence::COMPARISON, 1156 | precedence::AS, 1157 | |a, b| format!("({} != {}) as i32", a, b), 1158 | ); 1159 | } 1160 | F64Lt => { 1161 | expr_builder.binary_individual( 1162 | precedence::COMPARISON, 1163 | precedence::COMPARISON, 1164 | precedence::AS, 1165 | |a, b| format!("({} < {}) as i32", a, b), 1166 | ); 1167 | } 1168 | F64Gt => { 1169 | expr_builder.binary_individual( 1170 | precedence::COMPARISON, 1171 | precedence::COMPARISON, 1172 | precedence::AS, 1173 | |a, b| format!("({} > {}) as i32", a, b), 1174 | ); 1175 | } 1176 | F64Le => { 1177 | expr_builder.binary_individual( 1178 | precedence::COMPARISON, 1179 | precedence::COMPARISON, 1180 | precedence::AS, 1181 | |a, b| format!("({} <= {}) as i32", a, b), 1182 | ); 1183 | } 1184 | F64Ge => { 1185 | expr_builder.binary_individual( 1186 | precedence::COMPARISON, 1187 | precedence::COMPARISON, 1188 | precedence::AS, 1189 | |a, b| format!("({} >= {}) as i32", a, b), 1190 | ); 1191 | } 1192 | I32Clz => { 1193 | expr_builder.unary_individual(precedence::METHOD_CALL, precedence::AS, |a| { 1194 | format!("{}.leading_zeros() as i32", a) 1195 | }); 1196 | } 1197 | I32Ctz => { 1198 | expr_builder.unary_individual(precedence::METHOD_CALL, precedence::AS, |a| { 1199 | format!("{}.trailing_zeros() as i32", a) 1200 | }); 1201 | } 1202 | I32Popcnt => { 1203 | expr_builder.unary_individual(precedence::METHOD_CALL, precedence::AS, |a| { 1204 | format!("{}.count_ones() as i32", a) 1205 | }); 1206 | } 1207 | I32Add => { 1208 | expr_builder.method_call_one_arg(precedence::METHOD_CALL, |a, b| { 1209 | format!("{}.wrapping_add({})", a, b) 1210 | }); 1211 | } 1212 | I32Sub => { 1213 | expr_builder.method_call_one_arg(precedence::METHOD_CALL, |a, b| { 1214 | format!("{}.wrapping_sub({})", a, b) 1215 | }); 1216 | } 1217 | I32Mul => { 1218 | expr_builder.method_call_one_arg(precedence::METHOD_CALL, |a, b| { 1219 | format!("{}.wrapping_mul({})", a, b) 1220 | }); 1221 | } 1222 | I32DivS => { 1223 | expr_builder.binary_lr(precedence::DIV, |a, b| format!("{} / {}", a, b)); 1224 | } 1225 | I32DivU => { 1226 | expr_builder.binary(precedence::AS, |a, b| { 1227 | format!("({} as u32 / {} as u32) as i32", a, b) 1228 | }); 1229 | } 1230 | I32RemS => { 1231 | expr_builder.method_call_one_arg(precedence::METHOD_CALL, |a, b| { 1232 | format!("{}.wrapping_rem({})", a, b) 1233 | }); 1234 | } 1235 | I32RemU => { 1236 | expr_builder.binary(precedence::AS, |a, b| { 1237 | format!("({} as u32).wrapping_rem({} as u32) as i32", a, b) 1238 | }); 1239 | } 1240 | I32And => { 1241 | expr_builder.binary(precedence::BIT_AND, |a, b| format!("{} & {}", a, b)); 1242 | } 1243 | I32Or => { 1244 | expr_builder.binary(precedence::BIT_OR, |a, b| format!("{} | {}", a, b)); 1245 | } 1246 | I32Xor => { 1247 | expr_builder.binary(precedence::BIT_XOR, |a, b| format!("{} ^ {}", a, b)); 1248 | } 1249 | I32Shl => { 1250 | expr_builder.binary_individual( 1251 | precedence::METHOD_CALL, 1252 | precedence::AS, 1253 | precedence::METHOD_CALL, 1254 | |a, b| format!("{}.wrapping_shl({} as u32)", a, b), 1255 | ); 1256 | } 1257 | I32ShrS => { 1258 | expr_builder.binary_individual( 1259 | precedence::METHOD_CALL, 1260 | precedence::AS, 1261 | precedence::METHOD_CALL, 1262 | |a, b| format!("{}.wrapping_shr({} as u32)", a, b), 1263 | ); 1264 | } 1265 | I32ShrU => { 1266 | expr_builder.binary(precedence::AS, |a, b| { 1267 | format!("({} as u32).wrapping_shr({} as u32) as i32", a, b) 1268 | }); 1269 | } 1270 | I32Rotl => { 1271 | expr_builder.binary_individual( 1272 | precedence::METHOD_CALL, 1273 | precedence::AS, 1274 | precedence::METHOD_CALL, 1275 | |a, b| format!("{}.rotate_left({} as u32)", a, b), 1276 | ); 1277 | } 1278 | I32Rotr => { 1279 | expr_builder.binary_individual( 1280 | precedence::METHOD_CALL, 1281 | precedence::AS, 1282 | precedence::METHOD_CALL, 1283 | |a, b| format!("{}.rotate_right({} as u32)", a, b), 1284 | ); 1285 | } 1286 | I64Clz => { 1287 | expr_builder.unary_individual(precedence::METHOD_CALL, precedence::AS, |a| { 1288 | format!("{}.leading_zeros() as i64", a) 1289 | }); 1290 | } 1291 | I64Ctz => { 1292 | expr_builder.unary_individual(precedence::METHOD_CALL, precedence::AS, |a| { 1293 | format!("{}.trailing_zeros() as i64", a) 1294 | }); 1295 | } 1296 | I64Popcnt => { 1297 | expr_builder.unary_individual(precedence::METHOD_CALL, precedence::AS, |a| { 1298 | format!("{}.count_ones() as i64", a) 1299 | }); 1300 | } 1301 | I64Add => { 1302 | expr_builder.method_call_one_arg(precedence::METHOD_CALL, |a, b| { 1303 | format!("{}.wrapping_add({})", a, b) 1304 | }); 1305 | } 1306 | I64Sub => { 1307 | expr_builder.method_call_one_arg(precedence::METHOD_CALL, |a, b| { 1308 | format!("{}.wrapping_sub({})", a, b) 1309 | }); 1310 | } 1311 | I64Mul => { 1312 | expr_builder.method_call_one_arg(precedence::METHOD_CALL, |a, b| { 1313 | format!("{}.wrapping_mul({})", a, b) 1314 | }); 1315 | } 1316 | I64DivS => { 1317 | expr_builder.binary_lr(precedence::DIV, |a, b| format!("{} / {}", a, b)); 1318 | } 1319 | I64DivU => { 1320 | expr_builder.binary(precedence::AS, |a, b| { 1321 | format!("({} as u64 / {} as u64) as i64", a, b) 1322 | }); 1323 | } 1324 | I64RemS => { 1325 | expr_builder.method_call_one_arg(precedence::METHOD_CALL, |a, b| { 1326 | format!("{}.wrapping_rem({})", a, b) 1327 | }); 1328 | } 1329 | I64RemU => { 1330 | expr_builder.binary(precedence::AS, |a, b| { 1331 | format!("({} as u64).wrapping_rem({} as u64) as i64", a, b) 1332 | }); 1333 | } 1334 | I64And => { 1335 | expr_builder.binary(precedence::BIT_AND, |a, b| format!("{} & {}", a, b)); 1336 | } 1337 | I64Or => { 1338 | expr_builder.binary(precedence::BIT_OR, |a, b| format!("{} | {}", a, b)); 1339 | } 1340 | I64Xor => { 1341 | expr_builder.binary(precedence::BIT_XOR, |a, b| format!("{} ^ {}", a, b)); 1342 | } 1343 | I64Shl => { 1344 | expr_builder.binary_individual( 1345 | precedence::METHOD_CALL, 1346 | precedence::AS, 1347 | precedence::METHOD_CALL, 1348 | |a, b| format!("{}.wrapping_shl({} as u32)", a, b), 1349 | ); 1350 | } 1351 | I64ShrS => { 1352 | expr_builder.binary_individual( 1353 | precedence::METHOD_CALL, 1354 | precedence::AS, 1355 | precedence::METHOD_CALL, 1356 | |a, b| format!("{}.wrapping_shr({} as u32)", a, b), 1357 | ); 1358 | } 1359 | I64ShrU => { 1360 | expr_builder.binary(precedence::AS, |a, b| { 1361 | format!("({} as u64).wrapping_shr({} as u32) as i64", a, b) 1362 | }); 1363 | } 1364 | I64Rotl => { 1365 | expr_builder.binary_individual( 1366 | precedence::METHOD_CALL, 1367 | precedence::AS, 1368 | precedence::METHOD_CALL, 1369 | |a, b| format!("{}.rotate_left({} as u32)", a, b), 1370 | ); 1371 | } 1372 | I64Rotr => { 1373 | expr_builder.binary_individual( 1374 | precedence::METHOD_CALL, 1375 | precedence::AS, 1376 | precedence::METHOD_CALL, 1377 | |a, b| format!("{}.rotate_right({} as u32)", a, b), 1378 | ); 1379 | } 1380 | F32Abs => { 1381 | expr_builder.unary_individual( 1382 | precedence::METHOD_CALL, 1383 | precedence::FUNCTION_CALL, 1384 | |a| format!("f32::from_bits({}.to_bits() & 0x7FFF_FFFF)", a), 1385 | ); 1386 | } 1387 | F32Neg => { 1388 | expr_builder.unary_individual( 1389 | precedence::METHOD_CALL, 1390 | precedence::FUNCTION_CALL, 1391 | |a| format!("f32::from_bits({}.to_bits() ^ 0x8000_0000)", a), 1392 | ); 1393 | } 1394 | F32Ceil => { 1395 | expr_builder.unary(precedence::METHOD_CALL, |a| format!("{}.ceil()", a)); 1396 | } 1397 | F32Floor => { 1398 | expr_builder.unary(precedence::METHOD_CALL, |a| format!("{}.floor()", a)); 1399 | } 1400 | F32Trunc => { 1401 | expr_builder.unary(precedence::METHOD_CALL, |a| format!("{}.trunc()", a)); 1402 | } 1403 | F32Nearest => { 1404 | let (_, val) = expr_builder.pop().unwrap(); 1405 | writeln!( 1406 | writer, 1407 | "{0}let var{1} = {{ 1408 | {0} let val = {2}; 1409 | {0} let round = val.round(); 1410 | {0} if val.fract().abs() != 0.5 {{ 1411 | {0} round 1412 | {0} }} else if round % 2.0 == 1.0 {{ 1413 | {0} val.floor() 1414 | {0} }} else if round % 2.0 == -1.0 {{ 1415 | {0} val.ceil() 1416 | {0} }} else {{ 1417 | {0} round 1418 | {0} }} 1419 | {0}}};", 1420 | indentation, expr_index, val 1421 | ).unwrap(); 1422 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 1423 | expr_index += 1; 1424 | } 1425 | F32Sqrt => { 1426 | expr_builder.unary(precedence::METHOD_CALL, |a| format!("{}.sqrt()", a)); 1427 | } 1428 | F32Add => { 1429 | expr_builder.binary_lr(precedence::ADD, |a, b| format!("{} + {}", a, b)); 1430 | } 1431 | F32Sub => { 1432 | expr_builder.binary_lr(precedence::SUB, |a, b| format!("{} - {}", a, b)); 1433 | } 1434 | F32Mul => { 1435 | expr_builder.binary_lr(precedence::MUL, |a, b| format!("{} * {}", a, b)); 1436 | } 1437 | F32Div => { 1438 | expr_builder.binary_lr(precedence::DIV, |a, b| format!("{} / {}", a, b)); 1439 | } 1440 | F32Min => { 1441 | let (_, b) = expr_builder.pop().unwrap(); 1442 | let (_, a) = expr_builder.pop().unwrap(); 1443 | expr_builder.push(( 1444 | precedence::MAX, 1445 | format!("{{ let a = {}; let b = {}; if a.is_nan() || b.is_nan() {{ a }} else {{ a.min(b) }} }}", a, b), 1446 | )); 1447 | } 1448 | F32Max => { 1449 | let (_, b) = expr_builder.pop().unwrap(); 1450 | let (_, a) = expr_builder.pop().unwrap(); 1451 | expr_builder.push(( 1452 | precedence::MAX, 1453 | format!("{{ let a = {}; let b = {}; if a.is_nan() || b.is_nan() {{ a }} else {{ a.max(b) }} }}", a, b), 1454 | )); 1455 | } 1456 | F32Copysign => { 1457 | expr_builder.binary_individual( 1458 | precedence::METHOD_CALL, 1459 | precedence::METHOD_CALL, 1460 | precedence::FUNCTION_CALL, 1461 | |a, b| { 1462 | format!("f32::from_bits(({}.to_bits() & !(1 << 31)) | ({}.to_bits() & (1 << 31)))", a, b) 1463 | }, 1464 | ); 1465 | } 1466 | F64Abs => { 1467 | expr_builder.unary_individual( 1468 | precedence::METHOD_CALL, 1469 | precedence::FUNCTION_CALL, 1470 | |a| format!("f64::from_bits({}.to_bits() & 0x7FFF_FFFF_FFFF_FFFF)", a), 1471 | ); 1472 | } 1473 | F64Neg => { 1474 | expr_builder.unary_individual( 1475 | precedence::METHOD_CALL, 1476 | precedence::FUNCTION_CALL, 1477 | |a| format!("f64::from_bits({}.to_bits() ^ 0x8000_0000_0000_0000)", a), 1478 | ); 1479 | } 1480 | F64Ceil => { 1481 | expr_builder.unary(precedence::METHOD_CALL, |a| format!("{}.ceil()", a)); 1482 | } 1483 | F64Floor => { 1484 | expr_builder.unary(precedence::METHOD_CALL, |a| format!("{}.floor()", a)); 1485 | } 1486 | F64Trunc => { 1487 | expr_builder.unary(precedence::METHOD_CALL, |a| format!("{}.trunc()", a)); 1488 | } 1489 | F64Nearest => { 1490 | let (_, val) = expr_builder.pop().unwrap(); 1491 | writeln!( 1492 | writer, 1493 | "{0}let var{1} = {{ 1494 | {0} let val = {2}; 1495 | {0} let round = val.round(); 1496 | {0} if val.fract().abs() != 0.5 {{ 1497 | {0} round 1498 | {0} }} else if round % 2.0 == 1.0 {{ 1499 | {0} val.floor() 1500 | {0} }} else if round % 2.0 == -1.0 {{ 1501 | {0} val.ceil() 1502 | {0} }} else {{ 1503 | {0} round 1504 | {0} }} 1505 | {0}}};", 1506 | indentation, expr_index, val 1507 | ).unwrap(); 1508 | expr_builder.push((precedence::PATH, format!("var{}", expr_index))); 1509 | expr_index += 1; 1510 | } 1511 | F64Sqrt => { 1512 | expr_builder.unary(precedence::METHOD_CALL, |a| format!("{}.sqrt()", a)); 1513 | } 1514 | F64Add => { 1515 | expr_builder.binary_lr(precedence::ADD, |a, b| format!("{} + {}", a, b)); 1516 | } 1517 | F64Sub => { 1518 | expr_builder.binary_lr(precedence::SUB, |a, b| format!("{} - {}", a, b)); 1519 | } 1520 | F64Mul => { 1521 | expr_builder.binary_lr(precedence::MUL, |a, b| format!("{} * {}", a, b)); 1522 | } 1523 | F64Div => { 1524 | expr_builder.binary_lr(precedence::DIV, |a, b| format!("{} / {}", a, b)); 1525 | } 1526 | F64Min => { 1527 | let (_, b) = expr_builder.pop().unwrap(); 1528 | let (_, a) = expr_builder.pop().unwrap(); 1529 | expr_builder.push(( 1530 | precedence::MAX, 1531 | format!("{{ let a = {}; let b = {}; if a.is_nan() || b.is_nan() {{ a }} else {{ a.min(b) }} }}", a, b), 1532 | )); 1533 | } 1534 | F64Max => { 1535 | let (_, b) = expr_builder.pop().unwrap(); 1536 | let (_, a) = expr_builder.pop().unwrap(); 1537 | expr_builder.push(( 1538 | precedence::MAX, 1539 | format!("{{ let a = {}; let b = {}; if a.is_nan() || b.is_nan() {{ a }} else {{ a.max(b) }} }}", a, b), 1540 | )); 1541 | } 1542 | F64Copysign => { 1543 | expr_builder.binary_individual( 1544 | precedence::METHOD_CALL, 1545 | precedence::METHOD_CALL, 1546 | precedence::FUNCTION_CALL, 1547 | |a, b| { 1548 | format!("f64::from_bits(({}.to_bits() & !(1 << 63)) | ({}.to_bits() & (1 << 63)))", a, b) 1549 | }, 1550 | ); 1551 | } 1552 | I32WrapI64 => { 1553 | expr_builder.unary(precedence::AS, |a| format!("{} as i32", a)); 1554 | } 1555 | I32TruncSF32 => { 1556 | expr_builder.unary(precedence::AS, |a| format!("{} as i32", a)); 1557 | } 1558 | I32TruncUF32 => { 1559 | expr_builder.unary(precedence::AS, |a| format!("{} as u32 as i32", a)); 1560 | } 1561 | I32TruncSF64 => { 1562 | expr_builder.unary(precedence::AS, |a| format!("{} as i32", a)); 1563 | } 1564 | I32TruncUF64 => { 1565 | expr_builder.unary(precedence::AS, |a| format!("{} as u32 as i32", a)); 1566 | } 1567 | I64ExtendSI32 => { 1568 | expr_builder.unary(precedence::AS, |a| format!("{} as i64", a)); 1569 | } 1570 | I64ExtendUI32 => { 1571 | expr_builder.unary(precedence::AS, |a| format!("{} as u32 as i64", a)); 1572 | } 1573 | I64TruncSF32 => { 1574 | expr_builder.unary(precedence::AS, |a| format!("{} as i64", a)); 1575 | } 1576 | I64TruncUF32 => { 1577 | expr_builder.unary(precedence::AS, |a| format!("{} as u64 as i64", a)); 1578 | } 1579 | I64TruncSF64 => { 1580 | expr_builder.unary(precedence::AS, |a| format!("{} as i64", a)); 1581 | } 1582 | I64TruncUF64 => { 1583 | expr_builder.unary(precedence::AS, |a| format!("{} as u64 as i64", a)); 1584 | } 1585 | F32ConvertSI32 => { 1586 | expr_builder.unary(precedence::AS, |a| format!("{} as f32", a)); 1587 | } 1588 | F32ConvertUI32 => { 1589 | expr_builder.unary(precedence::AS, |a| format!("{} as u32 as f32", a)); 1590 | } 1591 | F32ConvertSI64 => { 1592 | expr_builder.unary(precedence::AS, |a| format!("{} as f32", a)); 1593 | } 1594 | F32ConvertUI64 => { 1595 | expr_builder.unary(precedence::AS, |a| format!("{} as u64 as f32", a)); 1596 | } 1597 | F32DemoteF64 => { 1598 | expr_builder.unary(precedence::AS, |a| format!("{} as f32", a)); 1599 | } 1600 | F64ConvertSI32 => { 1601 | expr_builder.unary(precedence::AS, |a| format!("{} as f64", a)); 1602 | } 1603 | F64ConvertUI32 => { 1604 | expr_builder.unary(precedence::AS, |a| format!("{} as u32 as f64", a)); 1605 | } 1606 | F64ConvertSI64 => { 1607 | expr_builder.unary(precedence::AS, |a| format!("{} as f64", a)); 1608 | } 1609 | F64ConvertUI64 => { 1610 | expr_builder.unary(precedence::AS, |a| format!("{} as u64 as f64", a)); 1611 | } 1612 | F64PromoteF32 => { 1613 | expr_builder.unary(precedence::AS, |a| format!("{} as f64", a)); 1614 | } 1615 | I32ReinterpretF32 => { 1616 | expr_builder.unary_individual(precedence::METHOD_CALL, precedence::AS, |a| { 1617 | format!("{}.to_bits() as i32", a) 1618 | }); 1619 | } 1620 | I64ReinterpretF64 => { 1621 | expr_builder.unary_individual(precedence::METHOD_CALL, precedence::AS, |a| { 1622 | format!("{}.to_bits() as i64", a) 1623 | }); 1624 | } 1625 | F32ReinterpretI32 => { 1626 | expr_builder.unary_individual(precedence::AS, precedence::FUNCTION_CALL, |a| { 1627 | format!("f32::from_bits({} as u32)", a) 1628 | }); 1629 | } 1630 | F64ReinterpretI64 => { 1631 | expr_builder.unary_individual(precedence::AS, precedence::FUNCTION_CALL, |a| { 1632 | format!("f64::from_bits({} as u64)", a) 1633 | }); 1634 | } 1635 | } 1636 | } 1637 | } 1638 | --------------------------------------------------------------------------------