Skip to main content

baedeker_core/binary/
instr.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4//! Instruction decoding.
5//!
6//! Decodes a subset of WebAssembly instructions sufficient to start Phase 1
7//! validation work. Function bodies continue to store raw bytes; decoded
8//! instructions are produced on demand from [`CodeBody`].
9//!
10//! See [Spec §5.4](https://webassembly.github.io/spec/core/binary/instructions.html).
11
12use alloc::vec::Vec;
13
14use crate::binary::leb128::{self, Cursor};
15use crate::binary::typeparser::{
16    parse_heap_type, parse_val_type as parse_binary_val_type, parse_val_type_with_first_byte,
17};
18use crate::error::{ByteOffset, DecodeContext, DecodeError, DecodeErrorKind};
19use crate::types::{
20    BlockType, CodeBody, ElemIdx, FuncIdx, GlobalIdx, LabelIdx, LocalIdx, MemArg, MemIdx, RefType,
21    TableIdx, TypeIdx, ValType,
22};
23
24/// A decoded instruction paired with its absolute byte offset in the module.
25#[derive(Debug, Clone, PartialEq)]
26pub struct DecodedInstr {
27    pub offset: ByteOffset,
28    pub instr: Instr,
29}
30
31/// A decoded WebAssembly instruction.
32#[derive(Debug, Clone, PartialEq)]
33pub enum Instr {
34    Unreachable,
35    Nop,
36    Block(BlockType),
37    Loop(BlockType),
38    If(BlockType),
39    Else,
40    End,
41    Br(LabelIdx),
42    BrIf(LabelIdx),
43    BrOnNull(LabelIdx),
44    BrOnNonNull(LabelIdx),
45    BrTable {
46        targets: Vec<LabelIdx>,
47        default: LabelIdx,
48    },
49    Return,
50    Call(FuncIdx),
51    ReturnCall(FuncIdx),
52    CallRef(TypeIdx),
53    ReturnCallRef(TypeIdx),
54    CallIndirect {
55        type_idx: TypeIdx,
56        table_idx: TableIdx,
57    },
58    ReturnCallIndirect {
59        type_idx: TypeIdx,
60        table_idx: TableIdx,
61    },
62    Drop,
63    Select,
64    SelectTyped(Vec<ValType>),
65    LocalGet(LocalIdx),
66    LocalSet(LocalIdx),
67    LocalTee(LocalIdx),
68    GlobalGet(GlobalIdx),
69    GlobalSet(GlobalIdx),
70    TableGet(TableIdx),
71    TableSet(TableIdx),
72    V128Load(MemArg),
73    V128Load8x8S(MemArg),
74    V128Load8x8U(MemArg),
75    V128Load16x4S(MemArg),
76    V128Load16x4U(MemArg),
77    V128Load32x2S(MemArg),
78    V128Load32x2U(MemArg),
79    V128Load8Splat(MemArg),
80    V128Load16Splat(MemArg),
81    V128Load32Splat(MemArg),
82    V128Load64Splat(MemArg),
83    V128Load32Zero(MemArg),
84    V128Load64Zero(MemArg),
85    V128Store(MemArg),
86    V128Load8Lane {
87        memarg: MemArg,
88        lane: u8,
89    },
90    V128Load16Lane {
91        memarg: MemArg,
92        lane: u8,
93    },
94    V128Load32Lane {
95        memarg: MemArg,
96        lane: u8,
97    },
98    V128Load64Lane {
99        memarg: MemArg,
100        lane: u8,
101    },
102    V128Store8Lane {
103        memarg: MemArg,
104        lane: u8,
105    },
106    V128Store16Lane {
107        memarg: MemArg,
108        lane: u8,
109    },
110    V128Store32Lane {
111        memarg: MemArg,
112        lane: u8,
113    },
114    V128Store64Lane {
115        memarg: MemArg,
116        lane: u8,
117    },
118    I32Load(MemArg),
119    I64Load(MemArg),
120    F32Load(MemArg),
121    F64Load(MemArg),
122    I32Load8S(MemArg),
123    I32Load8U(MemArg),
124    I32Load16S(MemArg),
125    I32Load16U(MemArg),
126    I64Load8S(MemArg),
127    I64Load8U(MemArg),
128    I64Load16S(MemArg),
129    I64Load16U(MemArg),
130    I64Load32S(MemArg),
131    I64Load32U(MemArg),
132    I32Store(MemArg),
133    I64Store(MemArg),
134    F32Store(MemArg),
135    F64Store(MemArg),
136    I32Store8(MemArg),
137    I32Store16(MemArg),
138    I64Store8(MemArg),
139    I64Store16(MemArg),
140    I64Store32(MemArg),
141    MemoryInit(crate::types::DataIdx, MemIdx),
142    DataDrop(crate::types::DataIdx),
143    MemoryCopy {
144        dst: MemIdx,
145        src: MemIdx,
146    },
147    MemoryFill(MemIdx),
148    TableInit {
149        elem_idx: ElemIdx,
150        table_idx: TableIdx,
151    },
152    ElemDrop(ElemIdx),
153    TableCopy {
154        dst: TableIdx,
155        src: TableIdx,
156    },
157    TableGrow(TableIdx),
158    TableSize(TableIdx),
159    TableFill(TableIdx),
160    MemorySize(MemIdx),
161    MemoryGrow(MemIdx),
162    I32Const(i32),
163    I64Const(i64),
164    F32Const(f32),
165    F64Const(f64),
166    RefNull(RefType),
167    RefIsNull,
168    RefFunc(FuncIdx),
169    RefAsNonNull,
170    V128Const([u8; 16]),
171    I8x16Splat,
172    I16x8Splat,
173    I32x4Splat,
174    I64x2Splat,
175    F32x4Splat,
176    F64x2Splat,
177    I32x4ExtractLane(u8),
178    I32x4ReplaceLane(u8),
179    F32x4ExtractLane(u8),
180    F32x4ReplaceLane(u8),
181    V128Not,
182    V128And,
183    V128Or,
184    V128Xor,
185    I8x16Add,
186    I8x16Sub,
187    I16x8Add,
188    I16x8Sub,
189    I32x4Add,
190    I32x4Sub,
191    I32x4Mul,
192    I64x2Add,
193    I64x2Sub,
194    F32x4Add,
195    F32x4Sub,
196    F32x4Mul,
197    F32x4Div,
198    F64x2Add,
199    F64x2Sub,
200    F64x2Mul,
201    F64x2Div,
202    I32Eqz,
203    I32Eq,
204    I32Ne,
205    I32LtS,
206    I32LtU,
207    I32GtS,
208    I32GtU,
209    I32LeS,
210    I32LeU,
211    I32GeS,
212    I32GeU,
213    I64Eqz,
214    I64Eq,
215    I64Ne,
216    I64LtS,
217    I64LtU,
218    I64GtS,
219    I64GtU,
220    I64LeS,
221    I64LeU,
222    I64GeS,
223    I64GeU,
224    F32Eq,
225    F32Ne,
226    F32Lt,
227    F32Gt,
228    F32Le,
229    F32Ge,
230    F64Eq,
231    F64Ne,
232    F64Lt,
233    F64Gt,
234    F64Le,
235    F64Ge,
236    I32Clz,
237    I32Ctz,
238    I32Popcnt,
239    I32Add,
240    I32Sub,
241    I32Mul,
242    I32DivS,
243    I32DivU,
244    I32RemS,
245    I32RemU,
246    I32And,
247    I32Or,
248    I32Xor,
249    I32Shl,
250    I32ShrS,
251    I32ShrU,
252    I32Rotl,
253    I32Rotr,
254    I64Clz,
255    I64Ctz,
256    I64Popcnt,
257    I64Add,
258    I64Sub,
259    I64Mul,
260    I64DivS,
261    I64DivU,
262    I64RemS,
263    I64RemU,
264    I64And,
265    I64Or,
266    I64Xor,
267    I64Shl,
268    I64ShrS,
269    I64ShrU,
270    I64Rotl,
271    I64Rotr,
272    F32Abs,
273    F32Neg,
274    F32Ceil,
275    F32Floor,
276    F32Trunc,
277    F32Nearest,
278    F32Sqrt,
279    F32Add,
280    F32Sub,
281    F32Mul,
282    F32Div,
283    F32Min,
284    F32Max,
285    F32Copysign,
286    F64Abs,
287    F64Neg,
288    F64Ceil,
289    F64Floor,
290    F64Trunc,
291    F64Nearest,
292    F64Sqrt,
293    F64Add,
294    F64Sub,
295    F64Mul,
296    F64Div,
297    F64Min,
298    F64Max,
299    F64Copysign,
300    I32WrapI64,
301    I32TruncF32S,
302    I32TruncF32U,
303    I32TruncF64S,
304    I32TruncF64U,
305    I64ExtendI32S,
306    I64ExtendI32U,
307    I64TruncF32S,
308    I64TruncF32U,
309    I64TruncF64S,
310    I64TruncF64U,
311    F32ConvertI32S,
312    F32ConvertI32U,
313    F32ConvertI64S,
314    F32ConvertI64U,
315    F32DemoteF64,
316    F64ConvertI32S,
317    F64ConvertI32U,
318    F64ConvertI64S,
319    F64ConvertI64U,
320    F64PromoteF32,
321    I32ReinterpretF32,
322    I64ReinterpretF64,
323    F32ReinterpretI32,
324    F64ReinterpretI64,
325    I32Extend8S,
326    I32Extend16S,
327    I64Extend8S,
328    I64Extend16S,
329    I64Extend32S,
330    I32TruncSatF32S,
331    I32TruncSatF32U,
332    I32TruncSatF64S,
333    I32TruncSatF64U,
334    I64TruncSatF32S,
335    I64TruncSatF32U,
336    I64TruncSatF64S,
337    I64TruncSatF64U,
338}
339
340impl<'a> CodeBody<'a> {
341    /// Decode this function body's raw instruction bytes.
342    pub fn instructions(&self) -> Result<Vec<Instr>, DecodeError> {
343        self.instructions_with_offsets()
344            .map(|instrs| instrs.into_iter().map(|decoded| decoded.instr).collect())
345    }
346
347    /// Decode this function body's raw instruction bytes with absolute module offsets.
348    pub fn instructions_with_offsets(&self) -> Result<Vec<DecodedInstr>, DecodeError> {
349        decode_instr_sequence_with_offsets(self.body, self.body_offset)
350    }
351}
352
353/// Decode a function-body instruction sequence until its terminating `end`.
354pub fn decode_instr_sequence(bytes: &[u8], base_offset: usize) -> Result<Vec<Instr>, DecodeError> {
355    decode_instr_sequence_with_offsets(bytes, base_offset)
356        .map(|instrs| instrs.into_iter().map(|decoded| decoded.instr).collect())
357}
358
359/// Decode a function-body instruction sequence until its terminating `end`, preserving offsets.
360pub fn decode_instr_sequence_with_offsets(
361    bytes: &[u8],
362    base_offset: usize,
363) -> Result<Vec<DecodedInstr>, DecodeError> {
364    let mut cursor = Cursor::new(bytes);
365    let mut instrs = Vec::new();
366    let mut block_depth = 0usize;
367
368    loop {
369        let decoded = decode_instr_with_offset(&mut cursor, base_offset)?;
370        match decoded.instr {
371            Instr::Block(_) | Instr::Loop(_) | Instr::If(_) => block_depth += 1,
372            Instr::End => {
373                if block_depth == 0 {
374                    instrs.push(decoded);
375                    return Ok(instrs);
376                }
377                block_depth -= 1;
378            }
379            _ => {}
380        }
381
382        instrs.push(decoded);
383
384        if cursor.is_empty() {
385            return Err(DecodeError {
386                offset: ByteOffset(base_offset + cursor.position()),
387                context: DecodeContext::CodeSection,
388                kind: DecodeErrorKind::UnexpectedEof,
389            });
390        }
391    }
392}
393
394/// Decode a single instruction.
395pub fn decode_instr(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<Instr, DecodeError> {
396    decode_instr_with_offset(cursor, base_offset).map(|decoded| decoded.instr)
397}
398
399/// Decode a single instruction with its absolute module offset.
400pub fn decode_instr_with_offset(
401    cursor: &mut Cursor<'_>,
402    base_offset: usize,
403) -> Result<DecodedInstr, DecodeError> {
404    let opcode_offset = cursor.position();
405    let opcode = cursor.read_byte().map_err(|_| DecodeError {
406        offset: ByteOffset(base_offset + opcode_offset),
407        context: DecodeContext::CodeSection,
408        kind: DecodeErrorKind::UnexpectedEof,
409    })?;
410
411    let instr = match opcode {
412        0x00 => Instr::Unreachable,
413        0x01 => Instr::Nop,
414        0x02 => Instr::Block(parse_block_type(cursor, base_offset)?),
415        0x03 => Instr::Loop(parse_block_type(cursor, base_offset)?),
416        0x04 => Instr::If(parse_block_type(cursor, base_offset)?),
417        0x05 => Instr::Else,
418        0x0B => Instr::End,
419        0x0C => Instr::Br(LabelIdx(decode_u32(cursor, base_offset)?)),
420        0x0D => Instr::BrIf(LabelIdx(decode_u32(cursor, base_offset)?)),
421        0x0E => {
422            let target_count = decode_u32(cursor, base_offset)?;
423            let mut targets = Vec::with_capacity(cursor.capacity_hint(target_count));
424            for _ in 0..target_count {
425                targets.push(LabelIdx(decode_u32(cursor, base_offset)?));
426            }
427            let default = LabelIdx(decode_u32(cursor, base_offset)?);
428            Instr::BrTable { targets, default }
429        }
430        0x0F => Instr::Return,
431        0x10 => Instr::Call(FuncIdx(decode_u32(cursor, base_offset)?)),
432        0x12 => Instr::ReturnCall(FuncIdx(decode_u32(cursor, base_offset)?)),
433        0x14 => Instr::CallRef(TypeIdx(decode_u32(cursor, base_offset)?)),
434        0x15 => Instr::ReturnCallRef(TypeIdx(decode_u32(cursor, base_offset)?)),
435        0x11 => Instr::CallIndirect {
436            type_idx: TypeIdx(decode_u32(cursor, base_offset)?),
437            table_idx: parse_table_idx(cursor, base_offset)?,
438        },
439        0x13 => Instr::ReturnCallIndirect {
440            type_idx: TypeIdx(decode_u32(cursor, base_offset)?),
441            table_idx: parse_table_idx(cursor, base_offset)?,
442        },
443        0x1A => Instr::Drop,
444        0x1B => Instr::Select,
445        0x1C => Instr::SelectTyped(parse_result_types(cursor, base_offset)?),
446        0x20 => Instr::LocalGet(LocalIdx(decode_u32(cursor, base_offset)?)),
447        0x21 => Instr::LocalSet(LocalIdx(decode_u32(cursor, base_offset)?)),
448        0x22 => Instr::LocalTee(LocalIdx(decode_u32(cursor, base_offset)?)),
449        0x23 => Instr::GlobalGet(GlobalIdx(decode_u32(cursor, base_offset)?)),
450        0x24 => Instr::GlobalSet(GlobalIdx(decode_u32(cursor, base_offset)?)),
451        0x25 => Instr::TableGet(parse_table_idx(cursor, base_offset)?),
452        0x26 => Instr::TableSet(parse_table_idx(cursor, base_offset)?),
453        0xFC => decode_bulk_memory_instr(cursor, base_offset)?,
454        0xFD => decode_simd_instr(cursor, base_offset)?,
455        0x28 => Instr::I32Load(parse_memarg(cursor, base_offset)?),
456        0x29 => Instr::I64Load(parse_memarg(cursor, base_offset)?),
457        0x2A => Instr::F32Load(parse_memarg(cursor, base_offset)?),
458        0x2B => Instr::F64Load(parse_memarg(cursor, base_offset)?),
459        0x2C => Instr::I32Load8S(parse_memarg(cursor, base_offset)?),
460        0x2D => Instr::I32Load8U(parse_memarg(cursor, base_offset)?),
461        0x2E => Instr::I32Load16S(parse_memarg(cursor, base_offset)?),
462        0x2F => Instr::I32Load16U(parse_memarg(cursor, base_offset)?),
463        0x30 => Instr::I64Load8S(parse_memarg(cursor, base_offset)?),
464        0x31 => Instr::I64Load8U(parse_memarg(cursor, base_offset)?),
465        0x32 => Instr::I64Load16S(parse_memarg(cursor, base_offset)?),
466        0x33 => Instr::I64Load16U(parse_memarg(cursor, base_offset)?),
467        0x34 => Instr::I64Load32S(parse_memarg(cursor, base_offset)?),
468        0x35 => Instr::I64Load32U(parse_memarg(cursor, base_offset)?),
469        0x36 => Instr::I32Store(parse_memarg(cursor, base_offset)?),
470        0x37 => Instr::I64Store(parse_memarg(cursor, base_offset)?),
471        0x38 => Instr::F32Store(parse_memarg(cursor, base_offset)?),
472        0x39 => Instr::F64Store(parse_memarg(cursor, base_offset)?),
473        0x3A => Instr::I32Store8(parse_memarg(cursor, base_offset)?),
474        0x3B => Instr::I32Store16(parse_memarg(cursor, base_offset)?),
475        0x3C => Instr::I64Store8(parse_memarg(cursor, base_offset)?),
476        0x3D => Instr::I64Store16(parse_memarg(cursor, base_offset)?),
477        0x3E => Instr::I64Store32(parse_memarg(cursor, base_offset)?),
478        0x3F => Instr::MemorySize(parse_mem_idx(cursor, base_offset)?),
479        0x40 => Instr::MemoryGrow(parse_mem_idx(cursor, base_offset)?),
480        0x41 => Instr::I32Const(decode_i32(cursor, base_offset)?),
481        0x42 => Instr::I64Const(decode_i64(cursor, base_offset)?),
482        0x43 => Instr::F32Const(decode_f32(cursor, base_offset)?),
483        0x44 => Instr::F64Const(decode_f64(cursor, base_offset)?),
484        0xD0 => Instr::RefNull(RefType::from_parts(
485            true,
486            parse_heap_type(cursor, base_offset, DecodeContext::CodeSection)?,
487        )),
488        0xD1 => Instr::RefIsNull,
489        0xD2 => Instr::RefFunc(FuncIdx(decode_u32(cursor, base_offset)?)),
490        0xD4 => Instr::RefAsNonNull,
491        0xD5 => Instr::BrOnNull(LabelIdx(decode_u32(cursor, base_offset)?)),
492        0xD6 => Instr::BrOnNonNull(LabelIdx(decode_u32(cursor, base_offset)?)),
493        0x45 => Instr::I32Eqz,
494        0x46 => Instr::I32Eq,
495        0x47 => Instr::I32Ne,
496        0x48 => Instr::I32LtS,
497        0x49 => Instr::I32LtU,
498        0x4A => Instr::I32GtS,
499        0x4B => Instr::I32GtU,
500        0x4C => Instr::I32LeS,
501        0x4D => Instr::I32LeU,
502        0x4E => Instr::I32GeS,
503        0x4F => Instr::I32GeU,
504        0x50 => Instr::I64Eqz,
505        0x51 => Instr::I64Eq,
506        0x52 => Instr::I64Ne,
507        0x53 => Instr::I64LtS,
508        0x54 => Instr::I64LtU,
509        0x55 => Instr::I64GtS,
510        0x56 => Instr::I64GtU,
511        0x57 => Instr::I64LeS,
512        0x58 => Instr::I64LeU,
513        0x59 => Instr::I64GeS,
514        0x5A => Instr::I64GeU,
515        0x5B => Instr::F32Eq,
516        0x5C => Instr::F32Ne,
517        0x5D => Instr::F32Lt,
518        0x5E => Instr::F32Gt,
519        0x5F => Instr::F32Le,
520        0x60 => Instr::F32Ge,
521        0x61 => Instr::F64Eq,
522        0x62 => Instr::F64Ne,
523        0x63 => Instr::F64Lt,
524        0x64 => Instr::F64Gt,
525        0x65 => Instr::F64Le,
526        0x66 => Instr::F64Ge,
527        0x67 => Instr::I32Clz,
528        0x68 => Instr::I32Ctz,
529        0x69 => Instr::I32Popcnt,
530        0x6A => Instr::I32Add,
531        0x6B => Instr::I32Sub,
532        0x6C => Instr::I32Mul,
533        0x6D => Instr::I32DivS,
534        0x6E => Instr::I32DivU,
535        0x6F => Instr::I32RemS,
536        0x70 => Instr::I32RemU,
537        0x71 => Instr::I32And,
538        0x72 => Instr::I32Or,
539        0x73 => Instr::I32Xor,
540        0x74 => Instr::I32Shl,
541        0x75 => Instr::I32ShrS,
542        0x76 => Instr::I32ShrU,
543        0x77 => Instr::I32Rotl,
544        0x78 => Instr::I32Rotr,
545        0x79 => Instr::I64Clz,
546        0x7A => Instr::I64Ctz,
547        0x7B => Instr::I64Popcnt,
548        0x7C => Instr::I64Add,
549        0x7D => Instr::I64Sub,
550        0x7E => Instr::I64Mul,
551        0x7F => Instr::I64DivS,
552        0x80 => Instr::I64DivU,
553        0x81 => Instr::I64RemS,
554        0x82 => Instr::I64RemU,
555        0x83 => Instr::I64And,
556        0x84 => Instr::I64Or,
557        0x85 => Instr::I64Xor,
558        0x86 => Instr::I64Shl,
559        0x87 => Instr::I64ShrS,
560        0x88 => Instr::I64ShrU,
561        0x89 => Instr::I64Rotl,
562        0x8A => Instr::I64Rotr,
563        0x8B => Instr::F32Abs,
564        0x8C => Instr::F32Neg,
565        0x8D => Instr::F32Ceil,
566        0x8E => Instr::F32Floor,
567        0x8F => Instr::F32Trunc,
568        0x90 => Instr::F32Nearest,
569        0x91 => Instr::F32Sqrt,
570        0x92 => Instr::F32Add,
571        0x93 => Instr::F32Sub,
572        0x94 => Instr::F32Mul,
573        0x95 => Instr::F32Div,
574        0x96 => Instr::F32Min,
575        0x97 => Instr::F32Max,
576        0x98 => Instr::F32Copysign,
577        0x99 => Instr::F64Abs,
578        0x9A => Instr::F64Neg,
579        0x9B => Instr::F64Ceil,
580        0x9C => Instr::F64Floor,
581        0x9D => Instr::F64Trunc,
582        0x9E => Instr::F64Nearest,
583        0x9F => Instr::F64Sqrt,
584        0xA0 => Instr::F64Add,
585        0xA1 => Instr::F64Sub,
586        0xA2 => Instr::F64Mul,
587        0xA3 => Instr::F64Div,
588        0xA4 => Instr::F64Min,
589        0xA5 => Instr::F64Max,
590        0xA6 => Instr::F64Copysign,
591        0xA7 => Instr::I32WrapI64,
592        0xA8 => Instr::I32TruncF32S,
593        0xA9 => Instr::I32TruncF32U,
594        0xAA => Instr::I32TruncF64S,
595        0xAB => Instr::I32TruncF64U,
596        0xAC => Instr::I64ExtendI32S,
597        0xAD => Instr::I64ExtendI32U,
598        0xAE => Instr::I64TruncF32S,
599        0xAF => Instr::I64TruncF32U,
600        0xB0 => Instr::I64TruncF64S,
601        0xB1 => Instr::I64TruncF64U,
602        0xB2 => Instr::F32ConvertI32S,
603        0xB3 => Instr::F32ConvertI32U,
604        0xB4 => Instr::F32ConvertI64S,
605        0xB5 => Instr::F32ConvertI64U,
606        0xB6 => Instr::F32DemoteF64,
607        0xB7 => Instr::F64ConvertI32S,
608        0xB8 => Instr::F64ConvertI32U,
609        0xB9 => Instr::F64ConvertI64S,
610        0xBA => Instr::F64ConvertI64U,
611        0xBB => Instr::F64PromoteF32,
612        0xBC => Instr::I32ReinterpretF32,
613        0xBD => Instr::I64ReinterpretF64,
614        0xBE => Instr::F32ReinterpretI32,
615        0xBF => Instr::F64ReinterpretI64,
616        0xC0 => Instr::I32Extend8S,
617        0xC1 => Instr::I32Extend16S,
618        0xC2 => Instr::I64Extend8S,
619        0xC3 => Instr::I64Extend16S,
620        0xC4 => Instr::I64Extend32S,
621        _ => {
622            return Err(DecodeError {
623                offset: ByteOffset(base_offset + opcode_offset),
624                context: DecodeContext::CodeSection,
625                kind: DecodeErrorKind::UnknownOpcode { byte: opcode },
626            });
627        }
628    };
629
630    Ok(DecodedInstr {
631        offset: ByteOffset(base_offset + opcode_offset),
632        instr,
633    })
634}
635
636fn decode_bulk_memory_instr(
637    cursor: &mut Cursor<'_>,
638    base_offset: usize,
639) -> Result<Instr, DecodeError> {
640    let opcode = decode_u32(cursor, base_offset)?;
641    match opcode {
642        0 => Ok(Instr::I32TruncSatF32S),
643        1 => Ok(Instr::I32TruncSatF32U),
644        2 => Ok(Instr::I32TruncSatF64S),
645        3 => Ok(Instr::I32TruncSatF64U),
646        4 => Ok(Instr::I64TruncSatF32S),
647        5 => Ok(Instr::I64TruncSatF32U),
648        6 => Ok(Instr::I64TruncSatF64S),
649        7 => Ok(Instr::I64TruncSatF64U),
650        8 => {
651            let data = crate::types::DataIdx(decode_u32(cursor, base_offset)?);
652            let mem = parse_mem_idx(cursor, base_offset)?;
653            Ok(Instr::MemoryInit(data, mem))
654        }
655        9 => Ok(Instr::DataDrop(crate::types::DataIdx(decode_u32(
656            cursor,
657            base_offset,
658        )?))),
659        10 => {
660            let dst = parse_mem_idx(cursor, base_offset)?;
661            let src = parse_mem_idx(cursor, base_offset)?;
662            Ok(Instr::MemoryCopy { dst, src })
663        }
664        11 => Ok(Instr::MemoryFill(parse_mem_idx(cursor, base_offset)?)),
665        12 => Ok(Instr::TableInit {
666            elem_idx: ElemIdx(decode_u32(cursor, base_offset)?),
667            table_idx: parse_table_idx(cursor, base_offset)?,
668        }),
669        13 => Ok(Instr::ElemDrop(ElemIdx(decode_u32(cursor, base_offset)?))),
670        14 => {
671            let dst = parse_table_idx(cursor, base_offset)?;
672            let src = parse_table_idx(cursor, base_offset)?;
673            Ok(Instr::TableCopy { dst, src })
674        }
675        15 => Ok(Instr::TableGrow(parse_table_idx(cursor, base_offset)?)),
676        16 => Ok(Instr::TableSize(parse_table_idx(cursor, base_offset)?)),
677        17 => Ok(Instr::TableFill(parse_table_idx(cursor, base_offset)?)),
678        _ => Err(DecodeError {
679            offset: ByteOffset(base_offset),
680            context: DecodeContext::CodeSection,
681            kind: DecodeErrorKind::UnknownOpcode { byte: 0xFC },
682        }),
683    }
684}
685
686fn decode_simd_instr(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<Instr, DecodeError> {
687    let opcode = decode_u32(cursor, base_offset)?;
688    match opcode {
689        0 => Ok(Instr::V128Load(parse_memarg(cursor, base_offset)?)),
690        1 => Ok(Instr::V128Load8x8S(parse_memarg(cursor, base_offset)?)),
691        2 => Ok(Instr::V128Load8x8U(parse_memarg(cursor, base_offset)?)),
692        3 => Ok(Instr::V128Load16x4S(parse_memarg(cursor, base_offset)?)),
693        4 => Ok(Instr::V128Load16x4U(parse_memarg(cursor, base_offset)?)),
694        5 => Ok(Instr::V128Load32x2S(parse_memarg(cursor, base_offset)?)),
695        6 => Ok(Instr::V128Load32x2U(parse_memarg(cursor, base_offset)?)),
696        7 => Ok(Instr::V128Load8Splat(parse_memarg(cursor, base_offset)?)),
697        8 => Ok(Instr::V128Load16Splat(parse_memarg(cursor, base_offset)?)),
698        9 => Ok(Instr::V128Load32Splat(parse_memarg(cursor, base_offset)?)),
699        10 => Ok(Instr::V128Load64Splat(parse_memarg(cursor, base_offset)?)),
700        11 => Ok(Instr::V128Store(parse_memarg(cursor, base_offset)?)),
701        12 => Ok(Instr::V128Const(parse_v128_const(cursor, base_offset)?)),
702        15 => Ok(Instr::I8x16Splat),
703        16 => Ok(Instr::I16x8Splat),
704        17 => Ok(Instr::I32x4Splat),
705        18 => Ok(Instr::I64x2Splat),
706        19 => Ok(Instr::F32x4Splat),
707        20 => Ok(Instr::F64x2Splat),
708        27 => Ok(Instr::I32x4ExtractLane(parse_lane_idx(
709            cursor,
710            base_offset,
711        )?)),
712        28 => Ok(Instr::I32x4ReplaceLane(parse_lane_idx(
713            cursor,
714            base_offset,
715        )?)),
716        31 => Ok(Instr::F32x4ExtractLane(parse_lane_idx(
717            cursor,
718            base_offset,
719        )?)),
720        32 => Ok(Instr::F32x4ReplaceLane(parse_lane_idx(
721            cursor,
722            base_offset,
723        )?)),
724        77 => Ok(Instr::V128Not),
725        78 => Ok(Instr::V128And),
726        80 => Ok(Instr::V128Or),
727        81 => Ok(Instr::V128Xor),
728        110 => Ok(Instr::I8x16Add),
729        113 => Ok(Instr::I8x16Sub),
730        142 => Ok(Instr::I16x8Add),
731        145 => Ok(Instr::I16x8Sub),
732        174 => Ok(Instr::I32x4Add),
733        177 => Ok(Instr::I32x4Sub),
734        181 => Ok(Instr::I32x4Mul),
735        206 => Ok(Instr::I64x2Add),
736        209 => Ok(Instr::I64x2Sub),
737        228 => Ok(Instr::F32x4Add),
738        229 => Ok(Instr::F32x4Sub),
739        230 => Ok(Instr::F32x4Mul),
740        231 => Ok(Instr::F32x4Div),
741        240 => Ok(Instr::F64x2Add),
742        241 => Ok(Instr::F64x2Sub),
743        242 => Ok(Instr::F64x2Mul),
744        243 => Ok(Instr::F64x2Div),
745        84 => {
746            let (memarg, lane) = parse_memarg_lane(cursor, base_offset)?;
747            Ok(Instr::V128Load8Lane { memarg, lane })
748        }
749        85 => {
750            let (memarg, lane) = parse_memarg_lane(cursor, base_offset)?;
751            Ok(Instr::V128Load16Lane { memarg, lane })
752        }
753        86 => {
754            let (memarg, lane) = parse_memarg_lane(cursor, base_offset)?;
755            Ok(Instr::V128Load32Lane { memarg, lane })
756        }
757        87 => {
758            let (memarg, lane) = parse_memarg_lane(cursor, base_offset)?;
759            Ok(Instr::V128Load64Lane { memarg, lane })
760        }
761        88 => {
762            let (memarg, lane) = parse_memarg_lane(cursor, base_offset)?;
763            Ok(Instr::V128Store8Lane { memarg, lane })
764        }
765        89 => {
766            let (memarg, lane) = parse_memarg_lane(cursor, base_offset)?;
767            Ok(Instr::V128Store16Lane { memarg, lane })
768        }
769        90 => {
770            let (memarg, lane) = parse_memarg_lane(cursor, base_offset)?;
771            Ok(Instr::V128Store32Lane { memarg, lane })
772        }
773        91 => {
774            let (memarg, lane) = parse_memarg_lane(cursor, base_offset)?;
775            Ok(Instr::V128Store64Lane { memarg, lane })
776        }
777        92 => Ok(Instr::V128Load32Zero(parse_memarg(cursor, base_offset)?)),
778        93 => Ok(Instr::V128Load64Zero(parse_memarg(cursor, base_offset)?)),
779        _ => Err(DecodeError {
780            offset: ByteOffset(base_offset),
781            context: DecodeContext::CodeSection,
782            kind: DecodeErrorKind::UnknownSimdOpcode { opcode },
783        }),
784    }
785}
786
787fn parse_memarg(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<MemArg, DecodeError> {
788    let flags = decode_u32(cursor, base_offset)?;
789    let (align, memory) = if flags & (1 << 6) != 0 {
790        (flags ^ (1 << 6), MemIdx(decode_u32(cursor, base_offset)?))
791    } else {
792        (flags, MemIdx(0))
793    };
794    let offset = decode_u32(cursor, base_offset)?;
795    Ok(MemArg {
796        align,
797        offset,
798        memory,
799    })
800}
801
802fn parse_memarg_lane(
803    cursor: &mut Cursor<'_>,
804    base_offset: usize,
805) -> Result<(MemArg, u8), DecodeError> {
806    let memarg = parse_memarg(cursor, base_offset)?;
807    let pos = cursor.position();
808    let lane = cursor.read_byte().map_err(|_| DecodeError {
809        offset: ByteOffset(base_offset + pos),
810        context: DecodeContext::CodeSection,
811        kind: DecodeErrorKind::UnexpectedEof,
812    })?;
813    Ok((memarg, lane))
814}
815
816fn parse_lane_idx(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<u8, DecodeError> {
817    let pos = cursor.position();
818    cursor.read_byte().map_err(|_| DecodeError {
819        offset: ByteOffset(base_offset + pos),
820        context: DecodeContext::CodeSection,
821        kind: DecodeErrorKind::UnexpectedEof,
822    })
823}
824
825fn parse_v128_const(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<[u8; 16], DecodeError> {
826    let pos = cursor.position();
827    let bytes = cursor.read_bytes(16).map_err(|_| DecodeError {
828        offset: ByteOffset(base_offset + pos),
829        context: DecodeContext::CodeSection,
830        kind: DecodeErrorKind::UnexpectedEof,
831    })?;
832    Ok(bytes.try_into().expect("16 bytes"))
833}
834
835fn parse_mem_idx(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<MemIdx, DecodeError> {
836    Ok(MemIdx(decode_u32(cursor, base_offset)?))
837}
838
839fn parse_table_idx(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<TableIdx, DecodeError> {
840    Ok(TableIdx(decode_u32(cursor, base_offset)?))
841}
842
843fn parse_result_types(
844    cursor: &mut Cursor<'_>,
845    base_offset: usize,
846) -> Result<Vec<ValType>, DecodeError> {
847    let count = decode_u32(cursor, base_offset)?;
848    let mut types = Vec::with_capacity(cursor.capacity_hint(count));
849    for _ in 0..count {
850        types.push(parse_binary_val_type(
851            cursor,
852            base_offset,
853            DecodeContext::CodeSection,
854        )?);
855    }
856    Ok(types)
857}
858
859fn parse_block_type(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<BlockType, DecodeError> {
860    let start = cursor.position();
861    let first = cursor.read_byte().map_err(|_| DecodeError {
862        offset: ByteOffset(base_offset + start),
863        context: DecodeContext::CodeSection,
864        kind: DecodeErrorKind::UnexpectedEof,
865    })?;
866
867    if first == 0x40 {
868        return Ok(BlockType::Empty);
869    }
870
871    if matches!(first, 0x7B..=0x7F | 0x70 | 0x6F | 0x63 | 0x64) {
872        return Ok(BlockType::Val(parse_val_type_with_first_byte(
873            cursor,
874            first,
875            base_offset + start,
876            DecodeContext::CodeSection,
877        )?));
878    }
879
880    let type_idx = decode_block_type_idx(cursor, first, base_offset + start)?;
881    Ok(BlockType::TypeIdx(type_idx))
882}
883
884fn decode_block_type_idx(
885    cursor: &mut Cursor<'_>,
886    first: u8,
887    absolute_offset: usize,
888) -> Result<u32, DecodeError> {
889    let mut result = i64::from(first & 0x7F);
890    let mut shift = 7u32;
891    let mut byte = first;
892    let mut i = 0;
893
894    while byte & 0x80 != 0 {
895        i += 1;
896        if i >= 5 {
897            return Err(DecodeError {
898                offset: ByteOffset(absolute_offset),
899                context: DecodeContext::CodeSection,
900                kind: DecodeErrorKind::Leb128TooLong,
901            });
902        }
903
904        byte = cursor.read_byte().map_err(|_| DecodeError {
905            offset: ByteOffset(absolute_offset),
906            context: DecodeContext::CodeSection,
907            kind: DecodeErrorKind::UnexpectedEof,
908        })?;
909        result |= i64::from(byte & 0x7F) << shift;
910        shift += 7;
911    }
912
913    if shift < 33 && (byte & 0x40) != 0 {
914        result |= (!0i64) << shift;
915    }
916
917    if result < 0 {
918        return Err(DecodeError {
919            offset: ByteOffset(absolute_offset),
920            context: DecodeContext::CodeSection,
921            kind: DecodeErrorKind::Leb128Overflow,
922        });
923    }
924
925    Ok(result as u32)
926}
927
928fn decode_u32(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<u32, DecodeError> {
929    leb128::decode_u32(cursor).map_err(|mut e| {
930        e.context = DecodeContext::CodeSection;
931        e.offset = ByteOffset(base_offset + e.offset.0);
932        e
933    })
934}
935
936fn decode_i32(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<i32, DecodeError> {
937    leb128::decode_i32(cursor).map_err(|mut e| {
938        e.context = DecodeContext::CodeSection;
939        e.offset = ByteOffset(base_offset + e.offset.0);
940        e
941    })
942}
943
944fn decode_i64(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<i64, DecodeError> {
945    leb128::decode_i64(cursor).map_err(|mut e| {
946        e.context = DecodeContext::CodeSection;
947        e.offset = ByteOffset(base_offset + e.offset.0);
948        e
949    })
950}
951
952fn decode_f32(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<f32, DecodeError> {
953    let offset = cursor.position();
954    let bytes = cursor.read_bytes(4).map_err(|_| DecodeError {
955        offset: ByteOffset(base_offset + offset),
956        context: DecodeContext::CodeSection,
957        kind: DecodeErrorKind::UnexpectedEof,
958    })?;
959    Ok(f32::from_le_bytes(bytes.try_into().expect("4 bytes")))
960}
961
962fn decode_f64(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<f64, DecodeError> {
963    let offset = cursor.position();
964    let bytes = cursor.read_bytes(8).map_err(|_| DecodeError {
965        offset: ByteOffset(base_offset + offset),
966        context: DecodeContext::CodeSection,
967        kind: DecodeErrorKind::UnexpectedEof,
968    })?;
969    Ok(f64::from_le_bytes(bytes.try_into().expect("8 bytes")))
970}
971
972#[cfg(test)]
973mod tests {
974    use alloc::vec;
975
976    use super::*;
977    use crate::types::{CodeBody, NumType};
978
979    #[test]
980    fn decode_simple_add_body() {
981        let body = CodeBody {
982            locals: Vec::new(),
983            body: &[0x20, 0x00, 0x20, 0x01, 0x6A, 0x0B],
984            body_offset: 100,
985        };
986
987        let instrs = body.instructions().unwrap();
988        assert_eq!(
989            instrs,
990            vec![
991                Instr::LocalGet(LocalIdx(0)),
992                Instr::LocalGet(LocalIdx(1)),
993                Instr::I32Add,
994                Instr::End,
995            ]
996        );
997    }
998
999    #[test]
1000    fn decode_block_with_branch() {
1001        let instrs = decode_instr_sequence(&[0x02, 0x40, 0x0C, 0x00, 0x0B, 0x0B], 200).unwrap();
1002        assert_eq!(
1003            instrs,
1004            vec![
1005                Instr::Block(BlockType::Empty),
1006                Instr::Br(LabelIdx(0)),
1007                Instr::End,
1008                Instr::End,
1009            ]
1010        );
1011    }
1012
1013    #[test]
1014    fn decode_br_table() {
1015        let instrs = decode_instr_sequence(&[0x0E, 0x02, 0x00, 0x01, 0x02, 0x0B], 220).unwrap();
1016        assert_eq!(
1017            instrs,
1018            vec![
1019                Instr::BrTable {
1020                    targets: vec![LabelIdx(0), LabelIdx(1)],
1021                    default: LabelIdx(2),
1022                },
1023                Instr::End,
1024            ]
1025        );
1026    }
1027
1028    #[test]
1029    fn decode_typed_select() {
1030        let instrs = decode_instr_sequence(&[0x1C, 0x01, 0x7E, 0x0B], 230).unwrap();
1031        assert_eq!(
1032            instrs,
1033            vec![
1034                Instr::SelectTyped(vec![ValType::Num(NumType::I64)]),
1035                Instr::End,
1036            ]
1037        );
1038    }
1039
1040    #[test]
1041    fn decode_globals_and_memory_ops() {
1042        let instrs = decode_instr_sequence(
1043            &[
1044                0x23, 0x00, 0x24, 0x01, 0x28, 0x02, 0x00, 0x2C, 0x00, 0x01, 0x35, 0x02, 0x02, 0x36,
1045                0x02, 0x04, 0x3A, 0x00, 0x08, 0x3E, 0x02, 0x0C, 0x3F, 0x00, 0x40, 0x00, 0x0B,
1046            ],
1047            240,
1048        )
1049        .unwrap();
1050        assert_eq!(
1051            instrs,
1052            vec![
1053                Instr::GlobalGet(GlobalIdx(0)),
1054                Instr::GlobalSet(GlobalIdx(1)),
1055                Instr::I32Load(MemArg {
1056                    align: 2,
1057                    offset: 0,
1058                    memory: MemIdx(0),
1059                }),
1060                Instr::I32Load8S(MemArg {
1061                    align: 0,
1062                    offset: 1,
1063                    memory: MemIdx(0),
1064                }),
1065                Instr::I64Load32U(MemArg {
1066                    align: 2,
1067                    offset: 2,
1068                    memory: MemIdx(0),
1069                }),
1070                Instr::I32Store(MemArg {
1071                    align: 2,
1072                    offset: 4,
1073                    memory: MemIdx(0),
1074                }),
1075                Instr::I32Store8(MemArg {
1076                    align: 0,
1077                    offset: 8,
1078                    memory: MemIdx(0),
1079                }),
1080                Instr::I64Store32(MemArg {
1081                    align: 2,
1082                    offset: 12,
1083                    memory: MemIdx(0),
1084                }),
1085                Instr::MemorySize(MemIdx(0)),
1086                Instr::MemoryGrow(MemIdx(0)),
1087                Instr::End,
1088            ]
1089        );
1090    }
1091
1092    #[test]
1093    fn decode_all_scalar_memory_opcodes() {
1094        let instrs = decode_instr_sequence(
1095            &[
1096                0x28, 0x02, 0x00, 0x29, 0x03, 0x00, 0x2A, 0x02, 0x00, 0x2B, 0x03, 0x00, 0x2C, 0x00,
1097                0x00, 0x2D, 0x00, 0x00, 0x2E, 0x01, 0x00, 0x2F, 0x01, 0x00, 0x30, 0x00, 0x00, 0x31,
1098                0x00, 0x00, 0x32, 0x01, 0x00, 0x33, 0x01, 0x00, 0x34, 0x02, 0x00, 0x35, 0x02, 0x00,
1099                0x36, 0x02, 0x00, 0x37, 0x03, 0x00, 0x38, 0x02, 0x00, 0x39, 0x03, 0x00, 0x3A, 0x00,
1100                0x00, 0x3B, 0x01, 0x00, 0x3C, 0x00, 0x00, 0x3D, 0x01, 0x00, 0x3E, 0x02, 0x00, 0x0B,
1101            ],
1102            280,
1103        )
1104        .unwrap();
1105
1106        assert_eq!(instrs.len(), 24);
1107        assert!(matches!(instrs[0], Instr::I32Load(_)));
1108        assert!(matches!(instrs[1], Instr::I64Load(_)));
1109        assert!(matches!(instrs[2], Instr::F32Load(_)));
1110        assert!(matches!(instrs[3], Instr::F64Load(_)));
1111        assert!(matches!(instrs[4], Instr::I32Load8S(_)));
1112        assert!(matches!(instrs[5], Instr::I32Load8U(_)));
1113        assert!(matches!(instrs[6], Instr::I32Load16S(_)));
1114        assert!(matches!(instrs[7], Instr::I32Load16U(_)));
1115        assert!(matches!(instrs[8], Instr::I64Load8S(_)));
1116        assert!(matches!(instrs[9], Instr::I64Load8U(_)));
1117        assert!(matches!(instrs[10], Instr::I64Load16S(_)));
1118        assert!(matches!(instrs[11], Instr::I64Load16U(_)));
1119        assert!(matches!(instrs[12], Instr::I64Load32S(_)));
1120        assert!(matches!(instrs[13], Instr::I64Load32U(_)));
1121        assert!(matches!(instrs[14], Instr::I32Store(_)));
1122        assert!(matches!(instrs[15], Instr::I64Store(_)));
1123        assert!(matches!(instrs[16], Instr::F32Store(_)));
1124        assert!(matches!(instrs[17], Instr::F64Store(_)));
1125        assert!(matches!(instrs[18], Instr::I32Store8(_)));
1126        assert!(matches!(instrs[19], Instr::I32Store16(_)));
1127        assert!(matches!(instrs[20], Instr::I64Store8(_)));
1128        assert!(matches!(instrs[21], Instr::I64Store16(_)));
1129        assert!(matches!(instrs[22], Instr::I64Store32(_)));
1130        assert!(matches!(instrs[23], Instr::End));
1131    }
1132
1133    #[test]
1134    fn decode_ref_instructions() {
1135        let instrs = decode_instr_sequence(
1136            &[
1137                0xD0, 0x70, 0xD1, 0xD2, 0x00, 0xD4, 0xD5, 0x01, 0xD6, 0x02, 0x0B,
1138            ],
1139            90,
1140        )
1141        .unwrap();
1142        assert_eq!(
1143            instrs,
1144            vec![
1145                Instr::RefNull(RefType::FuncRef),
1146                Instr::RefIsNull,
1147                Instr::RefFunc(FuncIdx(0)),
1148                Instr::RefAsNonNull,
1149                Instr::BrOnNull(LabelIdx(1)),
1150                Instr::BrOnNonNull(LabelIdx(2)),
1151                Instr::End
1152            ]
1153        );
1154    }
1155
1156    #[test]
1157    fn decode_typed_ref_null_instruction() {
1158        let instrs = decode_instr_sequence(&[0xD0, 0x00, 0x0B], 92).unwrap();
1159        assert_eq!(
1160            instrs,
1161            vec![
1162                Instr::RefNull(RefType::concrete(true, TypeIdx(0))),
1163                Instr::End
1164            ]
1165        );
1166    }
1167
1168    #[test]
1169    fn decode_call_indirect_and_table_ops() {
1170        let instrs = decode_instr_sequence(
1171            &[
1172                0x12, 0x00, 0x14, 0x03, 0x15, 0x04, 0x11, 0x01, 0x00, 0x13, 0x02, 0x00, 0x25, 0x00,
1173                0x26, 0x00, 0x0B,
1174            ],
1175            95,
1176        )
1177        .unwrap();
1178        assert_eq!(
1179            instrs,
1180            vec![
1181                Instr::ReturnCall(FuncIdx(0)),
1182                Instr::CallRef(TypeIdx(3)),
1183                Instr::ReturnCallRef(TypeIdx(4)),
1184                Instr::CallIndirect {
1185                    type_idx: TypeIdx(1),
1186                    table_idx: TableIdx(0),
1187                },
1188                Instr::ReturnCallIndirect {
1189                    type_idx: TypeIdx(2),
1190                    table_idx: TableIdx(0),
1191                },
1192                Instr::TableGet(TableIdx(0)),
1193                Instr::TableSet(TableIdx(0)),
1194                Instr::End,
1195            ]
1196        );
1197    }
1198
1199    #[test]
1200    fn decode_broader_numeric_ops() {
1201        let instrs = decode_instr_sequence(
1202            &[
1203                0x50, 0x51, 0x5B, 0x61, 0x67, 0x6B, 0x76, 0x79, 0x7D, 0x88, 0x8B, 0x92, 0x98, 0x99,
1204                0xA0, 0xA6, 0x0B,
1205            ],
1206            140,
1207        )
1208        .unwrap();
1209        assert_eq!(
1210            instrs,
1211            vec![
1212                Instr::I64Eqz,
1213                Instr::I64Eq,
1214                Instr::F32Eq,
1215                Instr::F64Eq,
1216                Instr::I32Clz,
1217                Instr::I32Sub,
1218                Instr::I32ShrU,
1219                Instr::I64Clz,
1220                Instr::I64Sub,
1221                Instr::I64ShrU,
1222                Instr::F32Abs,
1223                Instr::F32Add,
1224                Instr::F32Copysign,
1225                Instr::F64Abs,
1226                Instr::F64Add,
1227                Instr::F64Copysign,
1228                Instr::End,
1229            ]
1230        );
1231    }
1232
1233    #[test]
1234    fn decode_conversions_and_reinterpretations() {
1235        let instrs = decode_instr_sequence(
1236            &[
1237                0xA7, 0xA8, 0xAB, 0xAC, 0xB1, 0xB2, 0xB5, 0xB6, 0xB7, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE,
1238                0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xFC, 0x00, 0xFC, 0x03, 0xFC, 0x04, 0xFC, 0x07,
1239                0x0B,
1240            ],
1241            180,
1242        )
1243        .unwrap();
1244        assert_eq!(
1245            instrs,
1246            vec![
1247                Instr::I32WrapI64,
1248                Instr::I32TruncF32S,
1249                Instr::I32TruncF64U,
1250                Instr::I64ExtendI32S,
1251                Instr::I64TruncF64U,
1252                Instr::F32ConvertI32S,
1253                Instr::F32ConvertI64U,
1254                Instr::F32DemoteF64,
1255                Instr::F64ConvertI32S,
1256                Instr::F64ConvertI64U,
1257                Instr::F64PromoteF32,
1258                Instr::I32ReinterpretF32,
1259                Instr::I64ReinterpretF64,
1260                Instr::F32ReinterpretI32,
1261                Instr::F64ReinterpretI64,
1262                Instr::I32Extend8S,
1263                Instr::I32Extend16S,
1264                Instr::I64Extend8S,
1265                Instr::I64Extend16S,
1266                Instr::I64Extend32S,
1267                Instr::I32TruncSatF32S,
1268                Instr::I32TruncSatF64U,
1269                Instr::I64TruncSatF32S,
1270                Instr::I64TruncSatF64U,
1271                Instr::End,
1272            ]
1273        );
1274    }
1275
1276    #[test]
1277    fn decode_bulk_memory_ops() {
1278        let instrs = decode_instr_sequence(
1279            &[
1280                0xFC, 0x08, 0x00, 0x00, 0xFC, 0x09, 0x00, 0xFC, 0x0A, 0x00, 0x00, 0xFC, 0x0B, 0x00,
1281                0xFC, 0x0C, 0x00, 0x00, 0xFC, 0x0D, 0x00, 0xFC, 0x0E, 0x00, 0x00, 0xFC, 0x0F, 0x00,
1282                0xFC, 0x10, 0x00, 0xFC, 0x11, 0x00, 0x0B,
1283            ],
1284            315,
1285        )
1286        .unwrap();
1287        assert_eq!(
1288            instrs,
1289            vec![
1290                Instr::MemoryInit(crate::types::DataIdx(0), MemIdx(0)),
1291                Instr::DataDrop(crate::types::DataIdx(0)),
1292                Instr::MemoryCopy {
1293                    dst: MemIdx(0),
1294                    src: MemIdx(0)
1295                },
1296                Instr::MemoryFill(MemIdx(0)),
1297                Instr::TableInit {
1298                    elem_idx: ElemIdx(0),
1299                    table_idx: TableIdx(0),
1300                },
1301                Instr::ElemDrop(ElemIdx(0)),
1302                Instr::TableCopy {
1303                    dst: TableIdx(0),
1304                    src: TableIdx(0),
1305                },
1306                Instr::TableGrow(TableIdx(0)),
1307                Instr::TableSize(TableIdx(0)),
1308                Instr::TableFill(TableIdx(0)),
1309                Instr::End,
1310            ]
1311        );
1312    }
1313
1314    #[test]
1315    fn decode_nonzero_memory_indices() {
1316        let instrs = decode_instr_sequence(
1317            &[
1318                0x3F, 0x01, 0x40, 0x02, 0xFC, 0x08, 0x00, 0x03, 0xFC, 0x0A, 0x01, 0x02, 0xFC, 0x0B,
1319                0x04, 0x0B,
1320            ],
1321            402,
1322        )
1323        .unwrap();
1324        assert_eq!(
1325            instrs,
1326            vec![
1327                Instr::MemorySize(MemIdx(1)),
1328                Instr::MemoryGrow(MemIdx(2)),
1329                Instr::MemoryInit(crate::types::DataIdx(0), MemIdx(3)),
1330                Instr::MemoryCopy {
1331                    dst: MemIdx(1),
1332                    src: MemIdx(2),
1333                },
1334                Instr::MemoryFill(MemIdx(4)),
1335                Instr::End,
1336            ]
1337        );
1338    }
1339
1340    #[test]
1341    fn decode_nonzero_memargs() {
1342        let instrs = decode_instr_sequence(
1343            &[
1344                0x28, 0x42, 0x01, 0x05, 0x36, 0x42, 0x02, 0x00, 0xFD, 0x00, 0x44, 0x03, 0x00, 0xFD,
1345                0x54, 0x40, 0x04, 0x01, 0x07, 0x0B,
1346            ],
1347            427,
1348        )
1349        .unwrap();
1350        assert_eq!(
1351            instrs,
1352            vec![
1353                Instr::I32Load(MemArg {
1354                    align: 2,
1355                    offset: 5,
1356                    memory: MemIdx(1),
1357                }),
1358                Instr::I32Store(MemArg {
1359                    align: 2,
1360                    offset: 0,
1361                    memory: MemIdx(2),
1362                }),
1363                Instr::V128Load(MemArg {
1364                    align: 4,
1365                    offset: 0,
1366                    memory: MemIdx(3),
1367                }),
1368                Instr::V128Load8Lane {
1369                    memarg: MemArg {
1370                        align: 0,
1371                        offset: 1,
1372                        memory: MemIdx(4),
1373                    },
1374                    lane: 7,
1375                },
1376                Instr::End,
1377            ]
1378        );
1379    }
1380
1381    #[test]
1382    fn decode_vector_memory_ops() {
1383        let instrs = decode_instr_sequence(
1384            &[
1385                0xFD, 0x00, 0x04, 0x00, 0xFD, 0x0B, 0x04, 0x00, 0xFD, 0x54, 0x00, 0x00, 0x0F, 0xFD,
1386                0x58, 0x00, 0x00, 0x0F, 0xFD, 0x5C, 0x02, 0x00, 0xFD, 0x0C, 0x00, 0x01, 0x02, 0x03,
1387                0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x0B,
1388            ],
1389            320,
1390        )
1391        .unwrap();
1392        assert_eq!(
1393            instrs,
1394            vec![
1395                Instr::V128Load(MemArg {
1396                    align: 4,
1397                    offset: 0,
1398                    memory: MemIdx(0),
1399                }),
1400                Instr::V128Store(MemArg {
1401                    align: 4,
1402                    offset: 0,
1403                    memory: MemIdx(0),
1404                }),
1405                Instr::V128Load8Lane {
1406                    memarg: MemArg {
1407                        align: 0,
1408                        offset: 0,
1409                        memory: MemIdx(0),
1410                    },
1411                    lane: 15,
1412                },
1413                Instr::V128Store8Lane {
1414                    memarg: MemArg {
1415                        align: 0,
1416                        offset: 0,
1417                        memory: MemIdx(0),
1418                    },
1419                    lane: 15,
1420                },
1421                Instr::V128Load32Zero(MemArg {
1422                    align: 2,
1423                    offset: 0,
1424                    memory: MemIdx(0),
1425                }),
1426                Instr::V128Const([
1427                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
1428                    0x0D, 0x0E, 0x0F,
1429                ]),
1430                Instr::End,
1431            ]
1432        );
1433    }
1434
1435    #[test]
1436    fn reject_unknown_simd_opcode() {
1437        let err = decode_instr_sequence(&[0xFD, 0xC8, 0x01, 0x0B], 410).unwrap_err();
1438        assert_eq!(err.kind, DecodeErrorKind::UnknownSimdOpcode { opcode: 200 });
1439    }
1440
1441    #[test]
1442    fn decode_value_block_type() {
1443        let instrs = decode_instr_sequence(&[0x02, 0x7F, 0x41, 0x01, 0x0B, 0x0B], 300).unwrap();
1444        assert_eq!(
1445            instrs[0],
1446            Instr::Block(BlockType::Val(ValType::Num(NumType::I32)))
1447        );
1448    }
1449
1450    #[test]
1451    fn decode_instruction_offsets() {
1452        let body = CodeBody {
1453            locals: Vec::new(),
1454            body: &[0x20, 0x00, 0x20, 0x01, 0x6A, 0x0B],
1455            body_offset: 100,
1456        };
1457
1458        let instrs = body.instructions_with_offsets().unwrap();
1459        assert_eq!(instrs[0].offset, ByteOffset(100));
1460        assert_eq!(instrs[1].offset, ByteOffset(102));
1461        assert_eq!(instrs[2].offset, ByteOffset(104));
1462        assert_eq!(instrs[3].offset, ByteOffset(105));
1463    }
1464
1465    #[test]
1466    fn reject_unknown_opcode() {
1467        let err = decode_instr_sequence(&[0xFF, 0x0B], 400).unwrap_err();
1468        assert_eq!(err.kind, DecodeErrorKind::UnknownOpcode { byte: 0xFF });
1469    }
1470
1471    #[test]
1472    fn reject_unterminated_sequence() {
1473        let err = decode_instr_sequence(&[0x20, 0x00], 500).unwrap_err();
1474        assert_eq!(err.kind, DecodeErrorKind::UnexpectedEof);
1475    }
1476}