Skip to main content

oxihuman_core/
wasm_bridge.rs

1//! WASM host/guest interop bridge with a real MVP bytecode interpreter.
2//!
3//! The interpreter is a stack machine that executes WASM MVP bytecode stored in
4//! `WasmFunction::bytecode` against the bridge's shared linear memory.
5
6#[allow(dead_code)]
7#[derive(Debug, Clone, PartialEq)]
8pub enum WasmValueType {
9    I32,
10    I64,
11    F32,
12    F64,
13    FuncRef,
14    ExternRef,
15}
16
17/// A typed WASM runtime value.
18#[allow(dead_code)]
19#[derive(Debug, Clone, PartialEq)]
20pub enum WasmValue {
21    I32(i32),
22    I64(i64),
23    F32(f32),
24    F64(f64),
25}
26
27/// Errors that can occur during WASM execution.
28#[derive(Debug, thiserror::Error)]
29pub enum WasmError {
30    #[error("Function not found: {0}")]
31    FunctionNotFound(String),
32    #[error("Type mismatch: {0}")]
33    TypeMismatch(String),
34    #[error("Runtime trap: {0}")]
35    Trap(String),
36    #[error("Memory out of bounds at offset {0}")]
37    MemoryOutOfBounds(usize),
38    #[error("Division by zero")]
39    DivisionByZero,
40    #[error("Stack overflow")]
41    StackOverflow,
42    #[error("Unimplemented opcode: 0x{0:02X}")]
43    UnimplementedOpcode(u8),
44}
45
46#[allow(dead_code)]
47#[derive(Debug, Clone)]
48pub struct WasmBridgeConfig {
49    pub memory_pages: u32,
50    pub max_functions: usize,
51    pub debug_mode: bool,
52}
53
54#[allow(dead_code)]
55#[derive(Debug, Clone)]
56pub struct WasmMemory {
57    pub data: Vec<u8>,
58    pub size_bytes: usize,
59}
60
61/// A WASM function with metadata and executable bytecode.
62///
63/// `bytecode` = `[local_decl_count : LEB128] (count LEB128 + type byte)* expression_opcodes`
64/// An empty `bytecode` is treated as an empty function that returns with no values.
65#[allow(dead_code)]
66#[derive(Debug, Clone)]
67pub struct WasmFunction {
68    pub name: String,
69    pub param_types: Vec<WasmValueType>,
70    pub return_types: Vec<WasmValueType>,
71    pub bytecode: Vec<u8>,
72    pub local_types: Vec<WasmValueType>,
73}
74
75#[allow(dead_code)]
76#[derive(Debug, Clone)]
77pub struct WasmBridge {
78    pub config: WasmBridgeConfig,
79    pub memory: WasmMemory,
80    pub functions: Vec<WasmFunction>,
81    pub call_count: u64,
82}
83
84// ─── Block / control-flow machinery ─────────────────────────────────────────
85
86#[derive(Debug, Clone, PartialEq)]
87enum BlockKind {
88    Block,
89    Loop,
90    If,
91}
92
93#[derive(Debug, Clone)]
94struct BlockFrame {
95    kind: BlockKind,
96    arity: usize,
97    height: usize,
98    /// Block/If: end PC.  Loop: loop-header PC (for back-edge).
99    else_or_end_pc: usize,
100}
101
102// ─── LEB128 helpers ──────────────────────────────────────────────────────────
103
104fn read_leb128_u32(code: &[u8], pc: &mut usize) -> u32 {
105    let (mut r, mut s) = (0u32, 0u32);
106    loop {
107        let b = code[*pc];
108        *pc += 1;
109        r |= ((b & 0x7F) as u32) << s;
110        if b & 0x80 == 0 {
111            break;
112        }
113        s += 7;
114    }
115    r
116}
117
118#[allow(dead_code)]
119fn read_leb128_u64(code: &[u8], pc: &mut usize) -> u64 {
120    let (mut r, mut s) = (0u64, 0u32);
121    loop {
122        let b = code[*pc];
123        *pc += 1;
124        r |= ((b & 0x7F) as u64) << s;
125        if b & 0x80 == 0 {
126            break;
127        }
128        s += 7;
129    }
130    r
131}
132
133fn read_leb128_i32(code: &[u8], pc: &mut usize) -> i32 {
134    let (mut r, mut s) = (0i32, 0u32);
135    let last = loop {
136        let b = code[*pc];
137        *pc += 1;
138        r |= ((b & 0x7F) as i32) << s;
139        s += 7;
140        if b & 0x80 == 0 {
141            break b;
142        }
143    };
144    if s < 32 && (last & 0x40) != 0 {
145        r |= -1i32 << s;
146    }
147    r
148}
149
150fn read_leb128_i64(code: &[u8], pc: &mut usize) -> i64 {
151    let (mut r, mut s) = (0i64, 0u32);
152    let last = loop {
153        let b = code[*pc];
154        *pc += 1;
155        r |= ((b & 0x7F) as i64) << s;
156        s += 7;
157        if b & 0x80 == 0 {
158            break b;
159        }
160    };
161    if s < 64 && (last & 0x40) != 0 {
162        r |= -1i64 << s;
163    }
164    r
165}
166
167fn skip_leb128(code: &[u8], pc: &mut usize) {
168    while *pc < code.len() {
169        let b = code[*pc];
170        *pc += 1;
171        if b & 0x80 == 0 {
172            break;
173        }
174    }
175}
176
177// ─── Interpreter ─────────────────────────────────────────────────────────────
178
179const MAX_CALL_DEPTH: usize = 512;
180const MAX_STACK_DEPTH: usize = 65536;
181
182struct Interpreter<'a> {
183    stack: Vec<WasmValue>,
184    locals: Vec<WasmValue>,
185    memory: &'a mut WasmMemory,
186    code: &'a [u8],
187    pc: usize,
188    blocks: Vec<BlockFrame>,
189    call_depth: usize,
190}
191
192impl<'a> Interpreter<'a> {
193    fn new(
194        memory: &'a mut WasmMemory,
195        code: &'a [u8],
196        locals: Vec<WasmValue>,
197        call_depth: usize,
198    ) -> Self {
199        Interpreter {
200            stack: Vec::new(),
201            locals,
202            memory,
203            code,
204            pc: 0,
205            blocks: Vec::new(),
206            call_depth,
207        }
208    }
209
210    // ── stack helpers ─────────────────────────────────────────────────────────
211
212    fn push(&mut self, v: WasmValue) -> Result<(), WasmError> {
213        if self.stack.len() >= MAX_STACK_DEPTH {
214            return Err(WasmError::StackOverflow);
215        }
216        self.stack.push(v);
217        Ok(())
218    }
219    fn pop(&mut self) -> Result<WasmValue, WasmError> {
220        self.stack
221            .pop()
222            .ok_or_else(|| WasmError::Trap("value stack underflow".into()))
223    }
224    fn pop_i32(&mut self) -> Result<i32, WasmError> {
225        match self.pop()? {
226            WasmValue::I32(v) => Ok(v),
227            o => Err(WasmError::TypeMismatch(format!(
228                "expected i32, got {:?}",
229                o
230            ))),
231        }
232    }
233    fn pop_i64(&mut self) -> Result<i64, WasmError> {
234        match self.pop()? {
235            WasmValue::I64(v) => Ok(v),
236            o => Err(WasmError::TypeMismatch(format!(
237                "expected i64, got {:?}",
238                o
239            ))),
240        }
241    }
242    fn pop_f32(&mut self) -> Result<f32, WasmError> {
243        match self.pop()? {
244            WasmValue::F32(v) => Ok(v),
245            o => Err(WasmError::TypeMismatch(format!(
246                "expected f32, got {:?}",
247                o
248            ))),
249        }
250    }
251    fn pop_f64(&mut self) -> Result<f64, WasmError> {
252        match self.pop()? {
253            WasmValue::F64(v) => Ok(v),
254            o => Err(WasmError::TypeMismatch(format!(
255                "expected f64, got {:?}",
256                o
257            ))),
258        }
259    }
260    #[allow(dead_code)]
261    fn peek_byte(&self) -> Option<u8> {
262        self.code.get(self.pc).copied()
263    }
264
265    // ── bytecode reading ──────────────────────────────────────────────────────
266
267    fn read_byte(&mut self) -> Result<u8, WasmError> {
268        if self.pc >= self.code.len() {
269            return Err(WasmError::Trap("unexpected end of bytecode".into()));
270        }
271        let b = self.code[self.pc];
272        self.pc += 1;
273        Ok(b)
274    }
275    fn read_u32_leb(&mut self) -> Result<u32, WasmError> {
276        if self.pc >= self.code.len() {
277            return Err(WasmError::Trap("unexpected end (LEB128 u32)".into()));
278        }
279        Ok(read_leb128_u32(self.code, &mut self.pc))
280    }
281    fn read_i32_leb(&mut self) -> Result<i32, WasmError> {
282        if self.pc >= self.code.len() {
283            return Err(WasmError::Trap("unexpected end (LEB128 i32)".into()));
284        }
285        Ok(read_leb128_i32(self.code, &mut self.pc))
286    }
287    fn read_i64_leb(&mut self) -> Result<i64, WasmError> {
288        if self.pc >= self.code.len() {
289            return Err(WasmError::Trap("unexpected end (LEB128 i64)".into()));
290        }
291        Ok(read_leb128_i64(self.code, &mut self.pc))
292    }
293    fn read_f32(&mut self) -> Result<f32, WasmError> {
294        if self.pc + 4 > self.code.len() {
295            return Err(WasmError::Trap("unexpected end (f32)".into()));
296        }
297        let b = [
298            self.code[self.pc],
299            self.code[self.pc + 1],
300            self.code[self.pc + 2],
301            self.code[self.pc + 3],
302        ];
303        self.pc += 4;
304        Ok(f32::from_le_bytes(b))
305    }
306    fn read_f64(&mut self) -> Result<f64, WasmError> {
307        if self.pc + 8 > self.code.len() {
308            return Err(WasmError::Trap("unexpected end (f64)".into()));
309        }
310        let b: [u8; 8] = self.code[self.pc..self.pc + 8].try_into().unwrap_or([0; 8]);
311        self.pc += 8;
312        Ok(f64::from_le_bytes(b))
313    }
314
315    // ── memory load/store ─────────────────────────────────────────────────────
316
317    fn effective_addr(&self, base: u32, offset: u32) -> usize {
318        (base as usize).wrapping_add(offset as usize)
319    }
320
321    // Loads — returns error if out of bounds.
322    fn mload_i32(&self, a: usize) -> Result<i32, WasmError> {
323        if a + 4 > self.memory.data.len() {
324            return Err(WasmError::MemoryOutOfBounds(a));
325        }
326        Ok(i32::from_le_bytes(
327            self.memory.data[a..a + 4].try_into().unwrap_or([0; 4]),
328        ))
329    }
330    fn mload_i64(&self, a: usize) -> Result<i64, WasmError> {
331        if a + 8 > self.memory.data.len() {
332            return Err(WasmError::MemoryOutOfBounds(a));
333        }
334        Ok(i64::from_le_bytes(
335            self.memory.data[a..a + 8].try_into().unwrap_or([0; 8]),
336        ))
337    }
338    fn mload_f32(&self, a: usize) -> Result<f32, WasmError> {
339        if a + 4 > self.memory.data.len() {
340            return Err(WasmError::MemoryOutOfBounds(a));
341        }
342        Ok(f32::from_le_bytes(
343            self.memory.data[a..a + 4].try_into().unwrap_or([0; 4]),
344        ))
345    }
346    fn mload_f64(&self, a: usize) -> Result<f64, WasmError> {
347        if a + 8 > self.memory.data.len() {
348            return Err(WasmError::MemoryOutOfBounds(a));
349        }
350        Ok(f64::from_le_bytes(
351            self.memory.data[a..a + 8].try_into().unwrap_or([0; 8]),
352        ))
353    }
354    fn mload_i32_ext(&self, a: usize, bytes: usize, signed: bool) -> Result<i32, WasmError> {
355        if a + bytes > self.memory.data.len() {
356            return Err(WasmError::MemoryOutOfBounds(a));
357        }
358        let mut buf = [0u8; 4];
359        buf[..bytes].copy_from_slice(&self.memory.data[a..a + bytes]);
360        let raw = u32::from_le_bytes(buf);
361        Ok(if signed {
362            sign_extend_u32(raw, bytes * 8)
363        } else {
364            raw as i32
365        })
366    }
367    fn mload_i64_ext(&self, a: usize, bytes: usize, signed: bool) -> Result<i64, WasmError> {
368        if a + bytes > self.memory.data.len() {
369            return Err(WasmError::MemoryOutOfBounds(a));
370        }
371        let mut buf = [0u8; 8];
372        buf[..bytes].copy_from_slice(&self.memory.data[a..a + bytes]);
373        let raw = u64::from_le_bytes(buf);
374        Ok(if signed {
375            sign_extend_u64(raw, bytes * 8)
376        } else {
377            raw as i64
378        })
379    }
380
381    // Stores
382    fn mstore_i32(&mut self, a: usize, v: i32) -> Result<(), WasmError> {
383        if a + 4 > self.memory.data.len() {
384            return Err(WasmError::MemoryOutOfBounds(a));
385        }
386        self.memory.data[a..a + 4].copy_from_slice(&v.to_le_bytes());
387        Ok(())
388    }
389    fn mstore_i64(&mut self, a: usize, v: i64) -> Result<(), WasmError> {
390        if a + 8 > self.memory.data.len() {
391            return Err(WasmError::MemoryOutOfBounds(a));
392        }
393        self.memory.data[a..a + 8].copy_from_slice(&v.to_le_bytes());
394        Ok(())
395    }
396    fn mstore_f32(&mut self, a: usize, v: f32) -> Result<(), WasmError> {
397        if a + 4 > self.memory.data.len() {
398            return Err(WasmError::MemoryOutOfBounds(a));
399        }
400        self.memory.data[a..a + 4].copy_from_slice(&v.to_le_bytes());
401        Ok(())
402    }
403    fn mstore_f64(&mut self, a: usize, v: f64) -> Result<(), WasmError> {
404        if a + 8 > self.memory.data.len() {
405            return Err(WasmError::MemoryOutOfBounds(a));
406        }
407        self.memory.data[a..a + 8].copy_from_slice(&v.to_le_bytes());
408        Ok(())
409    }
410    fn mstore_trunc(&mut self, a: usize, bytes: usize, v: u64) -> Result<(), WasmError> {
411        if a + bytes > self.memory.data.len() {
412            return Err(WasmError::MemoryOutOfBounds(a));
413        }
414        let src = v.to_le_bytes();
415        self.memory.data[a..a + bytes].copy_from_slice(&src[..bytes]);
416        Ok(())
417    }
418
419    // ── mem_arg helper (reads align+offset) ──────────────────────────────────
420
421    fn read_mem_arg(&mut self) -> Result<u32, WasmError> {
422        let _align = self.read_u32_leb()?;
423        self.read_u32_leb()
424    }
425
426    fn pop_addr_offset(&mut self, offset: u32) -> Result<usize, WasmError> {
427        let base = self.pop_i32()? as u32;
428        Ok(self.effective_addr(base, offset))
429    }
430
431    // ── scan helper (for block entry) ─────────────────────────────────────────
432
433    /// Scan forward from `start_pc` to find the matching `else` and `end` offsets.
434    /// Returns `(else_pc, end_pc)`; if no else, `else_pc == end_pc`.
435    fn scan_to_else_or_end(&self, start_pc: usize) -> Result<(usize, usize), WasmError> {
436        let (mut depth, mut pc) = (1usize, start_pc);
437        let mut first_else: Option<usize> = None;
438        while pc < self.code.len() {
439            let op = self.code[pc];
440            pc += 1;
441            match op {
442                0x02..=0x04 => {
443                    depth += 1;
444                    pc += 1;
445                } // nested block/loop/if + block_type
446                0x05 if depth == 1 && first_else.is_none() => {
447                    first_else = Some(pc - 1);
448                }
449                0x0B => {
450                    depth -= 1;
451                    if depth == 0 {
452                        let end_pc = pc - 1;
453                        return Ok((first_else.unwrap_or(end_pc), end_pc));
454                    }
455                }
456                0x20..=0x24 => {
457                    skip_leb128(self.code, &mut pc);
458                }
459                0x28..=0x3E => {
460                    skip_leb128(self.code, &mut pc);
461                    skip_leb128(self.code, &mut pc);
462                }
463                0x3F | 0x40 => {
464                    pc += 1;
465                }
466                0x41 => {
467                    skip_leb128(self.code, &mut pc);
468                }
469                0x42 => {
470                    skip_leb128(self.code, &mut pc);
471                }
472                0x43 => {
473                    pc += 4;
474                }
475                0x44 => {
476                    pc += 8;
477                }
478                0x0C | 0x0D => {
479                    skip_leb128(self.code, &mut pc);
480                }
481                0x0E => {
482                    let n = read_leb128_u32(self.code, &mut pc) as usize;
483                    for _ in 0..=n {
484                        skip_leb128(self.code, &mut pc);
485                    }
486                }
487                0x10 | 0x11 => {
488                    skip_leb128(self.code, &mut pc);
489                    if op == 0x11 {
490                        skip_leb128(self.code, &mut pc);
491                    }
492                }
493                _ => {}
494            }
495        }
496        Err(WasmError::Trap("unmatched block (no end found)".into()))
497    }
498
499    // ── branch helper ─────────────────────────────────────────────────────────
500
501    fn do_branch(&mut self, depth: u32) -> Result<Option<usize>, WasmError> {
502        let depth = depth as usize;
503        if depth >= self.blocks.len() {
504            return Ok(None);
505        }
506        let idx = self.blocks.len() - 1 - depth;
507        let frame = self.blocks[idx].clone();
508        let slen = self.stack.len();
509        if slen < frame.arity {
510            return Err(WasmError::Trap("branch: stack underflow".into()));
511        }
512        let results: Vec<WasmValue> = self.stack[slen - frame.arity..].to_vec();
513        self.stack.truncate(frame.height);
514        for v in results {
515            self.push(v)?;
516        }
517        self.blocks.truncate(idx);
518        let new_pc = match frame.kind {
519            BlockKind::Loop => frame.else_or_end_pc,
520            _ => {
521                let end = frame.else_or_end_pc;
522                if end < self.code.len() && self.code[end] == 0x0B {
523                    end + 1
524                } else {
525                    end
526                }
527            }
528        };
529        Ok(Some(new_pc))
530    }
531
532    // ── main dispatch loop ────────────────────────────────────────────────────
533
534    fn run(&mut self, n_results: usize) -> Result<Vec<WasmValue>, WasmError> {
535        if self.call_depth > MAX_CALL_DEPTH {
536            return Err(WasmError::StackOverflow);
537        }
538
539        while self.pc < self.code.len() {
540            let op = self.read_byte()?;
541            match op {
542                // ── control ───────────────────────────────────────────────────
543                0x00 => return Err(WasmError::Trap("unreachable".into())),
544                0x01 => { /* nop */ }
545                0x02 => {
546                    // block
547                    let _ = self.read_byte()?;
548                    let scan = self.pc;
549                    let (_ep, end) = self.scan_to_else_or_end(scan)?;
550                    self.blocks.push(BlockFrame {
551                        kind: BlockKind::Block,
552                        arity: 0,
553                        height: self.stack.len(),
554                        else_or_end_pc: end,
555                    });
556                }
557                0x03 => {
558                    // loop
559                    let _ = self.read_byte()?;
560                    let hdr = self.pc;
561                    let scan = self.pc;
562                    let _ = self.scan_to_else_or_end(scan)?;
563                    self.blocks.push(BlockFrame {
564                        kind: BlockKind::Loop,
565                        arity: 0,
566                        height: self.stack.len(),
567                        else_or_end_pc: hdr,
568                    });
569                }
570                0x04 => {
571                    // if
572                    let _ = self.read_byte()?;
573                    let cond = self.pop_i32()?;
574                    let scan = self.pc;
575                    let (ep, end) = self.scan_to_else_or_end(scan)?;
576                    self.blocks.push(BlockFrame {
577                        kind: BlockKind::If,
578                        arity: 0,
579                        height: self.stack.len(),
580                        else_or_end_pc: end,
581                    });
582                    if cond == 0 {
583                        if ep < end {
584                            self.pc = ep + 1;
585                        } else {
586                            self.pc = end + 1;
587                            self.blocks.pop();
588                        }
589                    }
590                }
591                0x05 => {
592                    // else (reached in then-branch)
593                    let end = self
594                        .blocks
595                        .last()
596                        .map(|f| f.else_or_end_pc)
597                        .ok_or_else(|| WasmError::Trap("else without if".into()))?;
598                    self.pc = end + 1;
599                    self.blocks.pop();
600                }
601                0x0B => {
602                    // end
603                    if self.blocks.is_empty() {
604                        break;
605                    }
606                    let frame = self.blocks.pop().expect("block frame");
607                    let slen = self.stack.len();
608                    if slen < frame.arity {
609                        return Err(WasmError::Trap("end: stack underflow".into()));
610                    }
611                    let res: Vec<WasmValue> = self.stack[slen - frame.arity..].to_vec();
612                    self.stack.truncate(frame.height);
613                    for v in res {
614                        self.push(v)?;
615                    }
616                }
617                0x0C => {
618                    let d = self.read_u32_leb()?;
619                    match self.do_branch(d)? {
620                        Some(p) => self.pc = p,
621                        None => break,
622                    }
623                }
624                0x0D => {
625                    let d = self.read_u32_leb()?;
626                    let c = self.pop_i32()?;
627                    if c != 0 {
628                        match self.do_branch(d)? {
629                            Some(p) => self.pc = p,
630                            None => break,
631                        }
632                    }
633                }
634                0x0E => {
635                    // br_table
636                    let n = self.read_u32_leb()? as usize;
637                    let mut lbs = Vec::with_capacity(n + 1);
638                    for _ in 0..=n {
639                        lbs.push(self.read_u32_leb()?);
640                    }
641                    let idx = self.pop_i32()? as usize;
642                    let d = if idx < n { lbs[idx] } else { lbs[n] };
643                    match self.do_branch(d)? {
644                        Some(p) => self.pc = p,
645                        None => break,
646                    }
647                }
648                0x0F => break, // return
649                0x10 => {
650                    let _ = self.read_u32_leb()?;
651                    return Err(WasmError::Trap("call not supported".into()));
652                }
653                0x11 => {
654                    let _ = self.read_u32_leb()?;
655                    let _ = self.read_u32_leb()?;
656                    return Err(WasmError::Trap("call_indirect not supported".into()));
657                }
658
659                // ── parametric ────────────────────────────────────────────────
660                0x1A => {
661                    self.pop()?;
662                }
663                0x1B => {
664                    let c = self.pop_i32()?;
665                    let b = self.pop()?;
666                    let a = self.pop()?;
667                    self.push(if c != 0 { a } else { b })?;
668                }
669
670                // ── locals ────────────────────────────────────────────────────
671                0x20 => {
672                    let i = self.read_u32_leb()? as usize;
673                    let v = self
674                        .locals
675                        .get(i)
676                        .cloned()
677                        .ok_or_else(|| WasmError::Trap(format!("local.get: {} oob", i)))?;
678                    self.push(v)?;
679                }
680                0x21 => {
681                    let i = self.read_u32_leb()? as usize;
682                    let v = self.pop()?;
683                    *self
684                        .locals
685                        .get_mut(i)
686                        .ok_or_else(|| WasmError::Trap(format!("local.set: {} oob", i)))? = v;
687                }
688                0x22 => {
689                    let i = self.read_u32_leb()? as usize;
690                    let v = self
691                        .stack
692                        .last()
693                        .cloned()
694                        .ok_or_else(|| WasmError::Trap("local.tee: underflow".into()))?;
695                    *self
696                        .locals
697                        .get_mut(i)
698                        .ok_or_else(|| WasmError::Trap(format!("local.tee: {} oob", i)))? = v;
699                }
700                0x23 => {
701                    let _ = self.read_u32_leb()?;
702                    return Err(WasmError::Trap("global.get not supported".into()));
703                }
704                0x24 => {
705                    let _ = self.read_u32_leb()?;
706                    return Err(WasmError::Trap("global.set not supported".into()));
707                }
708
709                // ── memory loads (0x28..=0x35) ────────────────────────────────
710                0x28 => {
711                    let off = self.read_mem_arg()?;
712                    let a = self.pop_addr_offset(off)?;
713                    let v = self.mload_i32(a)?;
714                    self.push(WasmValue::I32(v))?;
715                }
716                0x29 => {
717                    let off = self.read_mem_arg()?;
718                    let a = self.pop_addr_offset(off)?;
719                    let v = self.mload_i64(a)?;
720                    self.push(WasmValue::I64(v))?;
721                }
722                0x2A => {
723                    let off = self.read_mem_arg()?;
724                    let a = self.pop_addr_offset(off)?;
725                    let v = self.mload_f32(a)?;
726                    self.push(WasmValue::F32(v))?;
727                }
728                0x2B => {
729                    let off = self.read_mem_arg()?;
730                    let a = self.pop_addr_offset(off)?;
731                    let v = self.mload_f64(a)?;
732                    self.push(WasmValue::F64(v))?;
733                }
734                0x2C => {
735                    let off = self.read_mem_arg()?;
736                    let a = self.pop_addr_offset(off)?;
737                    let v = self.mload_i32_ext(a, 1, true)?;
738                    self.push(WasmValue::I32(v))?;
739                }
740                0x2D => {
741                    let off = self.read_mem_arg()?;
742                    let a = self.pop_addr_offset(off)?;
743                    let v = self.mload_i32_ext(a, 1, false)?;
744                    self.push(WasmValue::I32(v))?;
745                }
746                0x2E => {
747                    let off = self.read_mem_arg()?;
748                    let a = self.pop_addr_offset(off)?;
749                    let v = self.mload_i32_ext(a, 2, true)?;
750                    self.push(WasmValue::I32(v))?;
751                }
752                0x2F => {
753                    let off = self.read_mem_arg()?;
754                    let a = self.pop_addr_offset(off)?;
755                    let v = self.mload_i32_ext(a, 2, false)?;
756                    self.push(WasmValue::I32(v))?;
757                }
758                0x30 => {
759                    let off = self.read_mem_arg()?;
760                    let a = self.pop_addr_offset(off)?;
761                    let v = self.mload_i64_ext(a, 1, true)?;
762                    self.push(WasmValue::I64(v))?;
763                }
764                0x31 => {
765                    let off = self.read_mem_arg()?;
766                    let a = self.pop_addr_offset(off)?;
767                    let v = self.mload_i64_ext(a, 1, false)?;
768                    self.push(WasmValue::I64(v))?;
769                }
770                0x32 => {
771                    let off = self.read_mem_arg()?;
772                    let a = self.pop_addr_offset(off)?;
773                    let v = self.mload_i64_ext(a, 2, true)?;
774                    self.push(WasmValue::I64(v))?;
775                }
776                0x33 => {
777                    let off = self.read_mem_arg()?;
778                    let a = self.pop_addr_offset(off)?;
779                    let v = self.mload_i64_ext(a, 2, false)?;
780                    self.push(WasmValue::I64(v))?;
781                }
782                0x34 => {
783                    let off = self.read_mem_arg()?;
784                    let a = self.pop_addr_offset(off)?;
785                    let v = self.mload_i64_ext(a, 4, true)?;
786                    self.push(WasmValue::I64(v))?;
787                }
788                0x35 => {
789                    let off = self.read_mem_arg()?;
790                    let a = self.pop_addr_offset(off)?;
791                    let v = self.mload_i64_ext(a, 4, false)?;
792                    self.push(WasmValue::I64(v))?;
793                }
794
795                // ── memory stores (0x36..=0x3E) ───────────────────────────────
796                0x36 => {
797                    let off = self.read_mem_arg()?;
798                    let v = self.pop_i32()?;
799                    let a = self.pop_addr_offset(off)?;
800                    self.mstore_i32(a, v)?;
801                }
802                0x37 => {
803                    let off = self.read_mem_arg()?;
804                    let v = self.pop_i64()?;
805                    let a = self.pop_addr_offset(off)?;
806                    self.mstore_i64(a, v)?;
807                }
808                0x38 => {
809                    let off = self.read_mem_arg()?;
810                    let v = self.pop_f32()?;
811                    let a = self.pop_addr_offset(off)?;
812                    self.mstore_f32(a, v)?;
813                }
814                0x39 => {
815                    let off = self.read_mem_arg()?;
816                    let v = self.pop_f64()?;
817                    let a = self.pop_addr_offset(off)?;
818                    self.mstore_f64(a, v)?;
819                }
820                0x3A => {
821                    let off = self.read_mem_arg()?;
822                    let v = self.pop_i32()? as u64;
823                    let a = self.pop_addr_offset(off)?;
824                    self.mstore_trunc(a, 1, v)?;
825                }
826                0x3B => {
827                    let off = self.read_mem_arg()?;
828                    let v = self.pop_i32()? as u64;
829                    let a = self.pop_addr_offset(off)?;
830                    self.mstore_trunc(a, 2, v)?;
831                }
832                0x3C => {
833                    let off = self.read_mem_arg()?;
834                    let v = self.pop_i64()? as u64;
835                    let a = self.pop_addr_offset(off)?;
836                    self.mstore_trunc(a, 1, v)?;
837                }
838                0x3D => {
839                    let off = self.read_mem_arg()?;
840                    let v = self.pop_i64()? as u64;
841                    let a = self.pop_addr_offset(off)?;
842                    self.mstore_trunc(a, 2, v)?;
843                }
844                0x3E => {
845                    let off = self.read_mem_arg()?;
846                    let v = self.pop_i64()? as u64;
847                    let a = self.pop_addr_offset(off)?;
848                    self.mstore_trunc(a, 4, v)?;
849                }
850
851                // ── memory.size / memory.grow ─────────────────────────────────
852                0x3F => {
853                    let _ = self.read_byte()?;
854                    self.push(WasmValue::I32((self.memory.data.len() / 65536) as i32))?;
855                }
856                0x40 => {
857                    let _ = self.read_byte()?;
858                    let n = self.pop_i32()?;
859                    let cur = (self.memory.data.len() / 65536) as i32;
860                    if n >= 0 {
861                        let nb = self.memory.data.len() + (n as usize * 65536);
862                        self.memory.data.resize(nb, 0);
863                        self.memory.size_bytes = nb;
864                        self.push(WasmValue::I32(cur))?;
865                    } else {
866                        self.push(WasmValue::I32(-1))?;
867                    }
868                }
869
870                // ── constants ─────────────────────────────────────────────────
871                0x41 => {
872                    let v = self.read_i32_leb()?;
873                    self.push(WasmValue::I32(v))?;
874                }
875                0x42 => {
876                    let v = self.read_i64_leb()?;
877                    self.push(WasmValue::I64(v))?;
878                }
879                0x43 => {
880                    let v = self.read_f32()?;
881                    self.push(WasmValue::F32(v))?;
882                }
883                0x44 => {
884                    let v = self.read_f64()?;
885                    self.push(WasmValue::F64(v))?;
886                }
887
888                // ── i32 comparisons (0x45..=0x4F) ────────────────────────────
889                0x45 => {
890                    let a = self.pop_i32()?;
891                    self.push(WasmValue::I32(bool_to_i32(a == 0)))?;
892                }
893                0x46 => {
894                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
895                    self.push(WasmValue::I32(bool_to_i32(a == b)))?;
896                }
897                0x47 => {
898                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
899                    self.push(WasmValue::I32(bool_to_i32(a != b)))?;
900                }
901                0x48 => {
902                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
903                    self.push(WasmValue::I32(bool_to_i32(a < b)))?;
904                }
905                0x49 => {
906                    let (b, a) = (self.pop_i32()? as u32, self.pop_i32()? as u32);
907                    self.push(WasmValue::I32(bool_to_i32(a < b)))?;
908                }
909                0x4A => {
910                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
911                    self.push(WasmValue::I32(bool_to_i32(a > b)))?;
912                }
913                0x4B => {
914                    let (b, a) = (self.pop_i32()? as u32, self.pop_i32()? as u32);
915                    self.push(WasmValue::I32(bool_to_i32(a > b)))?;
916                }
917                0x4C => {
918                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
919                    self.push(WasmValue::I32(bool_to_i32(a <= b)))?;
920                }
921                0x4D => {
922                    let (b, a) = (self.pop_i32()? as u32, self.pop_i32()? as u32);
923                    self.push(WasmValue::I32(bool_to_i32(a <= b)))?;
924                }
925                0x4E => {
926                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
927                    self.push(WasmValue::I32(bool_to_i32(a >= b)))?;
928                }
929                0x4F => {
930                    let (b, a) = (self.pop_i32()? as u32, self.pop_i32()? as u32);
931                    self.push(WasmValue::I32(bool_to_i32(a >= b)))?;
932                }
933
934                // ── i64 comparisons (0x50..=0x5A) ────────────────────────────
935                0x50 => {
936                    let a = self.pop_i64()?;
937                    self.push(WasmValue::I32(bool_to_i32(a == 0)))?;
938                }
939                0x51 => {
940                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
941                    self.push(WasmValue::I32(bool_to_i32(a == b)))?;
942                }
943                0x52 => {
944                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
945                    self.push(WasmValue::I32(bool_to_i32(a != b)))?;
946                }
947                0x53 => {
948                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
949                    self.push(WasmValue::I32(bool_to_i32(a < b)))?;
950                }
951                0x54 => {
952                    let (b, a) = (self.pop_i64()? as u64, self.pop_i64()? as u64);
953                    self.push(WasmValue::I32(bool_to_i32(a < b)))?;
954                }
955                0x55 => {
956                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
957                    self.push(WasmValue::I32(bool_to_i32(a > b)))?;
958                }
959                0x56 => {
960                    let (b, a) = (self.pop_i64()? as u64, self.pop_i64()? as u64);
961                    self.push(WasmValue::I32(bool_to_i32(a > b)))?;
962                }
963                0x57 => {
964                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
965                    self.push(WasmValue::I32(bool_to_i32(a <= b)))?;
966                }
967                0x58 => {
968                    let (b, a) = (self.pop_i64()? as u64, self.pop_i64()? as u64);
969                    self.push(WasmValue::I32(bool_to_i32(a <= b)))?;
970                }
971                0x59 => {
972                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
973                    self.push(WasmValue::I32(bool_to_i32(a >= b)))?;
974                }
975                0x5A => {
976                    let (b, a) = (self.pop_i64()? as u64, self.pop_i64()? as u64);
977                    self.push(WasmValue::I32(bool_to_i32(a >= b)))?;
978                }
979
980                // ── f32 comparisons (0x5B..=0x60) ────────────────────────────
981                0x5B => {
982                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
983                    self.push(WasmValue::I32(bool_to_i32(a == b)))?;
984                }
985                0x5C => {
986                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
987                    self.push(WasmValue::I32(bool_to_i32(a != b)))?;
988                }
989                0x5D => {
990                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
991                    self.push(WasmValue::I32(bool_to_i32(a < b)))?;
992                }
993                0x5E => {
994                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
995                    self.push(WasmValue::I32(bool_to_i32(a > b)))?;
996                }
997                0x5F => {
998                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
999                    self.push(WasmValue::I32(bool_to_i32(a <= b)))?;
1000                }
1001                0x60 => {
1002                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
1003                    self.push(WasmValue::I32(bool_to_i32(a >= b)))?;
1004                }
1005
1006                // ── f64 comparisons (0x61..=0x66) ────────────────────────────
1007                0x61 => {
1008                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1009                    self.push(WasmValue::I32(bool_to_i32(a == b)))?;
1010                }
1011                0x62 => {
1012                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1013                    self.push(WasmValue::I32(bool_to_i32(a != b)))?;
1014                }
1015                0x63 => {
1016                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1017                    self.push(WasmValue::I32(bool_to_i32(a < b)))?;
1018                }
1019                0x64 => {
1020                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1021                    self.push(WasmValue::I32(bool_to_i32(a > b)))?;
1022                }
1023                0x65 => {
1024                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1025                    self.push(WasmValue::I32(bool_to_i32(a <= b)))?;
1026                }
1027                0x66 => {
1028                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1029                    self.push(WasmValue::I32(bool_to_i32(a >= b)))?;
1030                }
1031
1032                // ── i32 unary (0x67..=0x69) ───────────────────────────────────
1033                0x67 => {
1034                    let a = self.pop_i32()? as u32;
1035                    self.push(WasmValue::I32(a.leading_zeros() as i32))?;
1036                }
1037                0x68 => {
1038                    let a = self.pop_i32()? as u32;
1039                    self.push(WasmValue::I32(a.trailing_zeros() as i32))?;
1040                }
1041                0x69 => {
1042                    let a = self.pop_i32()? as u32;
1043                    self.push(WasmValue::I32(a.count_ones() as i32))?;
1044                }
1045
1046                // ── i32 binary arithmetic (0x6A..=0x78) ──────────────────────
1047                0x6A => {
1048                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
1049                    self.push(WasmValue::I32(a.wrapping_add(b)))?;
1050                }
1051                0x6B => {
1052                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
1053                    self.push(WasmValue::I32(a.wrapping_sub(b)))?;
1054                }
1055                0x6C => {
1056                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
1057                    self.push(WasmValue::I32(a.wrapping_mul(b)))?;
1058                }
1059                0x6D => {
1060                    // i32.div_s
1061                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
1062                    if b == 0 {
1063                        return Err(WasmError::DivisionByZero);
1064                    }
1065                    if a == i32::MIN && b == -1 {
1066                        return Err(WasmError::Trap("i32 div overflow".into()));
1067                    }
1068                    self.push(WasmValue::I32(a / b))?;
1069                }
1070                0x6E => {
1071                    // i32.div_u
1072                    let (b, a) = (self.pop_i32()? as u32, self.pop_i32()? as u32);
1073                    if b == 0 {
1074                        return Err(WasmError::DivisionByZero);
1075                    }
1076                    self.push(WasmValue::I32((a / b) as i32))?;
1077                }
1078                0x6F => {
1079                    // i32.rem_s
1080                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
1081                    if b == 0 {
1082                        return Err(WasmError::DivisionByZero);
1083                    }
1084                    self.push(WasmValue::I32(a.wrapping_rem(b)))?;
1085                }
1086                0x70 => {
1087                    // i32.rem_u
1088                    let (b, a) = (self.pop_i32()? as u32, self.pop_i32()? as u32);
1089                    if b == 0 {
1090                        return Err(WasmError::DivisionByZero);
1091                    }
1092                    self.push(WasmValue::I32((a % b) as i32))?;
1093                }
1094                0x71 => {
1095                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
1096                    self.push(WasmValue::I32(a & b))?;
1097                }
1098                0x72 => {
1099                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
1100                    self.push(WasmValue::I32(a | b))?;
1101                }
1102                0x73 => {
1103                    let (b, a) = (self.pop_i32()?, self.pop_i32()?);
1104                    self.push(WasmValue::I32(a ^ b))?;
1105                }
1106                0x74 => {
1107                    let (b, a) = (self.pop_i32()? as u32 & 31, self.pop_i32()?);
1108                    self.push(WasmValue::I32(a.wrapping_shl(b)))?;
1109                }
1110                0x75 => {
1111                    let (b, a) = (self.pop_i32()? as u32 & 31, self.pop_i32()?);
1112                    self.push(WasmValue::I32(a.wrapping_shr(b)))?;
1113                }
1114                0x76 => {
1115                    let (b, a) = (self.pop_i32()? as u32 & 31, self.pop_i32()? as u32);
1116                    self.push(WasmValue::I32(a.wrapping_shr(b) as i32))?;
1117                }
1118                0x77 => {
1119                    let (b, a) = (self.pop_i32()? as u32 & 31, self.pop_i32()? as u32);
1120                    self.push(WasmValue::I32(a.rotate_left(b) as i32))?;
1121                }
1122                0x78 => {
1123                    let (b, a) = (self.pop_i32()? as u32 & 31, self.pop_i32()? as u32);
1124                    self.push(WasmValue::I32(a.rotate_right(b) as i32))?;
1125                }
1126
1127                // ── i64 unary (0x79..=0x7B) ───────────────────────────────────
1128                0x79 => {
1129                    let a = self.pop_i64()? as u64;
1130                    self.push(WasmValue::I64(a.leading_zeros() as i64))?;
1131                }
1132                0x7A => {
1133                    let a = self.pop_i64()? as u64;
1134                    self.push(WasmValue::I64(a.trailing_zeros() as i64))?;
1135                }
1136                0x7B => {
1137                    let a = self.pop_i64()? as u64;
1138                    self.push(WasmValue::I64(a.count_ones() as i64))?;
1139                }
1140
1141                // ── i64 binary arithmetic (0x7C..=0x8A) ──────────────────────
1142                0x7C => {
1143                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
1144                    self.push(WasmValue::I64(a.wrapping_add(b)))?;
1145                }
1146                0x7D => {
1147                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
1148                    self.push(WasmValue::I64(a.wrapping_sub(b)))?;
1149                }
1150                0x7E => {
1151                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
1152                    self.push(WasmValue::I64(a.wrapping_mul(b)))?;
1153                }
1154                0x7F => {
1155                    // i64.div_s
1156                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
1157                    if b == 0 {
1158                        return Err(WasmError::DivisionByZero);
1159                    }
1160                    if a == i64::MIN && b == -1 {
1161                        return Err(WasmError::Trap("i64 div overflow".into()));
1162                    }
1163                    self.push(WasmValue::I64(a / b))?;
1164                }
1165                0x80 => {
1166                    let (b, a) = (self.pop_i64()? as u64, self.pop_i64()? as u64);
1167                    if b == 0 {
1168                        return Err(WasmError::DivisionByZero);
1169                    }
1170                    self.push(WasmValue::I64((a / b) as i64))?;
1171                }
1172                0x81 => {
1173                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
1174                    if b == 0 {
1175                        return Err(WasmError::DivisionByZero);
1176                    }
1177                    self.push(WasmValue::I64(a.wrapping_rem(b)))?;
1178                }
1179                0x82 => {
1180                    let (b, a) = (self.pop_i64()? as u64, self.pop_i64()? as u64);
1181                    if b == 0 {
1182                        return Err(WasmError::DivisionByZero);
1183                    }
1184                    self.push(WasmValue::I64((a % b) as i64))?;
1185                }
1186                0x83 => {
1187                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
1188                    self.push(WasmValue::I64(a & b))?;
1189                }
1190                0x84 => {
1191                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
1192                    self.push(WasmValue::I64(a | b))?;
1193                }
1194                0x85 => {
1195                    let (b, a) = (self.pop_i64()?, self.pop_i64()?);
1196                    self.push(WasmValue::I64(a ^ b))?;
1197                }
1198                0x86 => {
1199                    let (b, a) = (self.pop_i64()? as u32 & 63, self.pop_i64()?);
1200                    self.push(WasmValue::I64(a.wrapping_shl(b)))?;
1201                }
1202                0x87 => {
1203                    let (b, a) = (self.pop_i64()? as u32 & 63, self.pop_i64()?);
1204                    self.push(WasmValue::I64(a.wrapping_shr(b)))?;
1205                }
1206                0x88 => {
1207                    let (b, a) = (self.pop_i64()? as u32 & 63, self.pop_i64()? as u64);
1208                    self.push(WasmValue::I64(a.wrapping_shr(b) as i64))?;
1209                }
1210                0x89 => {
1211                    let (b, a) = (self.pop_i64()? as u32 & 63, self.pop_i64()? as u64);
1212                    self.push(WasmValue::I64(a.rotate_left(b) as i64))?;
1213                }
1214                0x8A => {
1215                    let (b, a) = (self.pop_i64()? as u32 & 63, self.pop_i64()? as u64);
1216                    self.push(WasmValue::I64(a.rotate_right(b) as i64))?;
1217                }
1218
1219                // ── f32 arithmetic (0x8B..=0x98) ──────────────────────────────
1220                0x8B => {
1221                    let a = self.pop_f32()?;
1222                    self.push(WasmValue::F32(a.abs()))?;
1223                }
1224                0x8C => {
1225                    let a = self.pop_f32()?;
1226                    self.push(WasmValue::F32(-a))?;
1227                }
1228                0x8D => {
1229                    let a = self.pop_f32()?;
1230                    self.push(WasmValue::F32(a.ceil()))?;
1231                }
1232                0x8E => {
1233                    let a = self.pop_f32()?;
1234                    self.push(WasmValue::F32(a.floor()))?;
1235                }
1236                0x8F => {
1237                    let a = self.pop_f32()?;
1238                    self.push(WasmValue::F32(a.trunc()))?;
1239                }
1240                0x90 => {
1241                    let a = self.pop_f32()?;
1242                    self.push(WasmValue::F32(a.round()))?;
1243                }
1244                0x91 => {
1245                    let a = self.pop_f32()?;
1246                    self.push(WasmValue::F32(a.sqrt()))?;
1247                }
1248                0x92 => {
1249                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
1250                    self.push(WasmValue::F32(a + b))?;
1251                }
1252                0x93 => {
1253                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
1254                    self.push(WasmValue::F32(a - b))?;
1255                }
1256                0x94 => {
1257                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
1258                    self.push(WasmValue::F32(a * b))?;
1259                }
1260                0x95 => {
1261                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
1262                    self.push(WasmValue::F32(a / b))?;
1263                }
1264                0x96 => {
1265                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
1266                    self.push(WasmValue::F32(a.min(b)))?;
1267                }
1268                0x97 => {
1269                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
1270                    self.push(WasmValue::F32(a.max(b)))?;
1271                }
1272                0x98 => {
1273                    let (b, a) = (self.pop_f32()?, self.pop_f32()?);
1274                    self.push(WasmValue::F32(a.copysign(b)))?;
1275                }
1276
1277                // ── f64 arithmetic (0x99..=0xA6) ──────────────────────────────
1278                0x99 => {
1279                    let a = self.pop_f64()?;
1280                    self.push(WasmValue::F64(a.abs()))?;
1281                }
1282                0x9A => {
1283                    let a = self.pop_f64()?;
1284                    self.push(WasmValue::F64(-a))?;
1285                }
1286                0x9B => {
1287                    let a = self.pop_f64()?;
1288                    self.push(WasmValue::F64(a.ceil()))?;
1289                }
1290                0x9C => {
1291                    let a = self.pop_f64()?;
1292                    self.push(WasmValue::F64(a.floor()))?;
1293                }
1294                0x9D => {
1295                    let a = self.pop_f64()?;
1296                    self.push(WasmValue::F64(a.trunc()))?;
1297                }
1298                0x9E => {
1299                    let a = self.pop_f64()?;
1300                    self.push(WasmValue::F64(a.round()))?;
1301                }
1302                0x9F => {
1303                    let a = self.pop_f64()?;
1304                    self.push(WasmValue::F64(a.sqrt()))?;
1305                }
1306                0xA0 => {
1307                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1308                    self.push(WasmValue::F64(a + b))?;
1309                }
1310                0xA1 => {
1311                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1312                    self.push(WasmValue::F64(a - b))?;
1313                }
1314                0xA2 => {
1315                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1316                    self.push(WasmValue::F64(a * b))?;
1317                }
1318                0xA3 => {
1319                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1320                    self.push(WasmValue::F64(a / b))?;
1321                }
1322                0xA4 => {
1323                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1324                    self.push(WasmValue::F64(a.min(b)))?;
1325                }
1326                0xA5 => {
1327                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1328                    self.push(WasmValue::F64(a.max(b)))?;
1329                }
1330                0xA6 => {
1331                    let (b, a) = (self.pop_f64()?, self.pop_f64()?);
1332                    self.push(WasmValue::F64(a.copysign(b)))?;
1333                }
1334
1335                // ── conversions (0xA7..=0xBF) ─────────────────────────────────
1336                0xA7 => {
1337                    let a = self.pop_i64()?;
1338                    self.push(WasmValue::I32(a as i32))?;
1339                }
1340                0xA8 => {
1341                    let a = self.pop_f32()?;
1342                    check_float_f32(a)?;
1343                    self.push(WasmValue::I32(a as i32))?;
1344                }
1345                0xA9 => {
1346                    let a = self.pop_f32()?;
1347                    check_float_f32(a)?;
1348                    self.push(WasmValue::I32(a as u32 as i32))?;
1349                }
1350                0xAA => {
1351                    let a = self.pop_f64()?;
1352                    check_float_f64(a)?;
1353                    self.push(WasmValue::I32(a as i32))?;
1354                }
1355                0xAB => {
1356                    let a = self.pop_f64()?;
1357                    check_float_f64(a)?;
1358                    self.push(WasmValue::I32(a as u32 as i32))?;
1359                }
1360                0xAC => {
1361                    let a = self.pop_i32()?;
1362                    self.push(WasmValue::I64(a as i64))?;
1363                }
1364                0xAD => {
1365                    let a = self.pop_i32()? as u32;
1366                    self.push(WasmValue::I64(a as i64))?;
1367                }
1368                0xAE => {
1369                    let a = self.pop_f32()?;
1370                    check_float_f32(a)?;
1371                    self.push(WasmValue::I64(a as i64))?;
1372                }
1373                0xAF => {
1374                    let a = self.pop_f32()?;
1375                    check_float_f32(a)?;
1376                    self.push(WasmValue::I64(a as u64 as i64))?;
1377                }
1378                0xB0 => {
1379                    let a = self.pop_f64()?;
1380                    check_float_f64(a)?;
1381                    self.push(WasmValue::I64(a as i64))?;
1382                }
1383                0xB1 => {
1384                    let a = self.pop_f64()?;
1385                    check_float_f64(a)?;
1386                    self.push(WasmValue::I64(a as u64 as i64))?;
1387                }
1388                0xB2 => {
1389                    let a = self.pop_i32()?;
1390                    self.push(WasmValue::F32(a as f32))?;
1391                }
1392                0xB3 => {
1393                    let a = self.pop_i32()? as u32;
1394                    self.push(WasmValue::F32(a as f32))?;
1395                }
1396                0xB4 => {
1397                    let a = self.pop_i64()?;
1398                    self.push(WasmValue::F32(a as f32))?;
1399                }
1400                0xB5 => {
1401                    let a = self.pop_i64()? as u64;
1402                    self.push(WasmValue::F32(a as f32))?;
1403                }
1404                0xB6 => {
1405                    let a = self.pop_f64()?;
1406                    self.push(WasmValue::F32(a as f32))?;
1407                }
1408                0xB7 => {
1409                    let a = self.pop_i32()?;
1410                    self.push(WasmValue::F64(a as f64))?;
1411                }
1412                0xB8 => {
1413                    let a = self.pop_i32()? as u32;
1414                    self.push(WasmValue::F64(a as f64))?;
1415                }
1416                0xB9 => {
1417                    let a = self.pop_i64()?;
1418                    self.push(WasmValue::F64(a as f64))?;
1419                }
1420                0xBA => {
1421                    let a = self.pop_i64()? as u64;
1422                    self.push(WasmValue::F64(a as f64))?;
1423                }
1424                0xBB => {
1425                    let a = self.pop_f32()?;
1426                    self.push(WasmValue::F64(a as f64))?;
1427                }
1428                0xBC => {
1429                    let a = self.pop_f32()?;
1430                    self.push(WasmValue::I32(i32::from_le_bytes(a.to_le_bytes())))?;
1431                }
1432                0xBD => {
1433                    let a = self.pop_f64()?;
1434                    self.push(WasmValue::I64(i64::from_le_bytes(a.to_le_bytes())))?;
1435                }
1436                0xBE => {
1437                    let a = self.pop_i32()?;
1438                    self.push(WasmValue::F32(f32::from_le_bytes(a.to_le_bytes())))?;
1439                }
1440                0xBF => {
1441                    let a = self.pop_i64()?;
1442                    self.push(WasmValue::F64(f64::from_le_bytes(a.to_le_bytes())))?;
1443                }
1444
1445                other => return Err(WasmError::UnimplementedOpcode(other)),
1446            }
1447        }
1448
1449        // Collect return values from top of stack.
1450        let slen = self.stack.len();
1451        Ok(if n_results <= slen {
1452            self.stack[slen - n_results..].to_vec()
1453        } else {
1454            self.stack.clone()
1455        })
1456    }
1457}
1458
1459// ─── Free helpers ─────────────────────────────────────────────────────────────
1460
1461#[inline]
1462fn bool_to_i32(b: bool) -> i32 {
1463    if b {
1464        1
1465    } else {
1466        0
1467    }
1468}
1469
1470fn check_float_f32(v: f32) -> Result<(), WasmError> {
1471    if v.is_nan() || v.is_infinite() {
1472        Err(WasmError::Trap("float conversion: invalid value".into()))
1473    } else {
1474        Ok(())
1475    }
1476}
1477fn check_float_f64(v: f64) -> Result<(), WasmError> {
1478    if v.is_nan() || v.is_infinite() {
1479        Err(WasmError::Trap("float conversion: invalid value".into()))
1480    } else {
1481        Ok(())
1482    }
1483}
1484
1485fn sign_extend_u32(raw: u32, bits: usize) -> i32 {
1486    let shift = 32 - bits;
1487    ((raw << shift) as i32) >> shift
1488}
1489fn sign_extend_u64(raw: u64, bits: usize) -> i64 {
1490    let shift = 64 - bits;
1491    ((raw << shift) as i64) >> shift
1492}
1493
1494// ─── Public API ──────────────────────────────────────────────────────────────
1495
1496#[allow(dead_code)]
1497pub fn default_wasm_bridge_config() -> WasmBridgeConfig {
1498    WasmBridgeConfig {
1499        memory_pages: 1,
1500        max_functions: 256,
1501        debug_mode: false,
1502    }
1503}
1504
1505#[allow(dead_code)]
1506pub fn new_wasm_bridge(cfg: WasmBridgeConfig) -> WasmBridge {
1507    let size_bytes = cfg.memory_pages as usize * 65536;
1508    WasmBridge {
1509        config: cfg,
1510        memory: WasmMemory {
1511            data: vec![0u8; size_bytes],
1512            size_bytes,
1513        },
1514        functions: Vec::new(),
1515        call_count: 0,
1516    }
1517}
1518
1519#[allow(dead_code)]
1520pub fn wasm_memory_size(bridge: &WasmBridge) -> usize {
1521    bridge.memory.size_bytes
1522}
1523
1524#[allow(dead_code)]
1525pub fn wasm_read_u32(bridge: &WasmBridge, offset: usize) -> Option<u32> {
1526    if offset + 4 > bridge.memory.data.len() {
1527        return None;
1528    }
1529    Some(u32::from_le_bytes([
1530        bridge.memory.data[offset],
1531        bridge.memory.data[offset + 1],
1532        bridge.memory.data[offset + 2],
1533        bridge.memory.data[offset + 3],
1534    ]))
1535}
1536
1537#[allow(dead_code)]
1538pub fn wasm_write_u32(bridge: &mut WasmBridge, offset: usize, val: u32) -> bool {
1539    if offset + 4 > bridge.memory.data.len() {
1540        return false;
1541    }
1542    let b = val.to_le_bytes();
1543    bridge.memory.data[offset..offset + 4].copy_from_slice(&b);
1544    true
1545}
1546
1547#[allow(dead_code)]
1548pub fn register_wasm_function(bridge: &mut WasmBridge, func: WasmFunction) {
1549    if bridge.functions.len() < bridge.config.max_functions {
1550        bridge.functions.push(func);
1551    }
1552}
1553
1554#[allow(dead_code)]
1555pub fn wasm_function_count(bridge: &WasmBridge) -> usize {
1556    bridge.functions.len()
1557}
1558
1559/// Execute a named WASM function with the given arguments.
1560///
1561/// The function's `bytecode` starts with a LEB128 local-variable declaration
1562/// block, then the expression opcodes.  Empty `bytecode` returns `Ok([])`.
1563#[allow(dead_code)]
1564pub fn wasm_call(
1565    bridge: &mut WasmBridge,
1566    name: &str,
1567    args: &[WasmValue],
1568) -> Result<Vec<WasmValue>, WasmError> {
1569    let func = bridge
1570        .functions
1571        .iter()
1572        .find(|f| f.name == name)
1573        .cloned()
1574        .ok_or_else(|| WasmError::FunctionNotFound(name.to_string()))?;
1575
1576    bridge.call_count += 1;
1577    if func.bytecode.is_empty() {
1578        return Ok(Vec::new());
1579    }
1580
1581    // Decode local-variable declaration header.
1582    let mut pc = 0usize;
1583    let n_decls = read_leb128_u32(&func.bytecode, &mut pc) as usize;
1584    let mut extra_locals: Vec<WasmValue> = Vec::new();
1585    for _ in 0..n_decls {
1586        let count = read_leb128_u32(&func.bytecode, &mut pc) as usize;
1587        let type_byte = func.bytecode[pc];
1588        pc += 1;
1589        let default = match type_byte {
1590            0x7F => WasmValue::I32(0),
1591            0x7E => WasmValue::I64(0),
1592            0x7D => WasmValue::F32(0.0),
1593            0x7C => WasmValue::F64(0.0),
1594            _ => WasmValue::I32(0),
1595        };
1596        for _ in 0..count {
1597            extra_locals.push(default.clone());
1598        }
1599    }
1600
1601    let mut locals: Vec<WasmValue> = args.to_vec();
1602    locals.extend(extra_locals);
1603
1604    let expr_code = &func.bytecode[pc..];
1605    let n_results = func.return_types.len();
1606    let mut interp = Interpreter::new(&mut bridge.memory, expr_code, locals, 0);
1607    interp.run(n_results)
1608}
1609
1610/// Backward-compatible stub: calls `wasm_call` with no arguments.
1611/// Returns `true` if the call succeeds (function found and executed without trap).
1612#[allow(dead_code)]
1613pub fn wasm_call_stub(bridge: &mut WasmBridge, name: &str) -> bool {
1614    wasm_call(bridge, name, &[]).is_ok()
1615}
1616
1617#[allow(dead_code)]
1618pub fn value_type_name(t: &WasmValueType) -> &'static str {
1619    match t {
1620        WasmValueType::I32 => "i32",
1621        WasmValueType::I64 => "i64",
1622        WasmValueType::F32 => "f32",
1623        WasmValueType::F64 => "f64",
1624        WasmValueType::FuncRef => "funcref",
1625        WasmValueType::ExternRef => "externref",
1626    }
1627}
1628
1629#[allow(dead_code)]
1630pub fn wasm_bridge_to_json(bridge: &WasmBridge) -> String {
1631    format!(
1632        "{{\"memory_pages\":{},\"function_count\":{},\"call_count\":{},\"debug_mode\":{}}}",
1633        bridge.config.memory_pages,
1634        bridge.functions.len(),
1635        bridge.call_count,
1636        bridge.config.debug_mode
1637    )
1638}
1639
1640// ─── Tests ───────────────────────────────────────────────────────────────────
1641
1642#[cfg(test)]
1643mod tests {
1644    use super::*;
1645
1646    // ── backward-compatible tests ─────────────────────────────────────────────
1647
1648    #[test]
1649    fn test_default_config() {
1650        let cfg = default_wasm_bridge_config();
1651        assert_eq!(cfg.memory_pages, 1);
1652        assert_eq!(cfg.max_functions, 256);
1653        assert!(!cfg.debug_mode);
1654    }
1655
1656    #[test]
1657    fn test_new_bridge_memory_size() {
1658        let bridge = new_wasm_bridge(default_wasm_bridge_config());
1659        assert_eq!(wasm_memory_size(&bridge), 65536);
1660    }
1661
1662    #[test]
1663    fn test_write_read_u32() {
1664        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1665        assert!(wasm_write_u32(&mut bridge, 0, 0xDEAD_BEEF));
1666        assert_eq!(wasm_read_u32(&bridge, 0), Some(0xDEAD_BEEF));
1667    }
1668
1669    #[test]
1670    fn test_out_of_bounds_returns_none() {
1671        let bridge = new_wasm_bridge(default_wasm_bridge_config());
1672        assert!(wasm_read_u32(&bridge, 65534).is_none());
1673    }
1674
1675    #[test]
1676    fn test_register_and_call_function() {
1677        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1678        let func = WasmFunction {
1679            name: "add".to_string(),
1680            param_types: vec![WasmValueType::I32, WasmValueType::I32],
1681            return_types: vec![WasmValueType::I32],
1682            bytecode: Vec::new(),
1683            local_types: Vec::new(),
1684        };
1685        register_wasm_function(&mut bridge, func);
1686        assert_eq!(wasm_function_count(&bridge), 1);
1687        assert!(wasm_call_stub(&mut bridge, "add"));
1688        assert_eq!(bridge.call_count, 1);
1689    }
1690
1691    #[test]
1692    fn test_call_unknown_function() {
1693        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1694        assert!(!wasm_call_stub(&mut bridge, "nonexistent"));
1695        assert_eq!(bridge.call_count, 0);
1696    }
1697
1698    #[test]
1699    fn test_value_type_names() {
1700        assert_eq!(value_type_name(&WasmValueType::I32), "i32");
1701        assert_eq!(value_type_name(&WasmValueType::I64), "i64");
1702        assert_eq!(value_type_name(&WasmValueType::F32), "f32");
1703        assert_eq!(value_type_name(&WasmValueType::F64), "f64");
1704        assert_eq!(value_type_name(&WasmValueType::FuncRef), "funcref");
1705        assert_eq!(value_type_name(&WasmValueType::ExternRef), "externref");
1706    }
1707
1708    #[test]
1709    fn test_bridge_to_json() {
1710        let bridge = new_wasm_bridge(default_wasm_bridge_config());
1711        let json = wasm_bridge_to_json(&bridge);
1712        assert!(json.contains("memory_pages"));
1713        assert!(json.contains("call_count"));
1714    }
1715
1716    // ── interpreter tests ─────────────────────────────────────────────────────
1717
1718    fn make_func(
1719        name: &str,
1720        params: Vec<WasmValueType>,
1721        rets: Vec<WasmValueType>,
1722        bytecode: Vec<u8>,
1723    ) -> WasmFunction {
1724        WasmFunction {
1725            name: name.to_string(),
1726            param_types: params,
1727            return_types: rets,
1728            bytecode,
1729            local_types: Vec::new(),
1730        }
1731    }
1732
1733    /// (param i32 i32) (result i32): local.get 0, local.get 1, i32.add, end
1734    /// bytecode = [0 local decls] + [0x20,0x00, 0x20,0x01, 0x6A, 0x0B]
1735    #[test]
1736    fn test_wasm_call_add_function() {
1737        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1738        let f = make_func(
1739            "add",
1740            vec![WasmValueType::I32, WasmValueType::I32],
1741            vec![WasmValueType::I32],
1742            vec![0x00, 0x20, 0x00, 0x20, 0x01, 0x6A, 0x0B],
1743        );
1744        register_wasm_function(&mut bridge, f);
1745        let r = wasm_call(&mut bridge, "add", &[WasmValue::I32(3), WasmValue::I32(4)]).unwrap();
1746        assert_eq!(r, vec![WasmValue::I32(7)]);
1747    }
1748
1749    /// i32.const 42 (two-byte: 0x41 0x2A), end
1750    #[test]
1751    fn test_wasm_call_constant() {
1752        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1753        let f = make_func(
1754            "c42",
1755            vec![],
1756            vec![WasmValueType::I32],
1757            vec![0x00, 0x41, 0x2A, 0x0B],
1758        );
1759        register_wasm_function(&mut bridge, f);
1760        assert_eq!(
1761            wasm_call(&mut bridge, "c42", &[]).unwrap(),
1762            vec![WasmValue::I32(42)]
1763        );
1764    }
1765
1766    /// Write 99 to memory[0] and read it back.
1767    /// bytecode: [0 local decls][i32.const 0][i32.const 99 (two-byte LEB)][i32.store align=2 offset=0][end]
1768    /// i32.const 99: 0x41, 0xE3, 0x00  (99 = 0x63; as two-byte signed LEB: 0xE3 0x00)
1769    #[test]
1770    fn test_wasm_call_memory_write_read() {
1771        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1772        let bytecode = vec![
1773            0x00, // 0 local decls
1774            0x41, 0x00, // i32.const 0 (address)
1775            0x41, 0xE3, 0x00, // i32.const 99 (proper two-byte signed LEB128)
1776            0x36, 0x02, 0x00, // i32.store align=2 offset=0
1777            0x0B, // end
1778        ];
1779        let f = make_func("write_99", vec![], vec![], bytecode);
1780        register_wasm_function(&mut bridge, f);
1781        let result = wasm_call(&mut bridge, "write_99", &[]);
1782        assert!(result.is_ok(), "Expected Ok but got: {:?}", result);
1783        assert_eq!(wasm_read_u32(&bridge, 0), Some(99));
1784    }
1785
1786    #[test]
1787    fn test_wasm_call_unknown_function_error() {
1788        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1789        let r = wasm_call(&mut bridge, "no_such_function", &[]);
1790        assert!(matches!(r, Err(WasmError::FunctionNotFound(_))));
1791    }
1792
1793    /// Empty bytecode → Ok([])
1794    #[test]
1795    fn test_wasm_call_no_bytecode_succeeds() {
1796        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1797        let f = make_func("empty", vec![], vec![], vec![0x00, 0x0B]);
1798        register_wasm_function(&mut bridge, f);
1799        assert_eq!(wasm_call(&mut bridge, "empty", &[]).unwrap(), vec![]);
1800    }
1801
1802    // ── additional correctness ────────────────────────────────────────────────
1803
1804    #[test]
1805    fn test_wasm_call_i32_sub() {
1806        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1807        let f = make_func(
1808            "sub",
1809            vec![WasmValueType::I32, WasmValueType::I32],
1810            vec![WasmValueType::I32],
1811            vec![0x00, 0x20, 0x00, 0x20, 0x01, 0x6B, 0x0B],
1812        );
1813        register_wasm_function(&mut bridge, f);
1814        assert_eq!(
1815            wasm_call(&mut bridge, "sub", &[WasmValue::I32(10), WasmValue::I32(3)]).unwrap(),
1816            vec![WasmValue::I32(7)]
1817        );
1818    }
1819
1820    #[test]
1821    fn test_wasm_call_i32_mul() {
1822        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1823        let f = make_func(
1824            "mul",
1825            vec![WasmValueType::I32, WasmValueType::I32],
1826            vec![WasmValueType::I32],
1827            vec![0x00, 0x20, 0x00, 0x20, 0x01, 0x6C, 0x0B],
1828        );
1829        register_wasm_function(&mut bridge, f);
1830        assert_eq!(
1831            wasm_call(&mut bridge, "mul", &[WasmValue::I32(6), WasmValue::I32(7)]).unwrap(),
1832            vec![WasmValue::I32(42)]
1833        );
1834    }
1835
1836    #[test]
1837    fn test_wasm_call_div_by_zero_error() {
1838        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1839        let f = make_func(
1840            "div",
1841            vec![WasmValueType::I32, WasmValueType::I32],
1842            vec![WasmValueType::I32],
1843            vec![0x00, 0x20, 0x00, 0x20, 0x01, 0x6D, 0x0B],
1844        );
1845        register_wasm_function(&mut bridge, f);
1846        let r = wasm_call(&mut bridge, "div", &[WasmValue::I32(10), WasmValue::I32(0)]);
1847        assert!(matches!(r, Err(WasmError::DivisionByZero)));
1848    }
1849
1850    #[test]
1851    fn test_wasm_call_i32_eqz() {
1852        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1853        let f = make_func(
1854            "eqz",
1855            vec![WasmValueType::I32],
1856            vec![WasmValueType::I32],
1857            vec![0x00, 0x20, 0x00, 0x45, 0x0B],
1858        );
1859        register_wasm_function(&mut bridge, f);
1860        assert_eq!(
1861            wasm_call(&mut bridge, "eqz", &[WasmValue::I32(0)]).unwrap(),
1862            vec![WasmValue::I32(1)]
1863        );
1864        assert_eq!(
1865            wasm_call(&mut bridge, "eqz", &[WasmValue::I32(5)]).unwrap(),
1866            vec![WasmValue::I32(0)]
1867        );
1868    }
1869
1870    #[test]
1871    fn test_wasm_call_local_tee() {
1872        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1873        // local.get 0, local.tee 0, drop, local.get 0, end
1874        let f = make_func(
1875            "tee",
1876            vec![WasmValueType::I32],
1877            vec![WasmValueType::I32],
1878            vec![0x00, 0x20, 0x00, 0x22, 0x00, 0x1A, 0x20, 0x00, 0x0B],
1879        );
1880        register_wasm_function(&mut bridge, f);
1881        assert_eq!(
1882            wasm_call(&mut bridge, "tee", &[WasmValue::I32(77)]).unwrap(),
1883            vec![WasmValue::I32(77)]
1884        );
1885    }
1886
1887    #[test]
1888    fn test_wasm_call_select() {
1889        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1890        // i32.const 10, i32.const 20, i32.const 1, select → 10
1891        let f = make_func(
1892            "sel",
1893            vec![],
1894            vec![WasmValueType::I32],
1895            vec![0x00, 0x41, 0x0A, 0x41, 0x14, 0x41, 0x01, 0x1B, 0x0B],
1896        );
1897        register_wasm_function(&mut bridge, f);
1898        assert_eq!(
1899            wasm_call(&mut bridge, "sel", &[]).unwrap(),
1900            vec![WasmValue::I32(10)]
1901        );
1902    }
1903
1904    #[test]
1905    fn test_wasm_call_f32_add() {
1906        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1907        let mut bc = vec![0x00u8]; // 0 locals
1908        bc.push(0x43);
1909        bc.extend_from_slice(&1.5f32.to_le_bytes());
1910        bc.push(0x43);
1911        bc.extend_from_slice(&2.5f32.to_le_bytes());
1912        bc.push(0x92);
1913        bc.push(0x0B); // f32.add, end
1914        let f = make_func("f32a", vec![], vec![WasmValueType::F32], bc);
1915        register_wasm_function(&mut bridge, f);
1916        assert_eq!(
1917            wasm_call(&mut bridge, "f32a", &[]).unwrap(),
1918            vec![WasmValue::F32(4.0)]
1919        );
1920    }
1921
1922    #[test]
1923    fn test_wasm_call_i32_conversion() {
1924        let mut bridge = new_wasm_bridge(default_wasm_bridge_config());
1925        // i32.const 300 (LEB: 0xAC 0x02), i64.extend_i32_s, i32.wrap_i64, end
1926        let f = make_func(
1927            "rt",
1928            vec![],
1929            vec![WasmValueType::I32],
1930            vec![0x00, 0x41, 0xAC, 0x02, 0xAC, 0xA7, 0x0B],
1931        );
1932        register_wasm_function(&mut bridge, f);
1933        assert_eq!(
1934            wasm_call(&mut bridge, "rt", &[]).unwrap(),
1935            vec![WasmValue::I32(300)]
1936        );
1937    }
1938}