use super::super::cil::enums::*;
use serde::Serialize;
use std::sync::LazyLock;

/// Process-wide CIL opcode table.
///
/// The opcode table is fully determined by the ECMA-335 spec and never
/// changes at runtime. Building it once is essentially free (~512 entries)
/// but doing so per-`Reader` was a per-function cost that scaled with the
/// number of methods in the binary.
pub static OPCODES: LazyLock<OpCodes> = LazyLock::new(OpCodes::new);

#[derive(Debug, Clone, Serialize)]
pub struct OpCode {
    pub name: &'static str,
    pub value: OpCodeValue,
    pub operand_type: OperandType,
    pub flow_control: FlowControl,
    pub op_code_type: OpCodeType,
    pub stack_push: StackBehaviour,
    pub stack_pop: StackBehaviour,
}

impl OpCode {
    pub fn new(
        name: &'static str,
        value: OpCodeValue,
        operand_type: OperandType,
        flow_control: FlowControl,
        op_code_type: OpCodeType,
        stack_push: StackBehaviour,
        stack_pop: StackBehaviour,
    ) -> Self {
        Self {
            name,
            value,
            operand_type,
            flow_control,
            op_code_type,
            stack_push,
            stack_pop,
        }
    }

    pub fn size(&self) -> usize {
        if ((self.value as usize) < 0x100) || (self.value == OpCodeValue::UNKNOWN1) {
            1
        } else {
            2
        }
    }
}

#[derive(Debug)]
pub struct OpCodes {
    pub one_byte_op_codes: Vec<OpCode>,
    pub two_byte_op_codes: Vec<OpCode>,
}

impl Default for OpCodes {
    fn default() -> Self {
        Self::new()
    }
}

