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