wasm/validation/
code.rs

1use alloc::collections::btree_set::BTreeSet;
2use alloc::vec::Vec;
3use core::iter;
4
5use crate::core::indices::{
6    DataIdx, ElemIdx, FuncIdx, GlobalIdx, LabelIdx, LocalIdx, MemIdx, TableIdx, TypeIdx,
7};
8use crate::core::reader::section_header::{SectionHeader, SectionTy};
9use crate::core::reader::span::Span;
10use crate::core::reader::types::element::ElemType;
11use crate::core::reader::types::global::Global;
12use crate::core::reader::types::memarg::MemArg;
13use crate::core::reader::types::{BlockType, FuncType, MemType, NumType, TableType, ValType};
14use crate::core::reader::{WasmReadable, WasmReader};
15use crate::core::sidetable::{Sidetable, SidetableEntry};
16use crate::validation_stack::{LabelInfo, ValidationStack};
17use crate::{RefType, ValidationError};
18
19#[allow(clippy::too_many_arguments)]
20pub fn validate_code_section(
21    wasm: &mut WasmReader,
22    section_header: SectionHeader,
23    fn_types: &[FuncType],
24    type_idx_of_fn: &[usize],
25    num_imported_funcs: usize,
26    globals: &[Global],
27    memories: &[MemType],
28    data_count: &Option<u32>,
29    tables: &[TableType],
30    elements: &[ElemType],
31    validation_context_refs: &BTreeSet<FuncIdx>,
32    sidetable: &mut Sidetable,
33) -> Result<Vec<(Span, usize)>, ValidationError> {
34    assert_eq!(section_header.ty, SectionTy::Code);
35    let code_block_spans_stps = wasm.read_vec_enumerated(|wasm, idx| {
36        // We need to offset the index by the number of functions that were
37        // imported. Imported functions always live at the start of the index
38        // space.
39        let ty_idx = *type_idx_of_fn
40            .get(idx + num_imported_funcs)
41            .ok_or(ValidationError::FunctionAndCodeSectionsHaveDifferentLengths)?;
42        let func_ty = fn_types[ty_idx].clone();
43
44        let func_size = wasm.read_var_u32()?;
45        let func_block = wasm.make_span(func_size as usize)?;
46        let previous_pc = wasm.pc;
47
48        let locals = {
49            let params = func_ty.params.valtypes.iter().cloned();
50            let declared_locals = read_declared_locals(wasm)?;
51            params.chain(declared_locals).collect::<Vec<ValType>>()
52        };
53
54        let mut stack = ValidationStack::new_for_func(func_ty);
55        let stp = sidetable.len();
56
57        read_instructions(
58            wasm,
59            &mut stack,
60            sidetable,
61            &locals,
62            globals,
63            fn_types,
64            type_idx_of_fn,
65            memories,
66            data_count,
67            tables,
68            elements,
69            validation_context_refs,
70        )?;
71
72        // Check if there were unread trailing instructions after the last END
73        if previous_pc + func_size as usize != wasm.pc {
74            return Err(ValidationError::ExprHasTrailingInstructions);
75        }
76
77        Ok((func_block, stp))
78    })?;
79
80    trace!(
81        "Read code section. Found {} code blocks",
82        code_block_spans_stps.len()
83    );
84
85    Ok(code_block_spans_stps)
86}
87
88pub fn read_declared_locals(wasm: &mut WasmReader) -> Result<Vec<ValType>, ValidationError> {
89    let locals = wasm.read_vec(|wasm| {
90        let n = wasm.read_var_u32()? as usize;
91        let valtype = ValType::read(wasm)?;
92
93        Ok((n, valtype))
94    })?;
95
96    // these checks are related to the official test suite binary.wast file, the first 2 assert_malformed's starting at line 350
97    // we check to not have more than 2^32-1 locals, and if that number is okay, we then get to instantiate them all
98    // this is because the flat_map and collect take an insane amount of time
99    // in total, these 2 tests take more than 240s
100    let mut total_no_of_locals: usize = 0;
101    for local in &locals {
102        let temp = local.0;
103        if temp > i32::MAX as usize {
104            return Err(ValidationError::TooManyLocals(total_no_of_locals));
105        };
106        total_no_of_locals = match total_no_of_locals.checked_add(temp) {
107            None => return Err(ValidationError::TooManyLocals(total_no_of_locals)),
108            Some(n) => n,
109        }
110    }
111
112    if total_no_of_locals > i32::MAX as usize {
113        return Err(ValidationError::TooManyLocals(total_no_of_locals));
114    }
115
116    // Flatten local types for easier representation where n > 1
117    let locals = locals
118        .into_iter()
119        .flat_map(|entry| iter::repeat(entry.1).take(entry.0))
120        .collect::<Vec<ValType>>();
121
122    Ok(locals)
123}
124
125/// Validates a specific branch to some label by its index `label_idx`.
126/// Branches are generated by branching instructions and some can even generate multiple branches.
127fn validate_branch_and_generate_sidetable_entry(
128    wasm: &WasmReader,
129    label_idx: usize,
130    stack: &mut ValidationStack,
131    sidetable: &mut Sidetable,
132    unify_to_expected_types: bool,
133) -> Result<(), ValidationError> {
134    stack.assert_val_types_of_label_jump_types_on_top(label_idx, unify_to_expected_types)?;
135
136    // Get stack length before we mutably borrow the label's `CtrlStackEntry`.
137    let stack_len = stack.len();
138
139    let index_of_label_in_ctrl_stack = stack
140        .ctrl_stack
141        .len()
142        .checked_sub(label_idx)
143        .and_then(|i| i.checked_sub(1));
144
145    let targeted_ctrl_block_entry = index_of_label_in_ctrl_stack
146        .and_then(|idx| stack.ctrl_stack.get_mut(idx))
147        .ok_or(ValidationError::InvalidLabelIdx(label_idx))?;
148
149    let valcnt = targeted_ctrl_block_entry.label_types().len();
150    let popcnt = stack_len - targeted_ctrl_block_entry.height - valcnt;
151
152    // Now we generate the actual sidetable entry.
153    //
154    // This entry needs to be backpatched later, except for when it targets a label generated
155    // by a `loop` instruction. This is because loops are the only instruction able to perform
156    // backwards jumps, so we already know the exact instruction and sidetable pointers for that label.
157    // For all other types of labels (blocks, ifs, functions) we generate a partial sidetable entry.
158    //
159    // This partial sidetable entry abuses the `delta_pc` and `delta_stp` fields to temporarily store
160    // the current program counter and side table pointer until we reach the `end` of this label's scope,
161    // allowing us to perform the actual backpatching.
162    let stp_here = sidetable.len();
163    sidetable.push(SidetableEntry {
164        delta_pc: wasm.pc as isize,
165        delta_stp: stp_here as isize,
166        popcnt,
167        valcnt,
168    });
169
170    match &mut targeted_ctrl_block_entry.label_info {
171        LabelInfo::Block { stps_to_backpatch } => stps_to_backpatch.push(stp_here),
172        LabelInfo::Loop { ip, stp } => {
173            //we already know where to jump to for loops
174            sidetable[stp_here].delta_pc = *ip as isize - wasm.pc as isize;
175            sidetable[stp_here].delta_stp = *stp as isize - stp_here as isize;
176        }
177        LabelInfo::If {
178            stps_to_backpatch, ..
179        } => stps_to_backpatch.push(stp_here),
180        LabelInfo::Func { stps_to_backpatch } => stps_to_backpatch.push(stp_here),
181        LabelInfo::Untyped => {
182            unreachable!("this label is for untyped wasm sequences")
183        }
184    }
185    Ok(())
186}
187
188#[allow(clippy::too_many_arguments)]
189fn read_instructions(
190    wasm: &mut WasmReader,
191    stack: &mut ValidationStack,
192    sidetable: &mut Sidetable,
193    locals: &[ValType],
194    globals: &[Global],
195    fn_types: &[FuncType],
196    type_idx_of_fn: &[usize],
197    memories: &[MemType],
198    data_count: &Option<u32>,
199    tables: &[TableType],
200    elements: &[ElemType],
201    validation_context_refs: &BTreeSet<FuncIdx>,
202) -> Result<(), ValidationError> {
203    loop {
204        let Ok(first_instr_byte) = wasm.read_u8() else {
205            // TODO only do this if EOF
206            return Err(ValidationError::ExprMissingEnd);
207        };
208
209        #[cfg(debug_assertions)]
210        crate::core::utils::print_beautiful_instruction_name_1_byte(first_instr_byte, wasm.pc);
211
212        #[cfg(not(debug_assertions))]
213        trace!("Read instruction byte {first_instr_byte:#04X?} ({first_instr_byte}) at wasm_binary[{}]", wasm.pc);
214
215        use crate::core::reader::types::opcode::*;
216        match first_instr_byte {
217            // nop: [] -> []
218            NOP => {}
219            // block: [] -> [t*2]
220            BLOCK => {
221                let block_ty = BlockType::read(wasm)?.as_func_type(fn_types)?;
222                let label_info = LabelInfo::Block {
223                    stps_to_backpatch: Vec::new(),
224                };
225                // The types are explicitly popped and pushed in
226                // https://webassembly.github.io/spec/core/appendix/algorithm.html
227                // therefore types on the stack might change.
228                stack.assert_push_ctrl(label_info, block_ty, true)?;
229            }
230            LOOP => {
231                let block_ty = BlockType::read(wasm)?.as_func_type(fn_types)?;
232                let label_info = LabelInfo::Loop {
233                    ip: wasm.pc,
234                    stp: sidetable.len(),
235                };
236                // The types are explicitly popped and pushed in
237                // https://webassembly.github.io/spec/core/appendix/algorithm.html
238                // therefore types on the stack might change.
239                stack.assert_push_ctrl(label_info, block_ty, true)?;
240            }
241            IF => {
242                let block_ty = BlockType::read(wasm)?.as_func_type(fn_types)?;
243
244                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
245
246                let stp_here = sidetable.len();
247                sidetable.push(SidetableEntry {
248                    delta_pc: wasm.pc as isize,
249                    delta_stp: stp_here as isize,
250                    popcnt: 0,
251                    valcnt: block_ty.params.valtypes.len(),
252                });
253
254                let label_info = LabelInfo::If {
255                    stp: stp_here,
256                    stps_to_backpatch: Vec::new(),
257                };
258                // The types are explicitly popped and pushed in
259                // https://webassembly.github.io/spec/core/appendix/algorithm.html
260                // therefore types on the stack might change.
261                stack.assert_push_ctrl(label_info, block_ty, true)?;
262            }
263            ELSE => {
264                let (mut label_info, block_ty) = stack.assert_pop_ctrl(true)?;
265                if let LabelInfo::If {
266                    stp,
267                    stps_to_backpatch,
268                } = &mut label_info
269                {
270                    if *stp == usize::MAX {
271                        //this If was previously matched with an else already, it is already backpatched!
272                        return Err(ValidationError::ElseWithoutMatchingIf);
273                    }
274                    let stp_here = sidetable.len();
275                    sidetable.push(SidetableEntry {
276                        delta_pc: wasm.pc as isize,
277                        delta_stp: stp_here as isize,
278                        popcnt: 0,
279                        valcnt: block_ty.returns.valtypes.len(),
280                    });
281                    stps_to_backpatch.push(stp_here);
282
283                    sidetable[*stp].delta_pc = wasm.pc as isize - sidetable[*stp].delta_pc;
284                    sidetable[*stp].delta_stp =
285                        sidetable.len() as isize - sidetable[*stp].delta_stp;
286
287                    *stp = usize::MAX; // mark this If as backpatched
288
289                    for valtype in block_ty.returns.valtypes.iter().rev() {
290                        stack.assert_pop_val_type(*valtype)?;
291                    }
292
293                    for valtype in block_ty.params.valtypes.iter() {
294                        stack.push_valtype(*valtype);
295                    }
296                    // The types are explicitly popped and pushed in
297                    // https://webassembly.github.io/spec/core/appendix/algorithm.html
298                    // therefore types on the stack might change.
299                    stack.assert_push_ctrl(label_info, block_ty, true)?;
300                } else {
301                    return Err(ValidationError::ElseWithoutMatchingIf);
302                }
303            }
304            BR => {
305                let label_idx = wasm.read_var_u32()? as LabelIdx;
306                validate_branch_and_generate_sidetable_entry(
307                    wasm, label_idx, stack, sidetable, false,
308                )?;
309                stack.make_unspecified()?;
310            }
311            BR_IF => {
312                let label_idx = wasm.read_var_u32()? as LabelIdx;
313                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
314                // The types are explicitly popped and pushed in
315                // https://webassembly.github.io/spec/core/appendix/algorithm.html
316                // therefore types on the stack might change.
317                validate_branch_and_generate_sidetable_entry(
318                    wasm, label_idx, stack, sidetable, true,
319                )?;
320            }
321            BR_TABLE => {
322                let label_vec = wasm.read_vec(|wasm| wasm.read_var_u32().map(|v| v as LabelIdx))?;
323                let max_label_idx = wasm.read_var_u32()? as LabelIdx;
324                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
325                for label_idx in &label_vec {
326                    validate_branch_and_generate_sidetable_entry(
327                        wasm, *label_idx, stack, sidetable, false,
328                    )?;
329                }
330
331                validate_branch_and_generate_sidetable_entry(
332                    wasm,
333                    max_label_idx,
334                    stack,
335                    sidetable,
336                    false,
337                )?;
338
339                // The label arity of the branches must be explicitly checked against each other further
340                // if their arities are the same, then they must unify, as they unify against the stack variables already
341                // If the following check is not made, the algorithm incorrectly unifies label types with different arities
342                // in which the smaller arity type is a suffix in the label type list of the larger arity function
343
344                // stack includes all labels, that check is made in the above fn already
345                let max_label_arity = stack
346                    .ctrl_stack
347                    .get(stack.ctrl_stack.len() - max_label_idx - 1)
348                    .unwrap()
349                    .label_types()
350                    .len();
351                for label_idx in &label_vec {
352                    let label_arity = stack
353                        .ctrl_stack
354                        .get(stack.ctrl_stack.len() - *label_idx - 1)
355                        .unwrap()
356                        .label_types()
357                        .len();
358                    if max_label_arity != label_arity {
359                        return Err(ValidationError::InvalidLabelIdx(*label_idx));
360                    }
361                }
362
363                stack.make_unspecified()?;
364            }
365            END => {
366                // The types are explicitly popped and pushed in
367                // https://webassembly.github.io/spec/core/appendix/algorithm.html
368                // therefore types on the stack might change.
369                let (label_info, block_ty) = stack.assert_pop_ctrl(true)?;
370                let stp_here = sidetable.len();
371
372                match label_info {
373                    LabelInfo::Block { stps_to_backpatch } => {
374                        stps_to_backpatch.iter().for_each(|i| {
375                            sidetable[*i].delta_pc = (wasm.pc as isize) - sidetable[*i].delta_pc;
376                            sidetable[*i].delta_stp = (stp_here as isize) - sidetable[*i].delta_stp;
377                        });
378                    }
379                    LabelInfo::If {
380                        stp,
381                        stps_to_backpatch,
382                    } => {
383                        if stp != usize::MAX {
384                            //This If is still not backpatched, meaning it does not have a corresponding
385                            //ELSE. This is only allowed when the corresponding If block has the same input
386                            //types as its output types (an untyped ELSE block with no instruction is valid
387                            //if and only if it is of this type)
388                            if !(block_ty.params == block_ty.returns) {
389                                return Err(ValidationError::IfWithoutMatchingElse);
390                            }
391
392                            //This If is still not backpatched, meaning it does not have a corresponding
393                            //ELSE. Therefore if its condition fails, it jumps after END.
394                            sidetable[stp].delta_pc = (wasm.pc as isize) - sidetable[stp].delta_pc;
395                            sidetable[stp].delta_stp =
396                                (stp_here as isize) - sidetable[stp].delta_stp;
397                        }
398                        stps_to_backpatch.iter().for_each(|i| {
399                            sidetable[*i].delta_pc = (wasm.pc as isize) - sidetable[*i].delta_pc;
400                            sidetable[*i].delta_stp = (stp_here as isize) - sidetable[*i].delta_stp;
401                        });
402                    }
403                    LabelInfo::Loop { .. } => (),
404                    LabelInfo::Func { stps_to_backpatch } => {
405                        // same as blocks, except jump just before the end instr, not after it
406                        // the last end instruction will handle the return to callee during execution
407                        stps_to_backpatch.iter().for_each(|i| {
408                            sidetable[*i].delta_pc =
409                                (wasm.pc as isize) - sidetable[*i].delta_pc - 1; // minus 1 is important! TODO: Why?
410                            sidetable[*i].delta_stp = (stp_here as isize) - sidetable[*i].delta_stp;
411                        });
412                    }
413                    LabelInfo::Untyped => unreachable!("this label is for untyped wasm sequences"),
414                }
415
416                if stack.ctrl_stack.is_empty() {
417                    return Ok(());
418                }
419            }
420            RETURN => {
421                let label_idx = stack.ctrl_stack.len() - 1; // return behaves the same as br <most_outer>
422                validate_branch_and_generate_sidetable_entry(
423                    wasm, label_idx, stack, sidetable, false,
424                )?;
425                stack.make_unspecified()?;
426            }
427            // call [t1*] -> [t2*]
428            CALL => {
429                let func_idx = wasm.read_var_u32()? as FuncIdx;
430                let type_idx = *type_idx_of_fn
431                    .get(func_idx)
432                    .ok_or(ValidationError::FunctionIsNotDefined(func_idx))?;
433                let func_ty = &fn_types[type_idx];
434
435                for typ in func_ty.params.valtypes.iter().rev() {
436                    stack.assert_pop_val_type(*typ)?;
437                }
438
439                for typ in func_ty.returns.valtypes.iter() {
440                    stack.push_valtype(*typ);
441                }
442            }
443            CALL_INDIRECT => {
444                let type_idx = wasm.read_var_u32()? as TypeIdx;
445
446                let table_idx = wasm.read_var_u32()? as TableIdx;
447
448                let tab = tables
449                    .get(table_idx)
450                    .ok_or(ValidationError::TableIsNotDefined(table_idx))?;
451
452                if tab.et != RefType::FuncRef {
453                    return Err(ValidationError::WrongRefTypeForInteropValue(
454                        tab.et,
455                        RefType::FuncRef,
456                    ));
457                }
458
459                let func_ty = fn_types
460                    .get(type_idx)
461                    .ok_or(ValidationError::InvalidTypeIdx(type_idx))?;
462
463                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
464
465                for typ in func_ty.params.valtypes.iter().rev() {
466                    stack.assert_pop_val_type(*typ)?;
467                }
468
469                for typ in func_ty.returns.valtypes.iter() {
470                    stack.push_valtype(*typ);
471                }
472            }
473            // unreachable: [t1*] -> [t2*]
474            UNREACHABLE => {
475                stack.make_unspecified()?;
476            }
477            DROP => {
478                stack.drop_val()?;
479            }
480            SELECT => {
481                stack.validate_polymorphic_select()?;
482            }
483            SELECT_T => {
484                let type_vec = wasm.read_vec(ValType::read)?;
485                if type_vec.len() != 1 {
486                    return Err(ValidationError::InvalidSelectTypeVector);
487                }
488                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
489                stack.assert_pop_val_type(type_vec[0])?;
490                stack.assert_pop_val_type(type_vec[0])?;
491                stack.push_valtype(type_vec[0]);
492            }
493            // local.get: [] -> [t]
494            LOCAL_GET => {
495                let local_idx = wasm.read_var_u32()? as LocalIdx;
496                let local_ty = locals
497                    .get(local_idx)
498                    .ok_or(ValidationError::InvalidLocalIdx)?;
499                stack.push_valtype(*local_ty);
500            }
501            // local.set [t] -> []
502            LOCAL_SET => {
503                let local_idx = wasm.read_var_u32()? as LocalIdx;
504                let local_ty = locals
505                    .get(local_idx)
506                    .ok_or(ValidationError::InvalidLocalIdx)?;
507                stack.assert_pop_val_type(*local_ty)?;
508            }
509            // local.set [t] -> [t]
510            LOCAL_TEE => {
511                let local_idx = wasm.read_var_u32()? as LocalIdx;
512                let local_ty = locals
513                    .get(local_idx)
514                    .ok_or(ValidationError::InvalidLocalIdx)?;
515                stack.assert_val_types_on_top(&[*local_ty], true)?;
516            }
517            // global.get [] -> [t]
518            GLOBAL_GET => {
519                let global_idx = wasm.read_var_u32()? as GlobalIdx;
520                let global = globals
521                    .get(global_idx)
522                    .ok_or(ValidationError::InvalidGlobalIdx(global_idx))?;
523
524                stack.push_valtype(global.ty.ty);
525                trace!(
526                    "Instruction: global.get '{}' [] -> [{:?}]",
527                    global_idx,
528                    // global,
529                    global.ty.ty
530                );
531            }
532            // global.set [t] -> []
533            GLOBAL_SET => {
534                let global_idx = wasm.read_var_u32()? as GlobalIdx;
535                let global = globals
536                    .get(global_idx)
537                    .ok_or(ValidationError::InvalidGlobalIdx(global_idx))?;
538
539                if !global.ty.is_mut {
540                    return Err(ValidationError::GlobalIsConst);
541                }
542
543                stack.assert_pop_val_type(global.ty.ty)?;
544            }
545            TABLE_GET => {
546                let table_idx = wasm.read_var_u32()? as TableIdx;
547
548                if tables.len() <= table_idx {
549                    return Err(ValidationError::TableIsNotDefined(table_idx));
550                }
551
552                let t = tables.get(table_idx).unwrap().et;
553
554                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
555                stack.push_valtype(ValType::RefType(t));
556            }
557            TABLE_SET => {
558                let table_idx = wasm.read_var_u32()? as TableIdx;
559
560                if tables.len() <= table_idx {
561                    return Err(ValidationError::TableIsNotDefined(table_idx));
562                }
563
564                let t = tables.get(table_idx).unwrap().et;
565
566                stack.assert_pop_ref_type(Some(t))?;
567                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
568            }
569            I32_LOAD => {
570                if memories.is_empty() {
571                    return Err(ValidationError::MemoryIsNotDefined(0));
572                }
573                let memarg = MemArg::read(wasm)?;
574                if memarg.align > 2 {
575                    return Err(ValidationError::ErroneousAlignment(memarg.align, 2));
576                }
577                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
578                stack.push_valtype(ValType::NumType(NumType::I32));
579            }
580            I64_LOAD => {
581                if memories.is_empty() {
582                    return Err(ValidationError::MemoryIsNotDefined(0));
583                }
584                let memarg = MemArg::read(wasm)?;
585                if memarg.align > 3 {
586                    return Err(ValidationError::ErroneousAlignment(memarg.align, 3));
587                }
588                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
589                stack.push_valtype(ValType::NumType(NumType::I64));
590            }
591            F32_LOAD => {
592                if memories.is_empty() {
593                    return Err(ValidationError::MemoryIsNotDefined(0));
594                }
595                let memarg = MemArg::read(wasm)?;
596                if memarg.align > 2 {
597                    return Err(ValidationError::ErroneousAlignment(memarg.align, 2));
598                }
599                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
600                stack.push_valtype(ValType::NumType(NumType::F32));
601            }
602            F64_LOAD => {
603                if memories.is_empty() {
604                    return Err(ValidationError::MemoryIsNotDefined(0));
605                }
606                let memarg = MemArg::read(wasm)?;
607                if memarg.align > 3 {
608                    return Err(ValidationError::ErroneousAlignment(memarg.align, 3));
609                }
610                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
611                stack.push_valtype(ValType::NumType(NumType::F64));
612            }
613            I32_LOAD8_S => {
614                if memories.is_empty() {
615                    return Err(ValidationError::MemoryIsNotDefined(0));
616                }
617                let memarg = MemArg::read(wasm)?;
618                if memarg.align > 0 {
619                    return Err(ValidationError::ErroneousAlignment(memarg.align, 0));
620                }
621                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
622                stack.push_valtype(ValType::NumType(NumType::I32));
623            }
624            I32_LOAD8_U => {
625                if memories.is_empty() {
626                    return Err(ValidationError::MemoryIsNotDefined(0));
627                }
628                let memarg = MemArg::read(wasm)?;
629                if memarg.align > 0 {
630                    return Err(ValidationError::ErroneousAlignment(memarg.align, 0));
631                }
632                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
633                stack.push_valtype(ValType::NumType(NumType::I32));
634            }
635            I32_LOAD16_S => {
636                if memories.is_empty() {
637                    return Err(ValidationError::MemoryIsNotDefined(0));
638                }
639                let memarg = MemArg::read(wasm)?;
640                if memarg.align > 1 {
641                    return Err(ValidationError::ErroneousAlignment(memarg.align, 1));
642                }
643                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
644                stack.push_valtype(ValType::NumType(NumType::I32));
645            }
646            I32_LOAD16_U => {
647                if memories.is_empty() {
648                    return Err(ValidationError::MemoryIsNotDefined(0));
649                }
650                let memarg = MemArg::read(wasm)?;
651                if memarg.align > 1 {
652                    return Err(ValidationError::ErroneousAlignment(memarg.align, 1));
653                }
654                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
655                stack.push_valtype(ValType::NumType(NumType::I32));
656            }
657            I64_LOAD8_S => {
658                if memories.is_empty() {
659                    return Err(ValidationError::MemoryIsNotDefined(0));
660                }
661                let memarg = MemArg::read(wasm)?;
662                if memarg.align > 0 {
663                    return Err(ValidationError::ErroneousAlignment(memarg.align, 0));
664                }
665                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
666                stack.push_valtype(ValType::NumType(NumType::I64));
667            }
668            I64_LOAD8_U => {
669                if memories.is_empty() {
670                    return Err(ValidationError::MemoryIsNotDefined(0));
671                }
672                let memarg = MemArg::read(wasm)?;
673                if memarg.align > 0 {
674                    return Err(ValidationError::ErroneousAlignment(memarg.align, 0));
675                }
676                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
677                stack.push_valtype(ValType::NumType(NumType::I64));
678            }
679            I64_LOAD16_S => {
680                if memories.is_empty() {
681                    return Err(ValidationError::MemoryIsNotDefined(0));
682                }
683                let memarg = MemArg::read(wasm)?;
684                if memarg.align > 1 {
685                    return Err(ValidationError::ErroneousAlignment(memarg.align, 1));
686                }
687                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
688                stack.push_valtype(ValType::NumType(NumType::I64));
689            }
690            I64_LOAD16_U => {
691                if memories.is_empty() {
692                    return Err(ValidationError::MemoryIsNotDefined(0));
693                }
694                let memarg = MemArg::read(wasm)?;
695                if memarg.align > 1 {
696                    return Err(ValidationError::ErroneousAlignment(memarg.align, 1));
697                }
698                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
699                stack.push_valtype(ValType::NumType(NumType::I64));
700            }
701            I64_LOAD32_S => {
702                if memories.is_empty() {
703                    return Err(ValidationError::MemoryIsNotDefined(0));
704                }
705                let memarg = MemArg::read(wasm)?;
706                if memarg.align > 2 {
707                    return Err(ValidationError::ErroneousAlignment(memarg.align, 2));
708                }
709                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
710                stack.push_valtype(ValType::NumType(NumType::I64));
711            }
712            I64_LOAD32_U => {
713                if memories.is_empty() {
714                    return Err(ValidationError::MemoryIsNotDefined(0));
715                }
716                let memarg = MemArg::read(wasm)?;
717                if memarg.align > 2 {
718                    return Err(ValidationError::ErroneousAlignment(memarg.align, 2));
719                }
720                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
721                stack.push_valtype(ValType::NumType(NumType::I64));
722            }
723            I32_STORE => {
724                if memories.is_empty() {
725                    return Err(ValidationError::MemoryIsNotDefined(0));
726                }
727                let memarg = MemArg::read(wasm)?;
728                if memarg.align > 2 {
729                    return Err(ValidationError::ErroneousAlignment(memarg.align, 2));
730                }
731                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
732                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
733            }
734            I64_STORE => {
735                if memories.is_empty() {
736                    return Err(ValidationError::MemoryIsNotDefined(0));
737                }
738                let memarg = MemArg::read(wasm)?;
739                if memarg.align > 3 {
740                    return Err(ValidationError::ErroneousAlignment(memarg.align, 3));
741                }
742                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
743                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
744            }
745            F32_STORE => {
746                if memories.is_empty() {
747                    return Err(ValidationError::MemoryIsNotDefined(0));
748                }
749                let memarg = MemArg::read(wasm)?;
750                if memarg.align > 2 {
751                    return Err(ValidationError::ErroneousAlignment(memarg.align, 2));
752                }
753                stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
754                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
755            }
756            F64_STORE => {
757                if memories.is_empty() {
758                    return Err(ValidationError::MemoryIsNotDefined(0));
759                }
760                let memarg = MemArg::read(wasm)?;
761                if memarg.align > 3 {
762                    return Err(ValidationError::ErroneousAlignment(memarg.align, 3));
763                }
764                stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
765                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
766            }
767            I32_STORE8 => {
768                if memories.is_empty() {
769                    return Err(ValidationError::MemoryIsNotDefined(0));
770                }
771                let memarg = MemArg::read(wasm)?;
772                if memarg.align > 0 {
773                    return Err(ValidationError::ErroneousAlignment(memarg.align, 0));
774                }
775                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
776                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
777            }
778            I32_STORE16 => {
779                if memories.is_empty() {
780                    return Err(ValidationError::MemoryIsNotDefined(0));
781                }
782                let memarg = MemArg::read(wasm)?;
783                if memarg.align > 1 {
784                    return Err(ValidationError::ErroneousAlignment(memarg.align, 1));
785                }
786                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
787                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
788            }
789            I64_STORE8 => {
790                if memories.is_empty() {
791                    return Err(ValidationError::MemoryIsNotDefined(0));
792                }
793                let memarg = MemArg::read(wasm)?;
794                if memarg.align > 0 {
795                    return Err(ValidationError::ErroneousAlignment(memarg.align, 0));
796                }
797                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
798                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
799            }
800            I64_STORE16 => {
801                if memories.is_empty() {
802                    return Err(ValidationError::MemoryIsNotDefined(0));
803                }
804                let memarg = MemArg::read(wasm)?;
805                if memarg.align > 1 {
806                    return Err(ValidationError::ErroneousAlignment(memarg.align, 1));
807                }
808                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
809                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
810            }
811            I64_STORE32 => {
812                if memories.is_empty() {
813                    return Err(ValidationError::MemoryIsNotDefined(0));
814                }
815                let memarg = MemArg::read(wasm)?;
816                if memarg.align > 2 {
817                    return Err(ValidationError::ErroneousAlignment(memarg.align, 2));
818                }
819                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
820                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
821            }
822            MEMORY_SIZE => {
823                let mem_idx = wasm.read_u8()? as MemIdx;
824                if mem_idx != 0 {
825                    return Err(ValidationError::UnsupportedMultipleMemoriesProposal);
826                }
827                if memories.len() <= mem_idx {
828                    return Err(ValidationError::MemoryIsNotDefined(mem_idx));
829                }
830                stack.push_valtype(ValType::NumType(NumType::I32));
831            }
832            MEMORY_GROW => {
833                let mem_idx = wasm.read_u8()? as MemIdx;
834                if mem_idx != 0 {
835                    return Err(ValidationError::UnsupportedMultipleMemoriesProposal);
836                }
837                if memories.len() <= mem_idx {
838                    return Err(ValidationError::MemoryIsNotDefined(mem_idx));
839                }
840                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
841                stack.push_valtype(ValType::NumType(NumType::I32));
842            }
843            // i32.const: [] -> [i32]
844            I32_CONST => {
845                let _num = wasm.read_var_i32()?;
846                stack.push_valtype(ValType::NumType(NumType::I32));
847            }
848            I64_CONST => {
849                let _num = wasm.read_var_i64()?;
850                stack.push_valtype(ValType::NumType(NumType::I64));
851            }
852            F32_CONST => {
853                let _num = wasm.read_var_f32()?;
854                stack.push_valtype(ValType::NumType(NumType::F32));
855            }
856            F64_CONST => {
857                let _num = wasm.read_var_f64()?;
858                stack.push_valtype(ValType::NumType(NumType::F64));
859            }
860            I32_EQZ => {
861                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
862
863                stack.push_valtype(ValType::NumType(NumType::I32));
864            }
865            I32_EQ | I32_NE | I32_LT_S | I32_LT_U | I32_GT_S | I32_GT_U | I32_LE_S | I32_LE_U
866            | I32_GE_S | I32_GE_U => {
867                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
868                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
869
870                stack.push_valtype(ValType::NumType(NumType::I32));
871            }
872            I64_EQZ => {
873                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
874
875                stack.push_valtype(ValType::NumType(NumType::I32));
876            }
877            I64_EQ | I64_NE | I64_LT_S | I64_LT_U | I64_GT_S | I64_GT_U | I64_LE_S | I64_LE_U
878            | I64_GE_S | I64_GE_U => {
879                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
880                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
881
882                stack.push_valtype(ValType::NumType(NumType::I32));
883            }
884            F32_EQ | F32_NE | F32_LT | F32_GT | F32_LE | F32_GE => {
885                stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
886                stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
887
888                stack.push_valtype(ValType::NumType(NumType::I32));
889            }
890            F64_EQ | F64_NE | F64_LT | F64_GT | F64_LE | F64_GE => {
891                stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
892                stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
893
894                stack.push_valtype(ValType::NumType(NumType::I32));
895            }
896            F32_ABS | F32_NEG | F32_CEIL | F32_FLOOR | F32_TRUNC | F32_NEAREST | F32_SQRT => {
897                stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
898
899                stack.push_valtype(ValType::NumType(NumType::F32));
900            }
901            F32_ADD | F32_SUB | F32_MUL | F32_DIV | F32_MIN | F32_MAX | F32_COPYSIGN => {
902                stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
903                stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
904
905                stack.push_valtype(ValType::NumType(NumType::F32));
906            }
907            F64_ABS | F64_NEG | F64_CEIL | F64_FLOOR | F64_TRUNC | F64_NEAREST | F64_SQRT => {
908                stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
909
910                stack.push_valtype(ValType::NumType(NumType::F64));
911            }
912            F64_ADD | F64_SUB | F64_MUL | F64_DIV | F64_MIN | F64_MAX | F64_COPYSIGN => {
913                stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
914                stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
915
916                stack.push_valtype(ValType::NumType(NumType::F64));
917            }
918            I32_ADD | I32_SUB | I32_MUL | I32_DIV_S | I32_DIV_U | I32_REM_S => {
919                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
920                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
921
922                stack.push_valtype(ValType::NumType(NumType::I32));
923            }
924            // i32.clz: [i32] -> [i32]
925            I32_CLZ | I32_CTZ | I32_POPCNT => {
926                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
927
928                stack.push_valtype(ValType::NumType(NumType::I32));
929            }
930            I32_REM_U | I32_AND | I32_OR | I32_XOR | I32_SHL | I32_SHR_S | I32_SHR_U | I32_ROTL
931            | I32_ROTR => {
932                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
933                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
934
935                stack.push_valtype(ValType::NumType(NumType::I32));
936            }
937            I64_CLZ | I64_CTZ | I64_POPCNT => {
938                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
939
940                stack.push_valtype(ValType::NumType(NumType::I64));
941            }
942
943            I64_ADD | I64_SUB | I64_MUL | I64_DIV_S | I64_DIV_U | I64_REM_S | I64_REM_U
944            | I64_AND | I64_OR | I64_XOR | I64_SHL | I64_SHR_S | I64_SHR_U | I64_ROTL
945            | I64_ROTR => {
946                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
947                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
948
949                stack.push_valtype(ValType::NumType(NumType::I64));
950            }
951
952            I32_WRAP_I64 => {
953                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
954
955                stack.push_valtype(ValType::NumType(NumType::I32));
956            }
957
958            I32_TRUNC_F32_S | I32_TRUNC_F32_U | I32_REINTERPRET_F32 => {
959                stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
960
961                stack.push_valtype(ValType::NumType(NumType::I32));
962            }
963
964            I32_TRUNC_F64_S | I32_TRUNC_F64_U => {
965                stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
966
967                stack.push_valtype(ValType::NumType(NumType::I32));
968            }
969
970            I64_EXTEND_I32_S | I64_EXTEND_I32_U => {
971                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
972
973                stack.push_valtype(ValType::NumType(NumType::I64));
974            }
975
976            I64_TRUNC_F32_S | I64_TRUNC_F32_U => {
977                stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
978
979                stack.push_valtype(ValType::NumType(NumType::I64));
980            }
981
982            I64_TRUNC_F64_S | I64_TRUNC_F64_U | I64_REINTERPRET_F64 => {
983                stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
984
985                stack.push_valtype(ValType::NumType(NumType::I64));
986            }
987
988            F32_CONVERT_I32_S | F32_CONVERT_I32_U | F32_REINTERPRET_I32 => {
989                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
990
991                stack.push_valtype(ValType::NumType(NumType::F32));
992            }
993
994            F32_CONVERT_I64_S | F32_CONVERT_I64_U => {
995                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
996
997                stack.push_valtype(ValType::NumType(NumType::F32));
998            }
999
1000            F32_DEMOTE_F64 => {
1001                stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
1002
1003                stack.push_valtype(ValType::NumType(NumType::F32));
1004            }
1005
1006            F64_CONVERT_I32_S | F64_CONVERT_I32_U => {
1007                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1008
1009                stack.push_valtype(ValType::NumType(NumType::F64));
1010            }
1011
1012            F64_CONVERT_I64_S | F64_CONVERT_I64_U | F64_REINTERPRET_I64 => {
1013                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
1014
1015                stack.push_valtype(ValType::NumType(NumType::F64));
1016            }
1017
1018            F64_PROMOTE_F32 => {
1019                stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
1020
1021                stack.push_valtype(ValType::NumType(NumType::F64));
1022            }
1023
1024            REF_NULL => {
1025                let reftype = RefType::read(wasm)?;
1026                // at validation-time we don't really care if it's null or not
1027                stack.push_valtype(ValType::RefType(reftype));
1028            }
1029
1030            REF_IS_NULL => {
1031                stack.assert_pop_ref_type(None)?;
1032                stack.push_valtype(ValType::NumType(NumType::I32));
1033            }
1034
1035            REF_FUNC => {
1036                let func_idx = wasm.read_var_u32()? as FuncIdx;
1037
1038                // checking for existence suffices for checking whether this function has a valid type.
1039                if type_idx_of_fn.len() <= func_idx {
1040                    return Err(ValidationError::FunctionIsNotDefined(func_idx));
1041                }
1042
1043                // check whether func_idx is in C.refs
1044                // https://webassembly.github.io/spec/core/valid/conventions.html#context
1045                if !validation_context_refs.contains(&func_idx) {
1046                    return Err(ValidationError::ReferencingAnUnreferencedFunction(func_idx));
1047                }
1048
1049                stack.push_valtype(ValType::RefType(RefType::FuncRef));
1050            }
1051
1052            FC_EXTENSIONS => {
1053                let Ok(second_instr) = wasm.read_var_u32() else {
1054                    // TODO only do this if EOF
1055                    return Err(ValidationError::ExprMissingEnd);
1056                };
1057
1058                #[cfg(debug_assertions)]
1059                crate::core::utils::print_beautiful_fc_extension(second_instr, wasm.pc);
1060
1061                #[cfg(not(debug_assertions))]
1062                trace!(
1063                    "Read instruction byte {second_instr} at wasm_binary[{}]",
1064                    wasm.pc
1065                );
1066
1067                use crate::core::reader::types::opcode::fc_extensions::*;
1068                match second_instr {
1069                    I32_TRUNC_SAT_F32_S => {
1070                        stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
1071                        stack.push_valtype(ValType::NumType(NumType::I32));
1072                    }
1073                    I32_TRUNC_SAT_F32_U => {
1074                        stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
1075                        stack.push_valtype(ValType::NumType(NumType::I32));
1076                    }
1077                    I32_TRUNC_SAT_F64_S => {
1078                        stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
1079                        stack.push_valtype(ValType::NumType(NumType::I32));
1080                    }
1081                    I32_TRUNC_SAT_F64_U => {
1082                        stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
1083                        stack.push_valtype(ValType::NumType(NumType::I32));
1084                    }
1085                    I64_TRUNC_SAT_F32_S => {
1086                        stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
1087                        stack.push_valtype(ValType::NumType(NumType::I64));
1088                    }
1089                    I64_TRUNC_SAT_F32_U => {
1090                        stack.assert_pop_val_type(ValType::NumType(NumType::F32))?;
1091                        stack.push_valtype(ValType::NumType(NumType::I64));
1092                    }
1093                    I64_TRUNC_SAT_F64_S => {
1094                        stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
1095                        stack.push_valtype(ValType::NumType(NumType::I64));
1096                    }
1097                    I64_TRUNC_SAT_F64_U => {
1098                        stack.assert_pop_val_type(ValType::NumType(NumType::F64))?;
1099                        stack.push_valtype(ValType::NumType(NumType::I64));
1100                    }
1101                    MEMORY_INIT => {
1102                        let data_idx = wasm.read_var_u32()? as DataIdx;
1103                        let mem_idx = wasm.read_u8()? as MemIdx;
1104                        if mem_idx != 0 {
1105                            return Err(ValidationError::UnsupportedMultipleMemoriesProposal);
1106                        }
1107                        if memories.len() <= mem_idx {
1108                            return Err(ValidationError::MemoryIsNotDefined(mem_idx));
1109                        }
1110                        if data_count.is_none() {
1111                            return Err(ValidationError::NoDataSegments);
1112                        }
1113                        if data_count.unwrap() as usize <= data_idx {
1114                            return Err(ValidationError::DataSegmentNotFound(data_idx));
1115                        }
1116                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1117                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1118                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1119                    }
1120                    DATA_DROP => {
1121                        if data_count.is_none() {
1122                            return Err(ValidationError::NoDataSegments);
1123                        }
1124                        let data_idx = wasm.read_var_u32()? as DataIdx;
1125                        if data_count.unwrap() as usize <= data_idx {
1126                            return Err(ValidationError::DataSegmentNotFound(data_idx));
1127                        }
1128                    }
1129                    MEMORY_COPY => {
1130                        let (dst, src) = (wasm.read_u8()? as usize, wasm.read_u8()? as usize);
1131                        if dst != 0 || src != 0 {
1132                            return Err(ValidationError::UnsupportedMultipleMemoriesProposal);
1133                        }
1134                        if memories.is_empty() {
1135                            return Err(ValidationError::MemoryIsNotDefined(0));
1136                        }
1137                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1138                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1139                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1140                    }
1141                    MEMORY_FILL => {
1142                        let mem_idx = wasm.read_u8()? as MemIdx;
1143                        if mem_idx != 0 {
1144                            return Err(ValidationError::UnsupportedMultipleMemoriesProposal);
1145                        }
1146                        if memories.len() <= mem_idx {
1147                            return Err(ValidationError::MemoryIsNotDefined(mem_idx));
1148                        }
1149                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1150                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1151                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1152                    }
1153                    TABLE_INIT => {
1154                        let elem_idx = wasm.read_var_u32()? as ElemIdx;
1155                        let table_idx = wasm.read_var_u32()? as TableIdx;
1156
1157                        if tables.len() <= table_idx {
1158                            return Err(ValidationError::TableIsNotDefined(table_idx));
1159                        }
1160
1161                        let t1 = tables[table_idx].et;
1162
1163                        if elements.len() <= elem_idx {
1164                            return Err(ValidationError::ElementIsNotDefined(elem_idx));
1165                        }
1166
1167                        let t2 = elements[elem_idx].to_ref_type();
1168
1169                        if t1 != t2 {
1170                            return Err(ValidationError::DifferentRefTypes(t1, t2));
1171                        }
1172                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1173                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1174                        // INFO: wasmtime checks for this value to be an index in the tables array, interesting
1175                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1176                    }
1177                    ELEM_DROP => {
1178                        let elem_idx = wasm.read_var_u32()? as ElemIdx;
1179
1180                        if elements.len() <= elem_idx {
1181                            return Err(ValidationError::ElementIsNotDefined(elem_idx));
1182                        }
1183                    }
1184                    TABLE_COPY => {
1185                        let table_x_idx = wasm.read_var_u32()? as TableIdx;
1186                        let table_y_idx = wasm.read_var_u32()? as TableIdx;
1187
1188                        if tables.len() <= table_x_idx {
1189                            return Err(ValidationError::TableIsNotDefined(table_x_idx));
1190                        }
1191
1192                        if tables.len() <= table_y_idx {
1193                            return Err(ValidationError::TableIsNotDefined(table_y_idx));
1194                        }
1195
1196                        let t1 = tables[table_x_idx].et;
1197                        let t2 = tables[table_y_idx].et;
1198
1199                        if t1 != t2 {
1200                            return Err(ValidationError::DifferentRefTypes(t1, t2));
1201                        }
1202
1203                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1204                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1205                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1206                    }
1207                    TABLE_GROW => {
1208                        let table_idx = wasm.read_var_u32()? as TableIdx;
1209
1210                        if tables.len() <= table_idx {
1211                            return Err(ValidationError::TableIsNotDefined(table_idx));
1212                        }
1213
1214                        let t = tables[table_idx].et;
1215
1216                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1217                        stack.assert_pop_ref_type(Some(t))?;
1218
1219                        stack.push_valtype(ValType::NumType(NumType::I32));
1220                    }
1221                    TABLE_SIZE => {
1222                        let table_idx = wasm.read_var_u32()? as TableIdx;
1223
1224                        if tables.len() <= table_idx {
1225                            return Err(ValidationError::TableIsNotDefined(table_idx));
1226                        }
1227
1228                        stack.push_valtype(ValType::NumType(NumType::I32));
1229                    }
1230                    TABLE_FILL => {
1231                        let table_idx = wasm.read_var_u32()? as TableIdx;
1232
1233                        if tables.len() <= table_idx {
1234                            return Err(ValidationError::TableIsNotDefined(table_idx));
1235                        }
1236
1237                        let t = tables[table_idx].et;
1238
1239                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1240                        stack.assert_pop_ref_type(Some(t))?;
1241                        stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1242                    }
1243                    _ => {
1244                        return Err(ValidationError::InvalidMultiByteInstr(
1245                            first_instr_byte,
1246                            second_instr,
1247                        ))
1248                    }
1249                }
1250            }
1251
1252            I32_EXTEND8_S => {
1253                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1254
1255                stack.push_valtype(ValType::NumType(NumType::I32));
1256            }
1257            I32_EXTEND16_S => {
1258                stack.assert_pop_val_type(ValType::NumType(NumType::I32))?;
1259
1260                stack.push_valtype(ValType::NumType(NumType::I32));
1261            }
1262            I64_EXTEND8_S => {
1263                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
1264
1265                stack.push_valtype(ValType::NumType(NumType::I64));
1266            }
1267            I64_EXTEND16_S => {
1268                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
1269
1270                stack.push_valtype(ValType::NumType(NumType::I64));
1271            }
1272            I64_EXTEND32_S => {
1273                stack.assert_pop_val_type(ValType::NumType(NumType::I64))?;
1274
1275                stack.push_valtype(ValType::NumType(NumType::I64));
1276            }
1277
1278            // Unimplemented or invalid instructions
1279            0x06..=0x0A
1280            | 0x12..=0x19
1281            | 0x1C..=0x1F
1282            | 0x25..=0x27
1283            | 0xC0..=0xFA
1284            | 0xFB
1285            | 0xFD
1286            | 0xFE
1287            | 0xFF => {
1288                return Err(ValidationError::InvalidInstr(first_instr_byte));
1289            }
1290        }
1291    }
1292}