impl OpCodes {
    pub fn new() -> Self {
        let mut res = Self {
            one_byte_op_codes: vec![
                OpCode::new(
                    "UNKNOWN1",
                    OpCodeValue::UNKNOWN1,
                    OperandType::InlineNone,
                    FlowControl::Meta,
                    OpCodeType::Nternal,
                    StackBehaviour::Push0,
                    StackBehaviour::Pop0
                );
                0x100
            ],
            two_byte_op_codes: vec![
                OpCode::new(
                    "UNKNOWN2",
                    OpCodeValue::UNKNOWN2,
                    OperandType::InlineNone,
                    FlowControl::Meta,
                    OpCodeType::Nternal,
                    StackBehaviour::Push0,
                    StackBehaviour::Pop0
                );
                0x100
            ],
        };

        res.add_op_code(OpCode::new(
            "UNKNOWN1",
            OpCodeValue::UNKNOWN1,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Nternal,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "UNKNOWN2",
            OpCodeValue::UNKNOWN2,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Nternal,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "nop",
            OpCodeValue::Nop,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "break",
            OpCodeValue::Break,
            OperandType::InlineNone,
            FlowControl::Break,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldarg.0",
            OpCodeValue::Ldarg_0,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldarg.1",
            OpCodeValue::Ldarg_1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldarg::2",
            OpCodeValue::Ldarg_2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldarg.3",
            OpCodeValue::Ldarg_3,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldloc.0",
            OpCodeValue::Ldloc_0,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldloc.1",
            OpCodeValue::Ldloc_1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldloc.2",
            OpCodeValue::Ldloc_2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldloc.3",
            OpCodeValue::Ldloc_3,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "stloc.0",
            OpCodeValue::Stloc_0,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "stloc.1",
            OpCodeValue::Stloc_1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "stloc.2",
            OpCodeValue::Stloc_2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "stloc.3",
            OpCodeValue::Stloc_3,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "ldarg.s",
            OpCodeValue::Ldarg_S,
            OperandType::ShortInlineVar,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldarga.s",
            OpCodeValue::Ldarga_S,
            OperandType::ShortInlineVar,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "starg.s",
            OpCodeValue::Starg_S,
            OperandType::ShortInlineVar,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "ldloc.s",
            OpCodeValue::Ldloc_S,
            OperandType::ShortInlineVar,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldloca.s",
            OpCodeValue::Ldloca_S,
            OperandType::ShortInlineVar,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "stloc.s",
            OpCodeValue::Stloc_S,
            OperandType::ShortInlineVar,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "ldnull",
            OpCodeValue::Ldnull,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushref,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i4.m1",
            OpCodeValue::Ldc_I4_M1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i4.0",
            OpCodeValue::Ldc_I4_0,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i4.1",
            OpCodeValue::Ldc_I4_1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i4.2",
            OpCodeValue::Ldc_I4_2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i4.3",
            OpCodeValue::Ldc_I4_3,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i4.4",
            OpCodeValue::Ldc_I4_4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i4.5",
            OpCodeValue::Ldc_I4_5,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i4.6",
            OpCodeValue::Ldc_I4_6,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i4.7",
            OpCodeValue::Ldc_I4_7,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i4.8",
            OpCodeValue::Ldc_I4_8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i4.s",
            OpCodeValue::Ldc_I4_S,
            OperandType::ShortInlineI,
            FlowControl::Next,
            OpCodeType::Macro,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i4",
            OpCodeValue::Ldc_I4,
            OperandType::InlineI,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.i8",
            OpCodeValue::Ldc_I8,
            OperandType::InlineI8,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi8,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.r4",
            OpCodeValue::Ldc_R4,
            OperandType::ShortInlineR,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushr4,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldc.r8",
            OpCodeValue::Ldc_R8,
            OperandType::InlineR,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushr8,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "dup",
            OpCodeValue::Dup,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1_push1,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "pop",
            OpCodeValue::Pop,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "jmp",
            OpCodeValue::Jmp,
            OperandType::InlineMethod,
            FlowControl::Call,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "call",
            OpCodeValue::Call,
            OperandType::InlineMethod,
            FlowControl::Call,
            OpCodeType::Primitive,
            StackBehaviour::Varpush,
            StackBehaviour::Varpop,
        ));
        res.add_op_code(OpCode::new(
            "calli",
            OpCodeValue::Calli,
            OperandType::InlineSig,
            FlowControl::Call,
            OpCodeType::Primitive,
            StackBehaviour::Varpush,
            StackBehaviour::Varpop,
        ));
        res.add_op_code(OpCode::new(
            "ret",
            OpCodeValue::Ret,
            OperandType::InlineNone,
            FlowControl::Return,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Varpop,
        ));
        res.add_op_code(OpCode::new(
            "br.s",
            OpCodeValue::Br_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "brfalse.s",
            OpCodeValue::Brfalse_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "brtrue.s",
            OpCodeValue::Brtrue_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "beq.s",
            OpCodeValue::Beq_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "bge.s",
            OpCodeValue::Bge_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "bgt.s",
            OpCodeValue::Bgt_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "ble.s",
            OpCodeValue::Ble_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "blt.s",
            OpCodeValue::Blt_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "bne.un.s",
            OpCodeValue::Bne_Un_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "bge.un.s",
            OpCodeValue::Bge_Un_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "bgt.un.s",
            OpCodeValue::Bgt_Un_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "ble.un.s",
            OpCodeValue::Ble_Un_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "blt.un.s",
            OpCodeValue::Blt_Un_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "br",
            OpCodeValue::Br,
            OperandType::InlineBrTarget,
            FlowControl::Branch,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "brfalse",
            OpCodeValue::Brfalse,
            OperandType::InlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "brtrue",
            OpCodeValue::Brtrue,
            OperandType::InlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "beq",
            OpCodeValue::Beq,
            OperandType::InlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "bge",
            OpCodeValue::Bge,
            OperandType::InlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "bgt",
            OpCodeValue::Bgt,
            OperandType::InlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "ble",
            OpCodeValue::Ble,
            OperandType::InlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "blt",
            OpCodeValue::Blt,
            OperandType::InlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "bne.un",
            OpCodeValue::Bne_Un,
            OperandType::InlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "bge.un",
            OpCodeValue::Bge_Un,
            OperandType::InlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "bgt.un",
            OpCodeValue::Bgt_Un,
            OperandType::InlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "ble.un",
            OpCodeValue::Ble_Un,
            OperandType::InlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "blt.un",
            OpCodeValue::Blt_Un,
            OperandType::InlineBrTarget,
            FlowControl::Cond_Branch,
            OpCodeType::Macro,
            StackBehaviour::Push0,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "switch",
            OpCodeValue::Switch,
            OperandType::InlineSwitch,
            FlowControl::Cond_Branch,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldind.i1",
            OpCodeValue::Ldind_I1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldind.u1",
            OpCodeValue::Ldind_U1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldind.i2",
            OpCodeValue::Ldind_I2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldind.u2",
            OpCodeValue::Ldind_U2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldind.i4",
            OpCodeValue::Ldind_I4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldind.u4",
            OpCodeValue::Ldind_U4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldind.i8",
            OpCodeValue::Ldind_I8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi8,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldind.i",
            OpCodeValue::Ldind_I,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldind.r4",
            OpCodeValue::Ldind_R4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushr4,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldind.r8",
            OpCodeValue::Ldind_R8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushr8,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldind.ref",
            OpCodeValue::Ldind_Ref,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushref,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "stind.ref",
            OpCodeValue::Stind_Ref,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi_popi,
        ));
        res.add_op_code(OpCode::new(
            "stind.i1",
            OpCodeValue::Stind_I1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi_popi,
        ));
        res.add_op_code(OpCode::new(
            "stind.i2",
            OpCodeValue::Stind_I2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi_popi,
        ));
        res.add_op_code(OpCode::new(
            "stind.i4",
            OpCodeValue::Stind_I4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi_popi,
        ));
        res.add_op_code(OpCode::new(
            "stind.i8",
            OpCodeValue::Stind_I8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi_popi8,
        ));
        res.add_op_code(OpCode::new(
            "stind.r4",
            OpCodeValue::Stind_R4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi_popr4,
        ));
        res.add_op_code(OpCode::new(
            "stind.r8",
            OpCodeValue::Stind_R8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi_popr8,
        ));
        res.add_op_code(OpCode::new(
            "add",
            OpCodeValue::Add,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "sub",
            OpCodeValue::Sub,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "mul",
            OpCodeValue::Mul,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "div",
            OpCodeValue::Div,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "div.un",
            OpCodeValue::Div_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "rem",
            OpCodeValue::Rem,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "rem.un",
            OpCodeValue::Rem_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "and",
            OpCodeValue::And,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "or",
            OpCodeValue::Or,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "xor",
            OpCodeValue::Xor,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "shl",
            OpCodeValue::Shl,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "shr",
            OpCodeValue::Shr,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "shr.un",
            OpCodeValue::Shr_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "neg",
            OpCodeValue::Neg,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "not",
            OpCodeValue::Not,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.i1",
            OpCodeValue::Conv_I1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.i2",
            OpCodeValue::Conv_I2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.i4",
            OpCodeValue::Conv_I4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.i8",
            OpCodeValue::Conv_I8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi8,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.r4",
            OpCodeValue::Conv_R4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushr4,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.r8",
            OpCodeValue::Conv_R8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushr8,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.u4",
            OpCodeValue::Conv_U4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.u8",
            OpCodeValue::Conv_U8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi8,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "callvirt",
            OpCodeValue::Callvirt,
            OperandType::InlineMethod,
            FlowControl::Call,
            OpCodeType::Objmodel,
            StackBehaviour::Varpush,
            StackBehaviour::Varpop,
        ));
        res.add_op_code(OpCode::new(
            "cpobj",
            OpCodeValue::Cpobj,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popi_popi,
        ));
        res.add_op_code(OpCode::new(
            "ldobj",
            OpCodeValue::Ldobj,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push1,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldstr",
            OpCodeValue::Ldstr,
            OperandType::InlineString,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushref,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "newobj",
            OpCodeValue::Newobj,
            OperandType::InlineMethod,
            FlowControl::Call,
            OpCodeType::Objmodel,
            StackBehaviour::Pushref,
            StackBehaviour::Varpop,
        ));
        res.add_op_code(OpCode::new(
            "castclass",
            OpCodeValue::Castclass,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushref,
            StackBehaviour::Popref,
        ));
        res.add_op_code(OpCode::new(
            "isinst",
            OpCodeValue::Isinst,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi,
            StackBehaviour::Popref,
        ));
        res.add_op_code(OpCode::new(
            "conv.r.un",
            OpCodeValue::Conv_R_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushr8,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "unbox",
            OpCodeValue::Unbox,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Popref,
        ));
        res.add_op_code(OpCode::new(
            "throw",
            OpCodeValue::Throw,
            OperandType::InlineNone,
            FlowControl::Throw,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popref,
        ));
        res.add_op_code(OpCode::new(
            "ldfld",
            OpCodeValue::Ldfld,
            OperandType::InlineField,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push1,
            StackBehaviour::Popref,
        ));
        res.add_op_code(OpCode::new(
            "ldflda",
            OpCodeValue::Ldflda,
            OperandType::InlineField,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi,
            StackBehaviour::Popref,
        ));
        res.add_op_code(OpCode::new(
            "stfld",
            OpCodeValue::Stfld,
            OperandType::InlineField,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popref_pop1,
        ));
        res.add_op_code(OpCode::new(
            "ldsfld",
            OpCodeValue::Ldsfld,
            OperandType::InlineField,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldsflda",
            OpCodeValue::Ldsflda,
            OperandType::InlineField,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "stsfld",
            OpCodeValue::Stsfld,
            OperandType::InlineField,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "stobj",
            OpCodeValue::Stobj,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi_pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.i1.un",
            OpCodeValue::Conv_Ovf_I1_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.i2.un",
            OpCodeValue::Conv_Ovf_I2_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.i4.un",
            OpCodeValue::Conv_Ovf_I4_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.i8.un",
            OpCodeValue::Conv_Ovf_I8_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi8,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.u1.un",
            OpCodeValue::Conv_Ovf_U1_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.u2.un",
            OpCodeValue::Conv_Ovf_U2_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.u4.un",
            OpCodeValue::Conv_Ovf_U4_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.u8.un",
            OpCodeValue::Conv_Ovf_U8_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi8,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.i.un",
            OpCodeValue::Conv_Ovf_I_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.u.un",
            OpCodeValue::Conv_Ovf_U_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "box",
            OpCodeValue::Box,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushref,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "newarr",
            OpCodeValue::Newarr,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushref,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldlen",
            OpCodeValue::Ldlen,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi,
            StackBehaviour::Popref,
        ));
        res.add_op_code(OpCode::new(
            "ldelema",
            OpCodeValue::Ldelema,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "ldelem.i1",
            OpCodeValue::Ldelem_I1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "ldelem.u1",
            OpCodeValue::Ldelem_U1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "ldelem.i2",
            OpCodeValue::Ldelem_I2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "ldelem.u2",
            OpCodeValue::Ldelem_U2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "ldelem.i4",
            OpCodeValue::Ldelem_I4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "ldelem.u4",
            OpCodeValue::Ldelem_U4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "ldelem.i8",
            OpCodeValue::Ldelem_I8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi8,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "ldelem.i",
            OpCodeValue::Ldelem_I,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushi,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "ldelem.r4",
            OpCodeValue::Ldelem_R4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushr4,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "ldelem.r8",
            OpCodeValue::Ldelem_R8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushr8,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "ldelem.ref",
            OpCodeValue::Ldelem_Ref,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Pushref,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "stelem.i",
            OpCodeValue::Stelem_I,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popref_popi_popi,
        ));
        res.add_op_code(OpCode::new(
            "stelem.i1",
            OpCodeValue::Stelem_I1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popref_popi_popi,
        ));
        res.add_op_code(OpCode::new(
            "stelem.i2",
            OpCodeValue::Stelem_I2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popref_popi_popi,
        ));
        res.add_op_code(OpCode::new(
            "stelem.i4",
            OpCodeValue::Stelem_I4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popref_popi_popi,
        ));
        res.add_op_code(OpCode::new(
            "stelem.i8",
            OpCodeValue::Stelem_I8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popref_popi_popi8,
        ));
        res.add_op_code(OpCode::new(
            "stelem.r4",
            OpCodeValue::Stelem_R4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popref_popi_popr4,
        ));
        res.add_op_code(OpCode::new(
            "stelem.r8",
            OpCodeValue::Stelem_R8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popref_popi_popr8,
        ));
        res.add_op_code(OpCode::new(
            "stelem.ref",
            OpCodeValue::Stelem_Ref,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popref_popi_popref,
        ));
        res.add_op_code(OpCode::new(
            "ldelem",
            OpCodeValue::Ldelem,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push1,
            StackBehaviour::Popref_popi,
        ));
        res.add_op_code(OpCode::new(
            "stelem",
            OpCodeValue::Stelem,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popref_popi_pop1,
        ));
        res.add_op_code(OpCode::new(
            "unbox.any",
            OpCodeValue::Unbox_Any,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push1,
            StackBehaviour::Popref,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.i1",
            OpCodeValue::Conv_Ovf_I1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.u1",
            OpCodeValue::Conv_Ovf_U1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.i2",
            OpCodeValue::Conv_Ovf_I2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.u2",
            OpCodeValue::Conv_Ovf_U2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.i4",
            OpCodeValue::Conv_Ovf_I4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.u4",
            OpCodeValue::Conv_Ovf_U4,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.i8",
            OpCodeValue::Conv_Ovf_I8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi8,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.u8",
            OpCodeValue::Conv_Ovf_U8,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi8,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "refanyval",
            OpCodeValue::Refanyval,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "ckfinite",
            OpCodeValue::Ckfinite,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushr8,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "mkrefany",
            OpCodeValue::Mkrefany,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "ldtoken",
            OpCodeValue::Ldtoken,
            OperandType::InlineTok,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "conv.u2",
            OpCodeValue::Conv_U2,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.u1",
            OpCodeValue::Conv_U1,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.i",
            OpCodeValue::Conv_I,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.i",
            OpCodeValue::Conv_Ovf_I,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "conv.ovf.u",
            OpCodeValue::Conv_Ovf_U,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "add.ovf",
            OpCodeValue::Add_Ovf,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "add.ovf.un",
            OpCodeValue::Add_Ovf_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "mul.ovf",
            OpCodeValue::Mul_Ovf,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "mul.ovf.un",
            OpCodeValue::Mul_Ovf_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "sub.ovf",
            OpCodeValue::Sub_Ovf,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "sub.ovf.un",
            OpCodeValue::Sub_Ovf_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "endfinally",
            OpCodeValue::Endfinally,
            OperandType::InlineNone,
            FlowControl::Return,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::PopAll,
        ));
        res.add_op_code(OpCode::new(
            "leave",
            OpCodeValue::Leave,
            OperandType::InlineBrTarget,
            FlowControl::Branch,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::PopAll,
        ));
        res.add_op_code(OpCode::new(
            "leave.s",
            OpCodeValue::Leave_S,
            OperandType::ShortInlineBrTarget,
            FlowControl::Branch,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::PopAll,
        ));
        res.add_op_code(OpCode::new(
            "stind.i",
            OpCodeValue::Stind_I,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi_popi,
        ));
        res.add_op_code(OpCode::new(
            "conv.u",
            OpCodeValue::Conv_U,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "prefix7",
            OpCodeValue::Prefix7,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Nternal,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "prefix6",
            OpCodeValue::Prefix6,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Nternal,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "prefix5",
            OpCodeValue::Prefix5,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Nternal,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "prefix4",
            OpCodeValue::Prefix4,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Nternal,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "prefix3",
            OpCodeValue::Prefix3,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Nternal,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "prefix2",
            OpCodeValue::Prefix2,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Nternal,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "prefix1",
            OpCodeValue::Prefix1,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Nternal,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "prefixref",
            OpCodeValue::Prefixref,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Nternal,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "arglist",
            OpCodeValue::Arglist,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ceq",
            OpCodeValue::Ceq,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "cgt",
            OpCodeValue::Cgt,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "cgt.un",
            OpCodeValue::Cgt_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "clt",
            OpCodeValue::Clt,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "clt.un",
            OpCodeValue::Clt_Un,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1_pop1,
        ));
        res.add_op_code(OpCode::new(
            "ldftn",
            OpCodeValue::Ldftn,
            OperandType::InlineMethod,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldvirtftn",
            OpCodeValue::Ldvirtftn,
            OperandType::InlineMethod,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Popref,
        ));
        res.add_op_code(OpCode::new(
            "ldarg",
            OpCodeValue::Ldarg,
            OperandType::InlineVar,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldarga",
            OpCodeValue::Ldarga,
            OperandType::InlineVar,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "starg",
            OpCodeValue::Starg,
            OperandType::InlineVar,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "ldloc",
            OpCodeValue::Ldloc,
            OperandType::InlineVar,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push1,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "ldloca",
            OpCodeValue::Ldloca,
            OperandType::InlineVar,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "stloc",
            OpCodeValue::Stloc,
            OperandType::InlineVar,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "localloc",
            OpCodeValue::Localloc,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "endfilter",
            OpCodeValue::Endfilter,
            OperandType::InlineNone,
            FlowControl::Return,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "unaligned.",
            OpCodeValue::Unaligned,
            OperandType::ShortInlineI,
            FlowControl::Meta,
            OpCodeType::Prefix,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "volatile.",
            OpCodeValue::Volatile,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Prefix,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "tail.",
            OpCodeValue::Tailcall,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Prefix,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "initobj",
            OpCodeValue::Initobj,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Popi,
        ));
        res.add_op_code(OpCode::new(
            "constrained.",
            OpCodeValue::Constrained,
            OperandType::InlineType,
            FlowControl::Meta,
            OpCodeType::Prefix,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "cpblk",
            OpCodeValue::Cpblk,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi_popi_popi,
        ));
        res.add_op_code(OpCode::new(
            "initblk",
            OpCodeValue::Initblk,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Push0,
            StackBehaviour::Popi_popi_popi,
        ));
        res.add_op_code(OpCode::new(
            "no.",
            OpCodeValue::No,
            OperandType::ShortInlineI,
            FlowControl::Meta,
            OpCodeType::Prefix,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "rethrow",
            OpCodeValue::Rethrow,
            OperandType::InlineNone,
            FlowControl::Throw,
            OpCodeType::Objmodel,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "sizeof",
            OpCodeValue::Sizeof,
            OperandType::InlineType,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop0,
        ));
        res.add_op_code(OpCode::new(
            "refanytype",
            OpCodeValue::Refanytype,
            OperandType::InlineNone,
            FlowControl::Next,
            OpCodeType::Primitive,
            StackBehaviour::Pushi,
            StackBehaviour::Pop1,
        ));
        res.add_op_code(OpCode::new(
            "readonly.",
            OpCodeValue::Readonly,
            OperandType::InlineNone,
            FlowControl::Meta,
            OpCodeType::Prefix,
            StackBehaviour::Push0,
            StackBehaviour::Pop0,
        ));

        res
    }

    fn add_op_code(&mut self, op_code: OpCode) {
        let val = op_code.value;
        if (val as usize) >> 8 == 0 {
            self.one_byte_op_codes[val as usize] = op_code;
        } else if (val as usize) >> 8 == 0xFE {
            self.two_byte_op_codes[val as usize & 0xFF] = op_code;
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn opcodes_static_initialises_full_tables() {
        assert_eq!(OPCODES.one_byte_op_codes.len(), 0x100);
        assert_eq!(OPCODES.two_byte_op_codes.len(), 0x100);
    }

    #[test]
    fn opcodes_static_is_callable_concurrently() {
        // Force initialization on multiple threads to catch any Sync issues.
        let handles: Vec<_> = (0..8)
            .map(|_| std::thread::spawn(|| OPCODES.one_byte_op_codes.len()))
            .collect();
        for h in handles {
            assert_eq!(h.join().unwrap(), 0x100);
        }
    }

    #[test]
    fn opcodes_static_yields_known_opcode_names() {
        // Nop is one-byte opcode 0x00, Ret is one-byte 0x2A.
        assert_eq!(OPCODES.one_byte_op_codes[0x00].name, "nop");
        assert_eq!(OPCODES.one_byte_op_codes[0x2A].name, "ret");
    }
}
