//! Representation of the instructions.
//!
//! Instruction at a sligthly higher level than
//! <https://source.android.com/docs/core/runtime/dalvik-bytecode>

use serde::{Deserialize, Serialize};

use crate::hashmap_vectorize;
use crate::{DexString, DexValue, IdField, IdMethod, IdMethodType, IdType, MethodHandle, Result};
use androscalpel_serializer::Instruction as InsFormat;
use androscalpel_serializer::Serializable;

use anyhow::anyhow;
use pyo3::exceptions::PyTypeError;
use pyo3::prelude::*;

use std::collections::{HashMap, HashSet};

const I8_MIN_AS_I16: i16 = i8::MIN as i16;
const I8_MAX_AS_I16: i16 = i8::MAX as i16;
const I8_MIN_AS_I32: i32 = i8::MIN as i32;
const I8_MAX_AS_I32: i32 = i8::MAX as i32;
const I16_MIN_AS_I32: i32 = i16::MIN as i32;
const I16_MAX_AS_I32: i32 = i16::MAX as i32;
const I16_MIN_AS_I64: i64 = i16::MIN as i64;
const I16_MAX_AS_I64: i64 = i16::MAX as i64;
const I32_MIN_AS_I64: i64 = i32::MIN as i64;
const I32_MAX_AS_I64: i64 = i32::MAX as i64;
const U16_MAX_AS_USIZE: usize = u16::MAX as usize;

// TODO: impl PartialEq and Eq for call site to derive them here
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub enum Instruction {
    Nop(Nop),
    Move(Move),
    MoveWide(MoveWide),
    MoveObject(MoveObject),
    MoveResult(MoveResult),
    MoveResultWide(MoveResultWide),
    MoveException(MoveException),
    MoveResultObject(MoveResultObject),
    ReturnVoid(ReturnVoid),
    Return(Return),
    ReturnWide(ReturnWide),
    ReturnObject(ReturnObject),
    Const(Const),
    ConstWide(ConstWide),
    ConstString(ConstString),
    ConstClass(ConstClass),
    MonitorEnter(MonitorEnter),
    MonitorExit(MonitorExit),
    CheckCast(CheckCast),
    InstanceOf(InstanceOf),
    ArrayLength(ArrayLength),
    NewInstance(NewInstance),
    NewArray(NewArray),
    FilledNewArray(FilledNewArray),
    FillArrayData(FillArrayData),
    Throw(Throw),
    Goto(Goto),
    Switch(Switch),
    CmpLFloat(CmpLFloat),
    CmpGFloat(CmpGFloat),
    CmpLDouble(CmpLDouble),
    CmpGDouble(CmpGDouble),
    CmpLong(CmpLong),
    IfEq(IfEq),
    IfNe(IfNe),
    IfLt(IfLt),
    IfGe(IfGe),
    IfGt(IfGt),
    IfLe(IfLe),
    IfEqZ(IfEqZ),
    IfNeZ(IfNeZ),
    IfLtZ(IfLtZ),
    IfGeZ(IfGeZ),
    IfGtZ(IfGtZ),
    IfLeZ(IfLeZ),
    AGet(AGet),
    AGetWide(AGetWide),
    AGetObject(AGetObject),
    AGetBoolean(AGetBoolean),
    AGetByte(AGetByte),
    AGetChar(AGetChar),
    AGetShort(AGetShort),
    APut(APut),
    APutWide(APutWide),
    APutObject(APutObject),
    APutBoolean(APutBoolean),
    APutByte(APutByte),
    APutChar(APutChar),
    APutShort(APutShort),
    IGet(IGet),
    IGetWide(IGetWide),
    IGetObject(IGetObject),
    IGetBoolean(IGetBoolean),
    IGetByte(IGetByte),
    IGetChar(IGetChar),
    IGetShort(IGetShort),
    IPut(IPut),
    IPutWide(IPutWide),
    IPutObject(IPutObject),
    IPutBoolean(IPutBoolean),
    IPutByte(IPutByte),
    IPutChar(IPutChar),
    IPutShort(IPutShort),
    SGet(SGet),
    SGetWide(SGetWide),
    SGetObject(SGetObject),
    SGetBoolean(SGetBoolean),
    SGetByte(SGetByte),
    SGetChar(SGetChar),
    SGetShort(SGetShort),
    SPut(SPut),
    SPutWide(SPutWide),
    SPutObject(SPutObject),
    SPutBoolean(SPutBoolean),
    SPutByte(SPutByte),
    SPutChar(SPutChar),
    SPutShort(SPutShort),
    InvokeVirtual(InvokeVirtual),
    InvokeSuper(InvokeSuper),
    InvokeDirect(InvokeDirect),
    InvokeStatic(InvokeStatic),
    InvokeInterface(InvokeInterface),
    NegInt(NegInt),
    NotInt(NotInt),
    NegLong(NegLong),
    NotLong(NotLong),
    NegFloat(NegFloat),
    NegDouble(NegDouble),
    IntToLong(IntToLong),
    IntToFloat(IntToFloat),
    IntToDouble(IntToDouble),
    LongToInt(LongToInt),
    LongToFloat(LongToFloat),
    LongToDouble(LongToDouble),
    FloatToInt(FloatToInt),
    FloatToLong(FloatToLong),
    FloatToDouble(FloatToDouble),
    DoubleToInt(DoubleToInt),
    DoubleToLong(DoubleToLong),
    DoubleToFloat(DoubleToFloat),
    IntToByte(IntToByte),
    IntToChar(IntToChar),
    IntToShort(IntToShort),
    AddInt(AddInt),
    SubInt(SubInt),
    MulInt(MulInt),
    DivInt(DivInt),
    RemInt(RemInt),
    AndInt(AndInt),
    OrInt(OrInt),
    XorInt(XorInt),
    ShlInt(ShlInt),
    ShrInt(ShrInt),
    UshrInt(UshrInt),
    AddLong(AddLong),
    SubLong(SubLong),
    MulLong(MulLong),
    DivLong(DivLong),
    RemLong(RemLong),
    AndLong(AndLong),
    OrLong(OrLong),
    XorLong(XorLong),
    ShlLong(ShlLong),
    ShrLong(ShrLong),
    UshrLong(UshrLong),
    AddFloat(AddFloat),
    SubFloat(SubFloat),
    MulFloat(MulFloat),
    DivFloat(DivFloat),
    RemFloat(RemFloat),
    AddDouble(AddDouble),
    SubDouble(SubDouble),
    MulDouble(MulDouble),
    DivDouble(DivDouble),
    RemDouble(RemDouble),
    AddInt2Addr(AddInt2Addr),
    SubInt2Addr(SubInt2Addr),
    MulInt2Addr(MulInt2Addr),
    DivInt2Addr(DivInt2Addr),
    RemInt2Addr(RemInt2Addr),
    AndInt2Addr(AndInt2Addr),
    OrInt2Addr(OrInt2Addr),
    XorInt2Addr(XorInt2Addr),
    ShlInt2Addr(ShlInt2Addr),
    ShrInt2Addr(ShrInt2Addr),
    UshrInt2Addr(UshrInt2Addr),
    AddLong2Addr(AddLong2Addr),
    SubLong2Addr(SubLong2Addr),
    MulLong2Addr(MulLong2Addr),
    DivLong2Addr(DivLong2Addr),
    RemLong2Addr(RemLong2Addr),
    AndLong2Addr(AndLong2Addr),
    OrLong2Addr(OrLong2Addr),
    XorLong2Addr(XorLong2Addr),
    ShlLong2Addr(ShlLong2Addr),
    ShrLong2Addr(ShrLong2Addr),
    UshrLong2Addr(UshrLong2Addr),
    AddFloat2Addr(AddFloat2Addr),
    SubFloat2Addr(SubFloat2Addr),
    MulFloat2Addr(MulFloat2Addr),
    DivFloat2Addr(DivFloat2Addr),
    RemFloat2Addr(RemFloat2Addr),
    AddDouble2Addr(AddDouble2Addr),
    SubDouble2Addr(SubDouble2Addr),
    MulDouble2Addr(MulDouble2Addr),
    DivDouble2Addr(DivDouble2Addr),
    RemDouble2Addr(RemDouble2Addr),
    AddIntLit(AddIntLit),
    RsubIntLit(RsubIntLit),
    MulIntLit(MulIntLit),
    DivIntLit(DivIntLit),
    RemIntLit(RemIntLit),
    AndIntLit(AndIntLit),
    OrIntLit(OrIntLit),
    XorIntLit(XorIntLit),
    ShlIntLit(ShlIntLit),
    ShrIntLit(ShrIntLit),
    UshrIntLit(UshrIntLit),
    InvokePolymorphic(InvokePolymorphic),
    InvokeCustom(InvokeCustom),
    ConstMethodHandle(ConstMethodHandle),
    ConstMethodType(ConstMethodType),
    Try(Try),
    Label(Label),
}

impl Instruction {
    pub fn __str__(&self) -> String {
        match self {
            Self::Nop(ins) => ins.__str__(),
            Self::Move(ins) => ins.__str__(),
            Self::MoveWide(ins) => ins.__str__(),
            Self::MoveObject(ins) => ins.__str__(),
            Self::MoveResult(ins) => ins.__str__(),
            Self::MoveResultWide(ins) => ins.__str__(),
            Self::MoveException(ins) => ins.__str__(),
            Self::MoveResultObject(ins) => ins.__str__(),
            Self::ReturnVoid(ins) => ins.__str__(),
            Self::Return(ins) => ins.__str__(),
            Self::ReturnWide(ins) => ins.__str__(),
            Self::ReturnObject(ins) => ins.__str__(),
            Self::Const(ins) => ins.__str__(),
            Self::ConstWide(ins) => ins.__str__(),
            Self::ConstString(ins) => ins.__str__(),
            Self::ConstClass(ins) => ins.__str__(),
            Self::MonitorEnter(ins) => ins.__str__(),
            Self::MonitorExit(ins) => ins.__str__(),
            Self::CheckCast(ins) => ins.__str__(),
            Self::InstanceOf(ins) => ins.__str__(),
            Self::ArrayLength(ins) => ins.__str__(),
            Self::NewInstance(ins) => ins.__str__(),
            Self::NewArray(ins) => ins.__str__(),
            Self::FilledNewArray(ins) => ins.__str__(),
            Self::FillArrayData(ins) => ins.__str__(),
            Self::Throw(ins) => ins.__str__(),
            Self::Goto(ins) => ins.__str__(),
            Self::Switch(ins) => ins.__str__(),
            Self::CmpLFloat(ins) => ins.__str__(),
            Self::CmpGFloat(ins) => ins.__str__(),
            Self::CmpLDouble(ins) => ins.__str__(),
            Self::CmpGDouble(ins) => ins.__str__(),
            Self::CmpLong(ins) => ins.__str__(),
            Self::IfEq(ins) => ins.__str__(),
            Self::IfNe(ins) => ins.__str__(),
            Self::IfLt(ins) => ins.__str__(),
            Self::IfGe(ins) => ins.__str__(),
            Self::IfGt(ins) => ins.__str__(),
            Self::IfLe(ins) => ins.__str__(),
            Self::IfEqZ(ins) => ins.__str__(),
            Self::IfNeZ(ins) => ins.__str__(),
            Self::IfLtZ(ins) => ins.__str__(),
            Self::IfGeZ(ins) => ins.__str__(),
            Self::IfGtZ(ins) => ins.__str__(),
            Self::IfLeZ(ins) => ins.__str__(),
            Self::AGet(ins) => ins.__str__(),
            Self::AGetWide(ins) => ins.__str__(),
            Self::AGetObject(ins) => ins.__str__(),
            Self::AGetBoolean(ins) => ins.__str__(),
            Self::AGetByte(ins) => ins.__str__(),
            Self::AGetChar(ins) => ins.__str__(),
            Self::AGetShort(ins) => ins.__str__(),
            Self::APut(ins) => ins.__str__(),
            Self::APutWide(ins) => ins.__str__(),
            Self::APutObject(ins) => ins.__str__(),
            Self::APutBoolean(ins) => ins.__str__(),
            Self::APutByte(ins) => ins.__str__(),
            Self::APutChar(ins) => ins.__str__(),
            Self::APutShort(ins) => ins.__str__(),
            Self::IGet(ins) => ins.__str__(),
            Self::IGetWide(ins) => ins.__str__(),
            Self::IGetObject(ins) => ins.__str__(),
            Self::IGetBoolean(ins) => ins.__str__(),
            Self::IGetByte(ins) => ins.__str__(),
            Self::IGetChar(ins) => ins.__str__(),
            Self::IGetShort(ins) => ins.__str__(),
            Self::IPut(ins) => ins.__str__(),
            Self::IPutWide(ins) => ins.__str__(),
            Self::IPutObject(ins) => ins.__str__(),
            Self::IPutBoolean(ins) => ins.__str__(),
            Self::IPutByte(ins) => ins.__str__(),
            Self::IPutChar(ins) => ins.__str__(),
            Self::IPutShort(ins) => ins.__str__(),
            Self::SGet(ins) => ins.__str__(),
            Self::SGetWide(ins) => ins.__str__(),
            Self::SGetObject(ins) => ins.__str__(),
            Self::SGetBoolean(ins) => ins.__str__(),
            Self::SGetByte(ins) => ins.__str__(),
            Self::SGetChar(ins) => ins.__str__(),
            Self::SGetShort(ins) => ins.__str__(),
            Self::SPut(ins) => ins.__str__(),
            Self::SPutWide(ins) => ins.__str__(),
            Self::SPutObject(ins) => ins.__str__(),
            Self::SPutBoolean(ins) => ins.__str__(),
            Self::SPutByte(ins) => ins.__str__(),
            Self::SPutChar(ins) => ins.__str__(),
            Self::SPutShort(ins) => ins.__str__(),
            Self::InvokeVirtual(ins) => ins.__str__(),
            Self::InvokeSuper(ins) => ins.__str__(),
            Self::InvokeDirect(ins) => ins.__str__(),
            Self::InvokeStatic(ins) => ins.__str__(),
            Self::InvokeInterface(ins) => ins.__str__(),
            Self::NegInt(ins) => ins.__str__(),
            Self::NotInt(ins) => ins.__str__(),
            Self::NegLong(ins) => ins.__str__(),
            Self::NotLong(ins) => ins.__str__(),
            Self::NegFloat(ins) => ins.__str__(),
            Self::NegDouble(ins) => ins.__str__(),
            Self::IntToLong(ins) => ins.__str__(),
            Self::IntToFloat(ins) => ins.__str__(),
            Self::IntToDouble(ins) => ins.__str__(),
            Self::LongToInt(ins) => ins.__str__(),
            Self::LongToFloat(ins) => ins.__str__(),
            Self::LongToDouble(ins) => ins.__str__(),
            Self::FloatToInt(ins) => ins.__str__(),
            Self::FloatToLong(ins) => ins.__str__(),
            Self::FloatToDouble(ins) => ins.__str__(),
            Self::DoubleToInt(ins) => ins.__str__(),
            Self::DoubleToLong(ins) => ins.__str__(),
            Self::DoubleToFloat(ins) => ins.__str__(),
            Self::IntToByte(ins) => ins.__str__(),
            Self::IntToChar(ins) => ins.__str__(),
            Self::IntToShort(ins) => ins.__str__(),
            Self::AddInt(ins) => ins.__str__(),
            Self::SubInt(ins) => ins.__str__(),
            Self::MulInt(ins) => ins.__str__(),
            Self::DivInt(ins) => ins.__str__(),
            Self::RemInt(ins) => ins.__str__(),
            Self::AndInt(ins) => ins.__str__(),
            Self::OrInt(ins) => ins.__str__(),
            Self::XorInt(ins) => ins.__str__(),
            Self::ShlInt(ins) => ins.__str__(),
            Self::ShrInt(ins) => ins.__str__(),
            Self::UshrInt(ins) => ins.__str__(),
            Self::AddLong(ins) => ins.__str__(),
            Self::SubLong(ins) => ins.__str__(),
            Self::MulLong(ins) => ins.__str__(),
            Self::DivLong(ins) => ins.__str__(),
            Self::RemLong(ins) => ins.__str__(),
            Self::AndLong(ins) => ins.__str__(),
            Self::OrLong(ins) => ins.__str__(),
            Self::XorLong(ins) => ins.__str__(),
            Self::ShlLong(ins) => ins.__str__(),
            Self::ShrLong(ins) => ins.__str__(),
            Self::UshrLong(ins) => ins.__str__(),
            Self::AddFloat(ins) => ins.__str__(),
            Self::SubFloat(ins) => ins.__str__(),
            Self::MulFloat(ins) => ins.__str__(),
            Self::DivFloat(ins) => ins.__str__(),
            Self::RemFloat(ins) => ins.__str__(),
            Self::AddDouble(ins) => ins.__str__(),
            Self::SubDouble(ins) => ins.__str__(),
            Self::MulDouble(ins) => ins.__str__(),
            Self::DivDouble(ins) => ins.__str__(),
            Self::RemDouble(ins) => ins.__str__(),
            Self::AddInt2Addr(ins) => ins.__str__(),
            Self::SubInt2Addr(ins) => ins.__str__(),
            Self::MulInt2Addr(ins) => ins.__str__(),
            Self::DivInt2Addr(ins) => ins.__str__(),
            Self::RemInt2Addr(ins) => ins.__str__(),
            Self::AndInt2Addr(ins) => ins.__str__(),
            Self::OrInt2Addr(ins) => ins.__str__(),
            Self::XorInt2Addr(ins) => ins.__str__(),
            Self::ShlInt2Addr(ins) => ins.__str__(),
            Self::ShrInt2Addr(ins) => ins.__str__(),
            Self::UshrInt2Addr(ins) => ins.__str__(),
            Self::AddLong2Addr(ins) => ins.__str__(),
            Self::SubLong2Addr(ins) => ins.__str__(),
            Self::MulLong2Addr(ins) => ins.__str__(),
            Self::DivLong2Addr(ins) => ins.__str__(),
            Self::RemLong2Addr(ins) => ins.__str__(),
            Self::AndLong2Addr(ins) => ins.__str__(),
            Self::OrLong2Addr(ins) => ins.__str__(),
            Self::XorLong2Addr(ins) => ins.__str__(),
            Self::ShlLong2Addr(ins) => ins.__str__(),
            Self::ShrLong2Addr(ins) => ins.__str__(),
            Self::UshrLong2Addr(ins) => ins.__str__(),
            Self::AddFloat2Addr(ins) => ins.__str__(),
            Self::SubFloat2Addr(ins) => ins.__str__(),
            Self::MulFloat2Addr(ins) => ins.__str__(),
            Self::DivFloat2Addr(ins) => ins.__str__(),
            Self::RemFloat2Addr(ins) => ins.__str__(),
            Self::AddDouble2Addr(ins) => ins.__str__(),
            Self::SubDouble2Addr(ins) => ins.__str__(),
            Self::MulDouble2Addr(ins) => ins.__str__(),
            Self::DivDouble2Addr(ins) => ins.__str__(),
            Self::RemDouble2Addr(ins) => ins.__str__(),
            Self::AddIntLit(ins) => ins.__str__(),
            Self::RsubIntLit(ins) => ins.__str__(),
            Self::MulIntLit(ins) => ins.__str__(),
            Self::DivIntLit(ins) => ins.__str__(),
            Self::RemIntLit(ins) => ins.__str__(),
            Self::AndIntLit(ins) => ins.__str__(),
            Self::OrIntLit(ins) => ins.__str__(),
            Self::XorIntLit(ins) => ins.__str__(),
            Self::ShlIntLit(ins) => ins.__str__(),
            Self::ShrIntLit(ins) => ins.__str__(),
            Self::UshrIntLit(ins) => ins.__str__(),
            Self::InvokePolymorphic(ins) => ins.__str__(),
            Self::InvokeCustom(ins) => ins.__str__(),
            Self::ConstMethodHandle(ins) => ins.__str__(),
            Self::ConstMethodType(ins) => ins.__str__(),
            Self::Try(ins) => ins.__str__(),
            Self::Label(ins) => ins.__str__(),
        }
    }

    pub fn __repr__(&self) -> String {
        match self {
            Self::Nop(ins) => ins.__repr__(),
            Self::Move(ins) => ins.__repr__(),
            Self::MoveWide(ins) => ins.__repr__(),
            Self::MoveObject(ins) => ins.__repr__(),
            Self::MoveResult(ins) => ins.__repr__(),
            Self::MoveResultWide(ins) => ins.__repr__(),
            Self::MoveException(ins) => ins.__repr__(),
            Self::MoveResultObject(ins) => ins.__repr__(),
            Self::ReturnVoid(ins) => ins.__repr__(),
            Self::Return(ins) => ins.__repr__(),
            Self::ReturnWide(ins) => ins.__repr__(),
            Self::ReturnObject(ins) => ins.__repr__(),
            Self::Const(ins) => ins.__repr__(),
            Self::ConstWide(ins) => ins.__repr__(),
            Self::ConstString(ins) => ins.__repr__(),
            Self::ConstClass(ins) => ins.__repr__(),
            Self::MonitorEnter(ins) => ins.__repr__(),
            Self::MonitorExit(ins) => ins.__repr__(),
            Self::CheckCast(ins) => ins.__repr__(),
            Self::InstanceOf(ins) => ins.__repr__(),
            Self::ArrayLength(ins) => ins.__repr__(),
            Self::NewInstance(ins) => ins.__repr__(),
            Self::NewArray(ins) => ins.__repr__(),
            Self::FilledNewArray(ins) => ins.__repr__(),
            Self::FillArrayData(ins) => ins.__repr__(),
            Self::Throw(ins) => ins.__repr__(),
            Self::Goto(ins) => ins.__repr__(),
            Self::Switch(ins) => ins.__repr__(),
            Self::CmpLFloat(ins) => ins.__repr__(),
            Self::CmpGFloat(ins) => ins.__repr__(),
            Self::CmpLDouble(ins) => ins.__repr__(),
            Self::CmpGDouble(ins) => ins.__repr__(),
            Self::CmpLong(ins) => ins.__repr__(),
            Self::IfEq(ins) => ins.__repr__(),
            Self::IfNe(ins) => ins.__repr__(),
            Self::IfLt(ins) => ins.__repr__(),
            Self::IfGe(ins) => ins.__repr__(),
            Self::IfGt(ins) => ins.__repr__(),
            Self::IfLe(ins) => ins.__repr__(),
            Self::IfEqZ(ins) => ins.__repr__(),
            Self::IfNeZ(ins) => ins.__repr__(),
            Self::IfLtZ(ins) => ins.__repr__(),
            Self::IfGeZ(ins) => ins.__repr__(),
            Self::IfGtZ(ins) => ins.__repr__(),
            Self::IfLeZ(ins) => ins.__repr__(),
            Self::AGet(ins) => ins.__repr__(),
            Self::AGetWide(ins) => ins.__repr__(),
            Self::AGetObject(ins) => ins.__repr__(),
            Self::AGetBoolean(ins) => ins.__repr__(),
            Self::AGetByte(ins) => ins.__repr__(),
            Self::AGetChar(ins) => ins.__repr__(),
            Self::AGetShort(ins) => ins.__repr__(),
            Self::APut(ins) => ins.__repr__(),
            Self::APutWide(ins) => ins.__repr__(),
            Self::APutObject(ins) => ins.__repr__(),
            Self::APutBoolean(ins) => ins.__repr__(),
            Self::APutByte(ins) => ins.__repr__(),
            Self::APutChar(ins) => ins.__repr__(),
            Self::APutShort(ins) => ins.__repr__(),
            Self::IGet(ins) => ins.__repr__(),
            Self::IGetWide(ins) => ins.__repr__(),
            Self::IGetObject(ins) => ins.__repr__(),
            Self::IGetBoolean(ins) => ins.__repr__(),
            Self::IGetByte(ins) => ins.__repr__(),
            Self::IGetChar(ins) => ins.__repr__(),
            Self::IGetShort(ins) => ins.__repr__(),
            Self::IPut(ins) => ins.__repr__(),
            Self::IPutWide(ins) => ins.__repr__(),
            Self::IPutObject(ins) => ins.__repr__(),
            Self::IPutBoolean(ins) => ins.__repr__(),
            Self::IPutByte(ins) => ins.__repr__(),
            Self::IPutChar(ins) => ins.__repr__(),
            Self::IPutShort(ins) => ins.__repr__(),
            Self::SGet(ins) => ins.__repr__(),
            Self::SGetWide(ins) => ins.__repr__(),
            Self::SGetObject(ins) => ins.__repr__(),
            Self::SGetBoolean(ins) => ins.__repr__(),
            Self::SGetByte(ins) => ins.__repr__(),
            Self::SGetChar(ins) => ins.__repr__(),
            Self::SGetShort(ins) => ins.__repr__(),
            Self::SPut(ins) => ins.__repr__(),
            Self::SPutWide(ins) => ins.__repr__(),
            Self::SPutObject(ins) => ins.__repr__(),
            Self::SPutBoolean(ins) => ins.__repr__(),
            Self::SPutByte(ins) => ins.__repr__(),
            Self::SPutChar(ins) => ins.__repr__(),
            Self::SPutShort(ins) => ins.__repr__(),
            Self::InvokeVirtual(ins) => ins.__repr__(),
            Self::InvokeSuper(ins) => ins.__repr__(),
            Self::InvokeDirect(ins) => ins.__repr__(),
            Self::InvokeStatic(ins) => ins.__repr__(),
            Self::InvokeInterface(ins) => ins.__repr__(),
            Self::NegInt(ins) => ins.__repr__(),
            Self::NotInt(ins) => ins.__repr__(),
            Self::NegLong(ins) => ins.__repr__(),
            Self::NotLong(ins) => ins.__repr__(),
            Self::NegFloat(ins) => ins.__repr__(),
            Self::NegDouble(ins) => ins.__repr__(),
            Self::IntToLong(ins) => ins.__repr__(),
            Self::IntToFloat(ins) => ins.__repr__(),
            Self::IntToDouble(ins) => ins.__repr__(),
            Self::LongToInt(ins) => ins.__repr__(),
            Self::LongToFloat(ins) => ins.__repr__(),
            Self::LongToDouble(ins) => ins.__repr__(),
            Self::FloatToInt(ins) => ins.__repr__(),
            Self::FloatToLong(ins) => ins.__repr__(),
            Self::FloatToDouble(ins) => ins.__repr__(),
            Self::DoubleToInt(ins) => ins.__repr__(),
            Self::DoubleToLong(ins) => ins.__repr__(),
            Self::DoubleToFloat(ins) => ins.__repr__(),
            Self::IntToByte(ins) => ins.__repr__(),
            Self::IntToChar(ins) => ins.__repr__(),
            Self::IntToShort(ins) => ins.__repr__(),
            Self::AddInt(ins) => ins.__repr__(),
            Self::SubInt(ins) => ins.__repr__(),
            Self::MulInt(ins) => ins.__repr__(),
            Self::DivInt(ins) => ins.__repr__(),
            Self::RemInt(ins) => ins.__repr__(),
            Self::AndInt(ins) => ins.__repr__(),
            Self::OrInt(ins) => ins.__repr__(),
            Self::XorInt(ins) => ins.__repr__(),
            Self::ShlInt(ins) => ins.__repr__(),
            Self::ShrInt(ins) => ins.__repr__(),
            Self::UshrInt(ins) => ins.__repr__(),
            Self::AddLong(ins) => ins.__repr__(),
            Self::SubLong(ins) => ins.__repr__(),
            Self::MulLong(ins) => ins.__repr__(),
            Self::DivLong(ins) => ins.__repr__(),
            Self::RemLong(ins) => ins.__repr__(),
            Self::AndLong(ins) => ins.__repr__(),
            Self::OrLong(ins) => ins.__repr__(),
            Self::XorLong(ins) => ins.__repr__(),
            Self::ShlLong(ins) => ins.__repr__(),
            Self::ShrLong(ins) => ins.__repr__(),
            Self::UshrLong(ins) => ins.__repr__(),
            Self::AddFloat(ins) => ins.__repr__(),
            Self::SubFloat(ins) => ins.__repr__(),
            Self::MulFloat(ins) => ins.__repr__(),
            Self::DivFloat(ins) => ins.__repr__(),
            Self::RemFloat(ins) => ins.__repr__(),
            Self::AddDouble(ins) => ins.__repr__(),
            Self::SubDouble(ins) => ins.__repr__(),
            Self::MulDouble(ins) => ins.__repr__(),
            Self::DivDouble(ins) => ins.__repr__(),
            Self::RemDouble(ins) => ins.__repr__(),
            Self::AddInt2Addr(ins) => ins.__repr__(),
            Self::SubInt2Addr(ins) => ins.__repr__(),
            Self::MulInt2Addr(ins) => ins.__repr__(),
            Self::DivInt2Addr(ins) => ins.__repr__(),
            Self::RemInt2Addr(ins) => ins.__repr__(),
            Self::AndInt2Addr(ins) => ins.__repr__(),
            Self::OrInt2Addr(ins) => ins.__repr__(),
            Self::XorInt2Addr(ins) => ins.__repr__(),
            Self::ShlInt2Addr(ins) => ins.__repr__(),
            Self::ShrInt2Addr(ins) => ins.__repr__(),
            Self::UshrInt2Addr(ins) => ins.__repr__(),
            Self::AddLong2Addr(ins) => ins.__repr__(),
            Self::SubLong2Addr(ins) => ins.__repr__(),
            Self::MulLong2Addr(ins) => ins.__repr__(),
            Self::DivLong2Addr(ins) => ins.__repr__(),
            Self::RemLong2Addr(ins) => ins.__repr__(),
            Self::AndLong2Addr(ins) => ins.__repr__(),
            Self::OrLong2Addr(ins) => ins.__repr__(),
            Self::XorLong2Addr(ins) => ins.__repr__(),
            Self::ShlLong2Addr(ins) => ins.__repr__(),
            Self::ShrLong2Addr(ins) => ins.__repr__(),
            Self::UshrLong2Addr(ins) => ins.__repr__(),
            Self::AddFloat2Addr(ins) => ins.__repr__(),
            Self::SubFloat2Addr(ins) => ins.__repr__(),
            Self::MulFloat2Addr(ins) => ins.__repr__(),
            Self::DivFloat2Addr(ins) => ins.__repr__(),
            Self::RemFloat2Addr(ins) => ins.__repr__(),
            Self::AddDouble2Addr(ins) => ins.__repr__(),
            Self::SubDouble2Addr(ins) => ins.__repr__(),
            Self::MulDouble2Addr(ins) => ins.__repr__(),
            Self::DivDouble2Addr(ins) => ins.__repr__(),
            Self::RemDouble2Addr(ins) => ins.__repr__(),
            Self::AddIntLit(ins) => ins.__repr__(),
            Self::RsubIntLit(ins) => ins.__repr__(),
            Self::MulIntLit(ins) => ins.__repr__(),
            Self::DivIntLit(ins) => ins.__repr__(),
            Self::RemIntLit(ins) => ins.__repr__(),
            Self::AndIntLit(ins) => ins.__repr__(),
            Self::OrIntLit(ins) => ins.__repr__(),
            Self::XorIntLit(ins) => ins.__repr__(),
            Self::ShlIntLit(ins) => ins.__repr__(),
            Self::ShrIntLit(ins) => ins.__repr__(),
            Self::UshrIntLit(ins) => ins.__repr__(),
            Self::InvokePolymorphic(ins) => ins.__repr__(),
            Self::InvokeCustom(ins) => ins.__repr__(),
            Self::ConstMethodHandle(ins) => ins.__repr__(),
            Self::ConstMethodType(ins) => ins.__repr__(),
            Self::Try(ins) => ins.__repr__(),
            Self::Label(ins) => ins.__repr__(),
        }
    }

    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        match self {
            Self::Nop(ins) => ins.min_ins_size(),
            Self::Move(ins) => ins.min_ins_size(),
            Self::MoveWide(ins) => ins.min_ins_size(),
            Self::MoveObject(ins) => ins.min_ins_size(),
            Self::MoveResult(ins) => ins.min_ins_size(),
            Self::MoveResultWide(ins) => ins.min_ins_size(),
            Self::MoveException(ins) => ins.min_ins_size(),
            Self::MoveResultObject(ins) => ins.min_ins_size(),
            Self::ReturnVoid(ins) => ins.min_ins_size(),
            Self::Return(ins) => ins.min_ins_size(),
            Self::ReturnWide(ins) => ins.min_ins_size(),
            Self::ReturnObject(ins) => ins.min_ins_size(),
            Self::Const(ins) => ins.min_ins_size(),
            Self::ConstWide(ins) => ins.min_ins_size(),
            Self::ConstString(ins) => ins.min_ins_size(),
            Self::ConstClass(ins) => ins.min_ins_size(),
            Self::MonitorEnter(ins) => ins.min_ins_size(),
            Self::MonitorExit(ins) => ins.min_ins_size(),
            Self::CheckCast(ins) => ins.min_ins_size(),
            Self::InstanceOf(ins) => ins.min_ins_size(),
            Self::ArrayLength(ins) => ins.min_ins_size(),
            Self::NewInstance(ins) => ins.min_ins_size(),
            Self::NewArray(ins) => ins.min_ins_size(),
            Self::FilledNewArray(ins) => ins.min_ins_size(),
            Self::FillArrayData(ins) => ins.min_ins_size(),
            Self::Throw(ins) => ins.min_ins_size(),
            Self::Goto(ins) => ins.min_ins_size(),
            Self::Switch(ins) => ins.min_ins_size(),
            Self::CmpLFloat(ins) => ins.min_ins_size(),
            Self::CmpGFloat(ins) => ins.min_ins_size(),
            Self::CmpLDouble(ins) => ins.min_ins_size(),
            Self::CmpGDouble(ins) => ins.min_ins_size(),
            Self::CmpLong(ins) => ins.min_ins_size(),
            Self::IfEq(ins) => ins.min_ins_size(),
            Self::IfNe(ins) => ins.min_ins_size(),
            Self::IfLt(ins) => ins.min_ins_size(),
            Self::IfGe(ins) => ins.min_ins_size(),
            Self::IfGt(ins) => ins.min_ins_size(),
            Self::IfLe(ins) => ins.min_ins_size(),
            Self::IfEqZ(ins) => ins.min_ins_size(),
            Self::IfNeZ(ins) => ins.min_ins_size(),
            Self::IfLtZ(ins) => ins.min_ins_size(),
            Self::IfGeZ(ins) => ins.min_ins_size(),
            Self::IfGtZ(ins) => ins.min_ins_size(),
            Self::IfLeZ(ins) => ins.min_ins_size(),
            Self::AGet(ins) => ins.min_ins_size(),
            Self::AGetWide(ins) => ins.min_ins_size(),
            Self::AGetObject(ins) => ins.min_ins_size(),
            Self::AGetBoolean(ins) => ins.min_ins_size(),
            Self::AGetByte(ins) => ins.min_ins_size(),
            Self::AGetChar(ins) => ins.min_ins_size(),
            Self::AGetShort(ins) => ins.min_ins_size(),
            Self::APut(ins) => ins.min_ins_size(),
            Self::APutWide(ins) => ins.min_ins_size(),
            Self::APutObject(ins) => ins.min_ins_size(),
            Self::APutBoolean(ins) => ins.min_ins_size(),
            Self::APutByte(ins) => ins.min_ins_size(),
            Self::APutChar(ins) => ins.min_ins_size(),
            Self::APutShort(ins) => ins.min_ins_size(),
            Self::IGet(ins) => ins.min_ins_size(),
            Self::IGetWide(ins) => ins.min_ins_size(),
            Self::IGetObject(ins) => ins.min_ins_size(),
            Self::IGetBoolean(ins) => ins.min_ins_size(),
            Self::IGetByte(ins) => ins.min_ins_size(),
            Self::IGetChar(ins) => ins.min_ins_size(),
            Self::IGetShort(ins) => ins.min_ins_size(),
            Self::IPut(ins) => ins.min_ins_size(),
            Self::IPutWide(ins) => ins.min_ins_size(),
            Self::IPutObject(ins) => ins.min_ins_size(),
            Self::IPutBoolean(ins) => ins.min_ins_size(),
            Self::IPutByte(ins) => ins.min_ins_size(),
            Self::IPutChar(ins) => ins.min_ins_size(),
            Self::IPutShort(ins) => ins.min_ins_size(),
            Self::SGet(ins) => ins.min_ins_size(),
            Self::SGetWide(ins) => ins.min_ins_size(),
            Self::SGetObject(ins) => ins.min_ins_size(),
            Self::SGetBoolean(ins) => ins.min_ins_size(),
            Self::SGetByte(ins) => ins.min_ins_size(),
            Self::SGetChar(ins) => ins.min_ins_size(),
            Self::SGetShort(ins) => ins.min_ins_size(),
            Self::SPut(ins) => ins.min_ins_size(),
            Self::SPutWide(ins) => ins.min_ins_size(),
            Self::SPutObject(ins) => ins.min_ins_size(),
            Self::SPutBoolean(ins) => ins.min_ins_size(),
            Self::SPutByte(ins) => ins.min_ins_size(),
            Self::SPutChar(ins) => ins.min_ins_size(),
            Self::SPutShort(ins) => ins.min_ins_size(),
            Self::InvokeVirtual(ins) => ins.min_ins_size(),
            Self::InvokeSuper(ins) => ins.min_ins_size(),
            Self::InvokeDirect(ins) => ins.min_ins_size(),
            Self::InvokeStatic(ins) => ins.min_ins_size(),
            Self::InvokeInterface(ins) => ins.min_ins_size(),
            Self::NegInt(ins) => ins.min_ins_size(),
            Self::NotInt(ins) => ins.min_ins_size(),
            Self::NegLong(ins) => ins.min_ins_size(),
            Self::NotLong(ins) => ins.min_ins_size(),
            Self::NegFloat(ins) => ins.min_ins_size(),
            Self::NegDouble(ins) => ins.min_ins_size(),
            Self::IntToLong(ins) => ins.min_ins_size(),
            Self::IntToFloat(ins) => ins.min_ins_size(),
            Self::IntToDouble(ins) => ins.min_ins_size(),
            Self::LongToInt(ins) => ins.min_ins_size(),
            Self::LongToFloat(ins) => ins.min_ins_size(),
            Self::LongToDouble(ins) => ins.min_ins_size(),
            Self::FloatToInt(ins) => ins.min_ins_size(),
            Self::FloatToLong(ins) => ins.min_ins_size(),
            Self::FloatToDouble(ins) => ins.min_ins_size(),
            Self::DoubleToInt(ins) => ins.min_ins_size(),
            Self::DoubleToLong(ins) => ins.min_ins_size(),
            Self::DoubleToFloat(ins) => ins.min_ins_size(),
            Self::IntToByte(ins) => ins.min_ins_size(),
            Self::IntToChar(ins) => ins.min_ins_size(),
            Self::IntToShort(ins) => ins.min_ins_size(),
            Self::AddInt(ins) => ins.min_ins_size(),
            Self::SubInt(ins) => ins.min_ins_size(),
            Self::MulInt(ins) => ins.min_ins_size(),
            Self::DivInt(ins) => ins.min_ins_size(),
            Self::RemInt(ins) => ins.min_ins_size(),
            Self::AndInt(ins) => ins.min_ins_size(),
            Self::OrInt(ins) => ins.min_ins_size(),
            Self::XorInt(ins) => ins.min_ins_size(),
            Self::ShlInt(ins) => ins.min_ins_size(),
            Self::ShrInt(ins) => ins.min_ins_size(),
            Self::UshrInt(ins) => ins.min_ins_size(),
            Self::AddLong(ins) => ins.min_ins_size(),
            Self::SubLong(ins) => ins.min_ins_size(),
            Self::MulLong(ins) => ins.min_ins_size(),
            Self::DivLong(ins) => ins.min_ins_size(),
            Self::RemLong(ins) => ins.min_ins_size(),
            Self::AndLong(ins) => ins.min_ins_size(),
            Self::OrLong(ins) => ins.min_ins_size(),
            Self::XorLong(ins) => ins.min_ins_size(),
            Self::ShlLong(ins) => ins.min_ins_size(),
            Self::ShrLong(ins) => ins.min_ins_size(),
            Self::UshrLong(ins) => ins.min_ins_size(),
            Self::AddFloat(ins) => ins.min_ins_size(),
            Self::SubFloat(ins) => ins.min_ins_size(),
            Self::MulFloat(ins) => ins.min_ins_size(),
            Self::DivFloat(ins) => ins.min_ins_size(),
            Self::RemFloat(ins) => ins.min_ins_size(),
            Self::AddDouble(ins) => ins.min_ins_size(),
            Self::SubDouble(ins) => ins.min_ins_size(),
            Self::MulDouble(ins) => ins.min_ins_size(),
            Self::DivDouble(ins) => ins.min_ins_size(),
            Self::RemDouble(ins) => ins.min_ins_size(),
            Self::AddInt2Addr(ins) => ins.min_ins_size(),
            Self::SubInt2Addr(ins) => ins.min_ins_size(),
            Self::MulInt2Addr(ins) => ins.min_ins_size(),
            Self::DivInt2Addr(ins) => ins.min_ins_size(),
            Self::RemInt2Addr(ins) => ins.min_ins_size(),
            Self::AndInt2Addr(ins) => ins.min_ins_size(),
            Self::OrInt2Addr(ins) => ins.min_ins_size(),
            Self::XorInt2Addr(ins) => ins.min_ins_size(),
            Self::ShlInt2Addr(ins) => ins.min_ins_size(),
            Self::ShrInt2Addr(ins) => ins.min_ins_size(),
            Self::UshrInt2Addr(ins) => ins.min_ins_size(),
            Self::AddLong2Addr(ins) => ins.min_ins_size(),
            Self::SubLong2Addr(ins) => ins.min_ins_size(),
            Self::MulLong2Addr(ins) => ins.min_ins_size(),
            Self::DivLong2Addr(ins) => ins.min_ins_size(),
            Self::RemLong2Addr(ins) => ins.min_ins_size(),
            Self::AndLong2Addr(ins) => ins.min_ins_size(),
            Self::OrLong2Addr(ins) => ins.min_ins_size(),
            Self::XorLong2Addr(ins) => ins.min_ins_size(),
            Self::ShlLong2Addr(ins) => ins.min_ins_size(),
            Self::ShrLong2Addr(ins) => ins.min_ins_size(),
            Self::UshrLong2Addr(ins) => ins.min_ins_size(),
            Self::AddFloat2Addr(ins) => ins.min_ins_size(),
            Self::SubFloat2Addr(ins) => ins.min_ins_size(),
            Self::MulFloat2Addr(ins) => ins.min_ins_size(),
            Self::DivFloat2Addr(ins) => ins.min_ins_size(),
            Self::RemFloat2Addr(ins) => ins.min_ins_size(),
            Self::AddDouble2Addr(ins) => ins.min_ins_size(),
            Self::SubDouble2Addr(ins) => ins.min_ins_size(),
            Self::MulDouble2Addr(ins) => ins.min_ins_size(),
            Self::DivDouble2Addr(ins) => ins.min_ins_size(),
            Self::RemDouble2Addr(ins) => ins.min_ins_size(),
            Self::AddIntLit(ins) => ins.min_ins_size(),
            Self::RsubIntLit(ins) => ins.min_ins_size(),
            Self::MulIntLit(ins) => ins.min_ins_size(),
            Self::DivIntLit(ins) => ins.min_ins_size(),
            Self::RemIntLit(ins) => ins.min_ins_size(),
            Self::AndIntLit(ins) => ins.min_ins_size(),
            Self::OrIntLit(ins) => ins.min_ins_size(),
            Self::XorIntLit(ins) => ins.min_ins_size(),
            Self::ShlIntLit(ins) => ins.min_ins_size(),
            Self::ShrIntLit(ins) => ins.min_ins_size(),
            Self::UshrIntLit(ins) => ins.min_ins_size(),
            Self::InvokePolymorphic(ins) => ins.min_ins_size(),
            Self::InvokeCustom(ins) => ins.min_ins_size(),
            Self::ConstMethodHandle(ins) => ins.min_ins_size(),
            Self::ConstMethodType(ins) => ins.min_ins_size(),
            Self::Try(ins) => ins.min_ins_size(),
            Self::Label(ins) => ins.min_ins_size(),
        }
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        match self {
            Self::Nop(ins) => ins.max_ins_size(),
            Self::Move(ins) => ins.max_ins_size(),
            Self::MoveWide(ins) => ins.max_ins_size(),
            Self::MoveObject(ins) => ins.max_ins_size(),
            Self::MoveResult(ins) => ins.max_ins_size(),
            Self::MoveResultWide(ins) => ins.max_ins_size(),
            Self::MoveException(ins) => ins.max_ins_size(),
            Self::MoveResultObject(ins) => ins.max_ins_size(),
            Self::ReturnVoid(ins) => ins.max_ins_size(),
            Self::Return(ins) => ins.max_ins_size(),
            Self::ReturnWide(ins) => ins.max_ins_size(),
            Self::ReturnObject(ins) => ins.max_ins_size(),
            Self::Const(ins) => ins.max_ins_size(),
            Self::ConstWide(ins) => ins.max_ins_size(),
            Self::ConstString(ins) => ins.max_ins_size(),
            Self::ConstClass(ins) => ins.max_ins_size(),
            Self::MonitorEnter(ins) => ins.max_ins_size(),
            Self::MonitorExit(ins) => ins.max_ins_size(),
            Self::CheckCast(ins) => ins.max_ins_size(),
            Self::InstanceOf(ins) => ins.max_ins_size(),
            Self::ArrayLength(ins) => ins.max_ins_size(),
            Self::NewInstance(ins) => ins.max_ins_size(),
            Self::NewArray(ins) => ins.max_ins_size(),
            Self::FilledNewArray(ins) => ins.max_ins_size(),
            Self::FillArrayData(ins) => ins.max_ins_size(),
            Self::Throw(ins) => ins.max_ins_size(),
            Self::Goto(ins) => ins.max_ins_size(),
            Self::Switch(ins) => ins.max_ins_size(),
            Self::CmpLFloat(ins) => ins.max_ins_size(),
            Self::CmpGFloat(ins) => ins.max_ins_size(),
            Self::CmpLDouble(ins) => ins.max_ins_size(),
            Self::CmpGDouble(ins) => ins.max_ins_size(),
            Self::CmpLong(ins) => ins.max_ins_size(),
            Self::IfEq(ins) => ins.max_ins_size(),
            Self::IfNe(ins) => ins.max_ins_size(),
            Self::IfLt(ins) => ins.max_ins_size(),
            Self::IfGe(ins) => ins.max_ins_size(),
            Self::IfGt(ins) => ins.max_ins_size(),
            Self::IfLe(ins) => ins.max_ins_size(),
            Self::IfEqZ(ins) => ins.max_ins_size(),
            Self::IfNeZ(ins) => ins.max_ins_size(),
            Self::IfLtZ(ins) => ins.max_ins_size(),
            Self::IfGeZ(ins) => ins.max_ins_size(),
            Self::IfGtZ(ins) => ins.max_ins_size(),
            Self::IfLeZ(ins) => ins.max_ins_size(),
            Self::AGet(ins) => ins.max_ins_size(),
            Self::AGetWide(ins) => ins.max_ins_size(),
            Self::AGetObject(ins) => ins.max_ins_size(),
            Self::AGetBoolean(ins) => ins.max_ins_size(),
            Self::AGetByte(ins) => ins.max_ins_size(),
            Self::AGetChar(ins) => ins.max_ins_size(),
            Self::AGetShort(ins) => ins.max_ins_size(),
            Self::APut(ins) => ins.max_ins_size(),
            Self::APutWide(ins) => ins.max_ins_size(),
            Self::APutObject(ins) => ins.max_ins_size(),
            Self::APutBoolean(ins) => ins.max_ins_size(),
            Self::APutByte(ins) => ins.max_ins_size(),
            Self::APutChar(ins) => ins.max_ins_size(),
            Self::APutShort(ins) => ins.max_ins_size(),
            Self::IGet(ins) => ins.max_ins_size(),
            Self::IGetWide(ins) => ins.max_ins_size(),
            Self::IGetObject(ins) => ins.max_ins_size(),
            Self::IGetBoolean(ins) => ins.max_ins_size(),
            Self::IGetByte(ins) => ins.max_ins_size(),
            Self::IGetChar(ins) => ins.max_ins_size(),
            Self::IGetShort(ins) => ins.max_ins_size(),
            Self::IPut(ins) => ins.max_ins_size(),
            Self::IPutWide(ins) => ins.max_ins_size(),
            Self::IPutObject(ins) => ins.max_ins_size(),
            Self::IPutBoolean(ins) => ins.max_ins_size(),
            Self::IPutByte(ins) => ins.max_ins_size(),
            Self::IPutChar(ins) => ins.max_ins_size(),
            Self::IPutShort(ins) => ins.max_ins_size(),
            Self::SGet(ins) => ins.max_ins_size(),
            Self::SGetWide(ins) => ins.max_ins_size(),
            Self::SGetObject(ins) => ins.max_ins_size(),
            Self::SGetBoolean(ins) => ins.max_ins_size(),
            Self::SGetByte(ins) => ins.max_ins_size(),
            Self::SGetChar(ins) => ins.max_ins_size(),
            Self::SGetShort(ins) => ins.max_ins_size(),
            Self::SPut(ins) => ins.max_ins_size(),
            Self::SPutWide(ins) => ins.max_ins_size(),
            Self::SPutObject(ins) => ins.max_ins_size(),
            Self::SPutBoolean(ins) => ins.max_ins_size(),
            Self::SPutByte(ins) => ins.max_ins_size(),
            Self::SPutChar(ins) => ins.max_ins_size(),
            Self::SPutShort(ins) => ins.max_ins_size(),
            Self::InvokeVirtual(ins) => ins.max_ins_size(),
            Self::InvokeSuper(ins) => ins.max_ins_size(),
            Self::InvokeDirect(ins) => ins.max_ins_size(),
            Self::InvokeStatic(ins) => ins.max_ins_size(),
            Self::InvokeInterface(ins) => ins.max_ins_size(),
            Self::NegInt(ins) => ins.max_ins_size(),
            Self::NotInt(ins) => ins.max_ins_size(),
            Self::NegLong(ins) => ins.max_ins_size(),
            Self::NotLong(ins) => ins.max_ins_size(),
            Self::NegFloat(ins) => ins.max_ins_size(),
            Self::NegDouble(ins) => ins.max_ins_size(),
            Self::IntToLong(ins) => ins.max_ins_size(),
            Self::IntToFloat(ins) => ins.max_ins_size(),
            Self::IntToDouble(ins) => ins.max_ins_size(),
            Self::LongToInt(ins) => ins.max_ins_size(),
            Self::LongToFloat(ins) => ins.max_ins_size(),
            Self::LongToDouble(ins) => ins.max_ins_size(),
            Self::FloatToInt(ins) => ins.max_ins_size(),
            Self::FloatToLong(ins) => ins.max_ins_size(),
            Self::FloatToDouble(ins) => ins.max_ins_size(),
            Self::DoubleToInt(ins) => ins.max_ins_size(),
            Self::DoubleToLong(ins) => ins.max_ins_size(),
            Self::DoubleToFloat(ins) => ins.max_ins_size(),
            Self::IntToByte(ins) => ins.max_ins_size(),
            Self::IntToChar(ins) => ins.max_ins_size(),
            Self::IntToShort(ins) => ins.max_ins_size(),
            Self::AddInt(ins) => ins.max_ins_size(),
            Self::SubInt(ins) => ins.max_ins_size(),
            Self::MulInt(ins) => ins.max_ins_size(),
            Self::DivInt(ins) => ins.max_ins_size(),
            Self::RemInt(ins) => ins.max_ins_size(),
            Self::AndInt(ins) => ins.max_ins_size(),
            Self::OrInt(ins) => ins.max_ins_size(),
            Self::XorInt(ins) => ins.max_ins_size(),
            Self::ShlInt(ins) => ins.max_ins_size(),
            Self::ShrInt(ins) => ins.max_ins_size(),
            Self::UshrInt(ins) => ins.max_ins_size(),
            Self::AddLong(ins) => ins.max_ins_size(),
            Self::SubLong(ins) => ins.max_ins_size(),
            Self::MulLong(ins) => ins.max_ins_size(),
            Self::DivLong(ins) => ins.max_ins_size(),
            Self::RemLong(ins) => ins.max_ins_size(),
            Self::AndLong(ins) => ins.max_ins_size(),
            Self::OrLong(ins) => ins.max_ins_size(),
            Self::XorLong(ins) => ins.max_ins_size(),
            Self::ShlLong(ins) => ins.max_ins_size(),
            Self::ShrLong(ins) => ins.max_ins_size(),
            Self::UshrLong(ins) => ins.max_ins_size(),
            Self::AddFloat(ins) => ins.max_ins_size(),
            Self::SubFloat(ins) => ins.max_ins_size(),
            Self::MulFloat(ins) => ins.max_ins_size(),
            Self::DivFloat(ins) => ins.max_ins_size(),
            Self::RemFloat(ins) => ins.max_ins_size(),
            Self::AddDouble(ins) => ins.max_ins_size(),
            Self::SubDouble(ins) => ins.max_ins_size(),
            Self::MulDouble(ins) => ins.max_ins_size(),
            Self::DivDouble(ins) => ins.max_ins_size(),
            Self::RemDouble(ins) => ins.max_ins_size(),
            Self::AddInt2Addr(ins) => ins.max_ins_size(),
            Self::SubInt2Addr(ins) => ins.max_ins_size(),
            Self::MulInt2Addr(ins) => ins.max_ins_size(),
            Self::DivInt2Addr(ins) => ins.max_ins_size(),
            Self::RemInt2Addr(ins) => ins.max_ins_size(),
            Self::AndInt2Addr(ins) => ins.max_ins_size(),
            Self::OrInt2Addr(ins) => ins.max_ins_size(),
            Self::XorInt2Addr(ins) => ins.max_ins_size(),
            Self::ShlInt2Addr(ins) => ins.max_ins_size(),
            Self::ShrInt2Addr(ins) => ins.max_ins_size(),
            Self::UshrInt2Addr(ins) => ins.max_ins_size(),
            Self::AddLong2Addr(ins) => ins.max_ins_size(),
            Self::SubLong2Addr(ins) => ins.max_ins_size(),
            Self::MulLong2Addr(ins) => ins.max_ins_size(),
            Self::DivLong2Addr(ins) => ins.max_ins_size(),
            Self::RemLong2Addr(ins) => ins.max_ins_size(),
            Self::AndLong2Addr(ins) => ins.max_ins_size(),
            Self::OrLong2Addr(ins) => ins.max_ins_size(),
            Self::XorLong2Addr(ins) => ins.max_ins_size(),
            Self::ShlLong2Addr(ins) => ins.max_ins_size(),
            Self::ShrLong2Addr(ins) => ins.max_ins_size(),
            Self::UshrLong2Addr(ins) => ins.max_ins_size(),
            Self::AddFloat2Addr(ins) => ins.max_ins_size(),
            Self::SubFloat2Addr(ins) => ins.max_ins_size(),
            Self::MulFloat2Addr(ins) => ins.max_ins_size(),
            Self::DivFloat2Addr(ins) => ins.max_ins_size(),
            Self::RemFloat2Addr(ins) => ins.max_ins_size(),
            Self::AddDouble2Addr(ins) => ins.max_ins_size(),
            Self::SubDouble2Addr(ins) => ins.max_ins_size(),
            Self::MulDouble2Addr(ins) => ins.max_ins_size(),
            Self::DivDouble2Addr(ins) => ins.max_ins_size(),
            Self::RemDouble2Addr(ins) => ins.max_ins_size(),
            Self::AddIntLit(ins) => ins.max_ins_size(),
            Self::RsubIntLit(ins) => ins.max_ins_size(),
            Self::MulIntLit(ins) => ins.max_ins_size(),
            Self::DivIntLit(ins) => ins.max_ins_size(),
            Self::RemIntLit(ins) => ins.max_ins_size(),
            Self::AndIntLit(ins) => ins.max_ins_size(),
            Self::OrIntLit(ins) => ins.max_ins_size(),
            Self::XorIntLit(ins) => ins.max_ins_size(),
            Self::ShlIntLit(ins) => ins.max_ins_size(),
            Self::ShrIntLit(ins) => ins.max_ins_size(),
            Self::UshrIntLit(ins) => ins.max_ins_size(),
            Self::InvokePolymorphic(ins) => ins.max_ins_size(),
            Self::InvokeCustom(ins) => ins.max_ins_size(),
            Self::ConstMethodHandle(ins) => ins.max_ins_size(),
            Self::ConstMethodType(ins) => ins.max_ins_size(),
            Self::Try(ins) => ins.max_ins_size(),
            Self::Label(ins) => ins.max_ins_size(),
        }
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> Result<usize> {
        match self {
            Self::Nop(ins) => Ok(ins.ins_size()),
            Self::Move(ins) => Ok(ins.ins_size()),
            Self::MoveWide(ins) => Ok(ins.ins_size()),
            Self::MoveObject(ins) => Ok(ins.ins_size()),
            Self::MoveResult(ins) => Ok(ins.ins_size()),
            Self::MoveResultWide(ins) => Ok(ins.ins_size()),
            Self::MoveException(ins) => Ok(ins.ins_size()),
            Self::MoveResultObject(ins) => Ok(ins.ins_size()),
            Self::ReturnVoid(ins) => Ok(ins.ins_size()),
            Self::Return(ins) => Ok(ins.ins_size()),
            Self::ReturnWide(ins) => Ok(ins.ins_size()),
            Self::ReturnObject(ins) => Ok(ins.ins_size()),
            Self::Const(ins) => Ok(ins.ins_size()),
            Self::ConstWide(ins) => Ok(ins.ins_size()),
            Self::ConstString(_) => Err(anyhow!(
                "Cannot get the size of a const-string size without knowing the string idx"
            )),
            Self::ConstClass(ins) => Ok(ins.ins_size()),
            Self::MonitorEnter(ins) => Ok(ins.ins_size()),
            Self::MonitorExit(ins) => Ok(ins.ins_size()),
            Self::CheckCast(ins) => Ok(ins.ins_size()),
            Self::InstanceOf(ins) => Ok(ins.ins_size()),
            Self::ArrayLength(ins) => Ok(ins.ins_size()),
            Self::NewInstance(ins) => Ok(ins.ins_size()),
            Self::NewArray(ins) => Ok(ins.ins_size()),
            Self::FilledNewArray(ins) => Ok(ins.ins_size()),
            Self::FillArrayData(ins) => Ok(ins.ins_size()),
            Self::Throw(ins) => Ok(ins.ins_size()),
            Self::Goto(_) => Err(anyhow!(
                "Cannot get the size of a goto without knowing the branch offset"
            )),
            Self::Switch(ins) => Ok(ins.ins_size()),
            Self::CmpLFloat(ins) => Ok(ins.ins_size()),
            Self::CmpGFloat(ins) => Ok(ins.ins_size()),
            Self::CmpLDouble(ins) => Ok(ins.ins_size()),
            Self::CmpGDouble(ins) => Ok(ins.ins_size()),
            Self::CmpLong(ins) => Ok(ins.ins_size()),
            Self::IfEq(ins) => Ok(ins.ins_size()),
            Self::IfNe(ins) => Ok(ins.ins_size()),
            Self::IfLt(ins) => Ok(ins.ins_size()),
            Self::IfGe(ins) => Ok(ins.ins_size()),
            Self::IfGt(ins) => Ok(ins.ins_size()),
            Self::IfLe(ins) => Ok(ins.ins_size()),
            Self::IfEqZ(ins) => Ok(ins.ins_size()),
            Self::IfNeZ(ins) => Ok(ins.ins_size()),
            Self::IfLtZ(ins) => Ok(ins.ins_size()),
            Self::IfGeZ(ins) => Ok(ins.ins_size()),
            Self::IfGtZ(ins) => Ok(ins.ins_size()),
            Self::IfLeZ(ins) => Ok(ins.ins_size()),
            Self::AGet(ins) => Ok(ins.ins_size()),
            Self::AGetWide(ins) => Ok(ins.ins_size()),
            Self::AGetObject(ins) => Ok(ins.ins_size()),
            Self::AGetBoolean(ins) => Ok(ins.ins_size()),
            Self::AGetByte(ins) => Ok(ins.ins_size()),
            Self::AGetChar(ins) => Ok(ins.ins_size()),
            Self::AGetShort(ins) => Ok(ins.ins_size()),
            Self::APut(ins) => Ok(ins.ins_size()),
            Self::APutWide(ins) => Ok(ins.ins_size()),
            Self::APutObject(ins) => Ok(ins.ins_size()),
            Self::APutBoolean(ins) => Ok(ins.ins_size()),
            Self::APutByte(ins) => Ok(ins.ins_size()),
            Self::APutChar(ins) => Ok(ins.ins_size()),
            Self::APutShort(ins) => Ok(ins.ins_size()),
            Self::IGet(ins) => Ok(ins.ins_size()),
            Self::IGetWide(ins) => Ok(ins.ins_size()),
            Self::IGetObject(ins) => Ok(ins.ins_size()),
            Self::IGetBoolean(ins) => Ok(ins.ins_size()),
            Self::IGetByte(ins) => Ok(ins.ins_size()),
            Self::IGetChar(ins) => Ok(ins.ins_size()),
            Self::IGetShort(ins) => Ok(ins.ins_size()),
            Self::IPut(ins) => Ok(ins.ins_size()),
            Self::IPutWide(ins) => Ok(ins.ins_size()),
            Self::IPutObject(ins) => Ok(ins.ins_size()),
            Self::IPutBoolean(ins) => Ok(ins.ins_size()),
            Self::IPutByte(ins) => Ok(ins.ins_size()),
            Self::IPutChar(ins) => Ok(ins.ins_size()),
            Self::IPutShort(ins) => Ok(ins.ins_size()),
            Self::SGet(ins) => Ok(ins.ins_size()),
            Self::SGetWide(ins) => Ok(ins.ins_size()),
            Self::SGetObject(ins) => Ok(ins.ins_size()),
            Self::SGetBoolean(ins) => Ok(ins.ins_size()),
            Self::SGetByte(ins) => Ok(ins.ins_size()),
            Self::SGetChar(ins) => Ok(ins.ins_size()),
            Self::SGetShort(ins) => Ok(ins.ins_size()),
            Self::SPut(ins) => Ok(ins.ins_size()),
            Self::SPutWide(ins) => Ok(ins.ins_size()),
            Self::SPutObject(ins) => Ok(ins.ins_size()),
            Self::SPutBoolean(ins) => Ok(ins.ins_size()),
            Self::SPutByte(ins) => Ok(ins.ins_size()),
            Self::SPutChar(ins) => Ok(ins.ins_size()),
            Self::SPutShort(ins) => Ok(ins.ins_size()),
            Self::InvokeVirtual(ins) => Ok(ins.ins_size()),
            Self::InvokeSuper(ins) => Ok(ins.ins_size()),
            Self::InvokeDirect(ins) => Ok(ins.ins_size()),
            Self::InvokeStatic(ins) => Ok(ins.ins_size()),
            Self::InvokeInterface(ins) => Ok(ins.ins_size()),
            Self::NegInt(ins) => Ok(ins.ins_size()),
            Self::NotInt(ins) => Ok(ins.ins_size()),
            Self::NegLong(ins) => Ok(ins.ins_size()),
            Self::NotLong(ins) => Ok(ins.ins_size()),
            Self::NegFloat(ins) => Ok(ins.ins_size()),
            Self::NegDouble(ins) => Ok(ins.ins_size()),
            Self::IntToLong(ins) => Ok(ins.ins_size()),
            Self::IntToFloat(ins) => Ok(ins.ins_size()),
            Self::IntToDouble(ins) => Ok(ins.ins_size()),
            Self::LongToInt(ins) => Ok(ins.ins_size()),
            Self::LongToFloat(ins) => Ok(ins.ins_size()),
            Self::LongToDouble(ins) => Ok(ins.ins_size()),
            Self::FloatToInt(ins) => Ok(ins.ins_size()),
            Self::FloatToLong(ins) => Ok(ins.ins_size()),
            Self::FloatToDouble(ins) => Ok(ins.ins_size()),
            Self::DoubleToInt(ins) => Ok(ins.ins_size()),
            Self::DoubleToLong(ins) => Ok(ins.ins_size()),
            Self::DoubleToFloat(ins) => Ok(ins.ins_size()),
            Self::IntToByte(ins) => Ok(ins.ins_size()),
            Self::IntToChar(ins) => Ok(ins.ins_size()),
            Self::IntToShort(ins) => Ok(ins.ins_size()),
            Self::AddInt(ins) => Ok(ins.ins_size()),
            Self::SubInt(ins) => Ok(ins.ins_size()),
            Self::MulInt(ins) => Ok(ins.ins_size()),
            Self::DivInt(ins) => Ok(ins.ins_size()),
            Self::RemInt(ins) => Ok(ins.ins_size()),
            Self::AndInt(ins) => Ok(ins.ins_size()),
            Self::OrInt(ins) => Ok(ins.ins_size()),
            Self::XorInt(ins) => Ok(ins.ins_size()),
            Self::ShlInt(ins) => Ok(ins.ins_size()),
            Self::ShrInt(ins) => Ok(ins.ins_size()),
            Self::UshrInt(ins) => Ok(ins.ins_size()),
            Self::AddLong(ins) => Ok(ins.ins_size()),
            Self::SubLong(ins) => Ok(ins.ins_size()),
            Self::MulLong(ins) => Ok(ins.ins_size()),
            Self::DivLong(ins) => Ok(ins.ins_size()),
            Self::RemLong(ins) => Ok(ins.ins_size()),
            Self::AndLong(ins) => Ok(ins.ins_size()),
            Self::OrLong(ins) => Ok(ins.ins_size()),
            Self::XorLong(ins) => Ok(ins.ins_size()),
            Self::ShlLong(ins) => Ok(ins.ins_size()),
            Self::ShrLong(ins) => Ok(ins.ins_size()),
            Self::UshrLong(ins) => Ok(ins.ins_size()),
            Self::AddFloat(ins) => Ok(ins.ins_size()),
            Self::SubFloat(ins) => Ok(ins.ins_size()),
            Self::MulFloat(ins) => Ok(ins.ins_size()),
            Self::DivFloat(ins) => Ok(ins.ins_size()),
            Self::RemFloat(ins) => Ok(ins.ins_size()),
            Self::AddDouble(ins) => Ok(ins.ins_size()),
            Self::SubDouble(ins) => Ok(ins.ins_size()),
            Self::MulDouble(ins) => Ok(ins.ins_size()),
            Self::DivDouble(ins) => Ok(ins.ins_size()),
            Self::RemDouble(ins) => Ok(ins.ins_size()),
            Self::AddInt2Addr(ins) => Ok(ins.ins_size()),
            Self::SubInt2Addr(ins) => Ok(ins.ins_size()),
            Self::MulInt2Addr(ins) => Ok(ins.ins_size()),
            Self::DivInt2Addr(ins) => Ok(ins.ins_size()),
            Self::RemInt2Addr(ins) => Ok(ins.ins_size()),
            Self::AndInt2Addr(ins) => Ok(ins.ins_size()),
            Self::OrInt2Addr(ins) => Ok(ins.ins_size()),
            Self::XorInt2Addr(ins) => Ok(ins.ins_size()),
            Self::ShlInt2Addr(ins) => Ok(ins.ins_size()),
            Self::ShrInt2Addr(ins) => Ok(ins.ins_size()),
            Self::UshrInt2Addr(ins) => Ok(ins.ins_size()),
            Self::AddLong2Addr(ins) => Ok(ins.ins_size()),
            Self::SubLong2Addr(ins) => Ok(ins.ins_size()),
            Self::MulLong2Addr(ins) => Ok(ins.ins_size()),
            Self::DivLong2Addr(ins) => Ok(ins.ins_size()),
            Self::RemLong2Addr(ins) => Ok(ins.ins_size()),
            Self::AndLong2Addr(ins) => Ok(ins.ins_size()),
            Self::OrLong2Addr(ins) => Ok(ins.ins_size()),
            Self::XorLong2Addr(ins) => Ok(ins.ins_size()),
            Self::ShlLong2Addr(ins) => Ok(ins.ins_size()),
            Self::ShrLong2Addr(ins) => Ok(ins.ins_size()),
            Self::UshrLong2Addr(ins) => Ok(ins.ins_size()),
            Self::AddFloat2Addr(ins) => Ok(ins.ins_size()),
            Self::SubFloat2Addr(ins) => Ok(ins.ins_size()),
            Self::MulFloat2Addr(ins) => Ok(ins.ins_size()),
            Self::DivFloat2Addr(ins) => Ok(ins.ins_size()),
            Self::RemFloat2Addr(ins) => Ok(ins.ins_size()),
            Self::AddDouble2Addr(ins) => Ok(ins.ins_size()),
            Self::SubDouble2Addr(ins) => Ok(ins.ins_size()),
            Self::MulDouble2Addr(ins) => Ok(ins.ins_size()),
            Self::DivDouble2Addr(ins) => Ok(ins.ins_size()),
            Self::RemDouble2Addr(ins) => Ok(ins.ins_size()),
            Self::AddIntLit(ins) => Ok(ins.ins_size()),
            Self::RsubIntLit(ins) => Ok(ins.ins_size()),
            Self::MulIntLit(ins) => Ok(ins.ins_size()),
            Self::DivIntLit(ins) => Ok(ins.ins_size()),
            Self::RemIntLit(ins) => Ok(ins.ins_size()),
            Self::AndIntLit(ins) => Ok(ins.ins_size()),
            Self::OrIntLit(ins) => Ok(ins.ins_size()),
            Self::XorIntLit(ins) => Ok(ins.ins_size()),
            Self::ShlIntLit(ins) => Ok(ins.ins_size()),
            Self::ShrIntLit(ins) => Ok(ins.ins_size()),
            Self::UshrIntLit(ins) => Ok(ins.ins_size()),
            Self::InvokePolymorphic(ins) => Ok(ins.ins_size()),
            Self::InvokeCustom(ins) => Ok(ins.ins_size()),
            Self::ConstMethodHandle(ins) => Ok(ins.ins_size()),
            Self::ConstMethodType(ins) => Ok(ins.ins_size()),
            Self::Try(ins) => Ok(ins.ins_size()),
            Self::Label(ins) => Ok(ins.ins_size()),
        }
    }

    /// Return the raw instruction ([`InsFormat`]) when it does not need additionnal data.
    ///
    /// # Warning
    ///
    /// Some instruction require additionnal data, or do not have associated instruction format.
    /// This method will return an error for thos instruction.
    ///
    /// This method should be used in a match statetement in the default branch, for brevity.
    ///
    /// # Variants that do not implement this method:
    ///
    /// - `ConstString`
    /// - `ConstClass`
    /// - `CheckCast`
    /// - `InstanceOf`
    /// - `NewInstance`
    /// - `NewArray`
    /// - `FilledNewArray`
    /// - `FillArrayData`
    /// - `Goto`
    /// - `Switch`
    /// - `IfEq`
    /// - `IfNe`
    /// - `IfLt`
    /// - `IfGe`
    /// - `IfGt`
    /// - `IfLe`
    /// - `IfEqZ`
    /// - `IfNeZ`
    /// - `IfLtZ`
    /// - `IfGeZ`
    /// - `IfGtZ`
    /// - `IfLeZ`
    /// - `IGet`
    /// - `IGetWide`
    /// - `IGetObject`
    /// - `IGetBoolean`
    /// - `IGetByte`
    /// - `IGetChar`
    /// - `IGetShort`
    /// - `IPut`
    /// - `IPutWide`
    /// - `IPutObject`
    /// - `IPutBoolean`
    /// - `IPutByte`
    /// - `IPutChar`
    /// - `IPutShort`
    /// - `SGet`
    /// - `SGetWide`
    /// - `SGetObject`
    /// - `SGetBoolean`
    /// - `SGetByte`
    /// - `SGetChar`
    /// - `SGetShort`
    /// - `SPut`
    /// - `SPutWide`
    /// - `SPutObject`
    /// - `SPutBoolean`
    /// - `SPutByte`
    /// - `SPutChar`
    /// - `SPutShort`
    /// - `InvokeVirtual`
    /// - `InvokeSuper`
    /// - `InvokeDirect`
    /// - `InvokeStatic`
    /// - `InvokeInterface`
    /// - `InvokePolymorphic`
    /// - `InvokeCustom`
    /// - `ConstMethodHandle`
    /// - `ConstMethodType`
    /// - `Try`
    /// - `Label`
    pub fn get_raw_ins(&self) -> Result<InsFormat> {
        match self {
            Self::Nop(ins) => Ok(ins.get_raw_ins()),
            Self::Move(ins) => Ok(ins.get_raw_ins()),
            Self::MoveWide(ins) => Ok(ins.get_raw_ins()),
            Self::MoveObject(ins) => Ok(ins.get_raw_ins()),
            Self::MoveResult(ins) => Ok(ins.get_raw_ins()),
            Self::MoveResultWide(ins) => Ok(ins.get_raw_ins()),
            Self::MoveException(ins) => Ok(ins.get_raw_ins()),
            Self::MoveResultObject(ins) => Ok(ins.get_raw_ins()),
            Self::ReturnVoid(ins) => Ok(ins.get_raw_ins()),
            Self::Return(ins) => Ok(ins.get_raw_ins()),
            Self::ReturnWide(ins) => Ok(ins.get_raw_ins()),
            Self::ReturnObject(ins) => Ok(ins.get_raw_ins()),
            Self::Const(ins) => Ok(ins.get_raw_ins()),
            Self::ConstWide(ins) => Ok(ins.get_raw_ins()),
            Self::ConstString(_) => Err(anyhow!(
                "Cannot get the raw instruction of a const-string without knowing the string idx"
            )),
            Self::ConstClass(_) => Err(anyhow!(
                "Cannot get the raw instruction of a const-class without knowing the class idx"
            )),
            Self::MonitorEnter(ins) => Ok(ins.get_raw_ins()),
            Self::MonitorExit(ins) => Ok(ins.get_raw_ins()),
            Self::CheckCast(_) => Err(anyhow!(
                "Cannot get the raw instruction of a check-cast without knowing the type idx"
            )),
            Self::InstanceOf(_) => Err(anyhow!(
                "Cannot get the raw instruction of a instance-of without knowing the type idx"
            )),
            Self::ArrayLength(ins) => Ok(ins.get_raw_ins()),
            Self::NewInstance(_) => Err(anyhow!(
                "Cannot get the raw instruction of a new-instance without knowing the class idx"
            )),
            Self::NewArray(_) => Err(anyhow!(
                "Cannot get the raw instruction of a new-array without knowing the type idx"
            )),
            Self::FilledNewArray(_) => Err(anyhow!(
                "Cannot get the raw instruction of a filled-new-array without knowing the type idx"
            )),
            Self::FillArrayData(_) => Err(anyhow!(
                "Cannot get the raw instruction of a fill-array-data without knowing the offset \
                    to the -fill-array-data-payload"
            )),
            Self::Throw(ins) => Ok(ins.get_raw_ins()),
            Self::Goto(_) => Err(anyhow!(
                "Cannot get the size of a goto without knowing the branch offset"
            )),
            Self::Switch(_) => Err(anyhow!(
                "Cannot get the raw instruction of a switch without knowing the offset \
                    to the packed/sparse-switch-payload"
            )),
            Self::CmpLFloat(ins) => Ok(ins.get_raw_ins()),
            Self::CmpGFloat(ins) => Ok(ins.get_raw_ins()),
            Self::CmpLDouble(ins) => Ok(ins.get_raw_ins()),
            Self::CmpGDouble(ins) => Ok(ins.get_raw_ins()),
            Self::CmpLong(ins) => Ok(ins.get_raw_ins()),
            Self::IfEq(_) => Err(anyhow!(
                "Cannot get the raw instruction of a if-eq without knowing \
                    the offset to the branch"
            )),
            Self::IfNe(_) => Err(anyhow!(
                "Cannot get the raw instruction of a if-ne without knowing \
                the offset to the branch"
            )),
            Self::IfLt(_) => Err(anyhow!(
                "Cannot get the raw instruction of a if-lt without knowing \
                the offset to the branch"
            )),
            Self::IfGe(_) => Err(anyhow!(
                "Cannot get the raw instruction of a if-ge without knowing \
                the offset to the branch"
            )),
            Self::IfGt(_) => Err(anyhow!(
                "Cannot get the raw instruction of a if-gt without knowing \
                the offset to the branch"
            )),
            Self::IfLe(_) => Err(anyhow!(
                "Cannot get the raw instruction of a if-le without knowing \
                the offset to the branch"
            )),
            Self::IfEqZ(_) => Err(anyhow!(
                "Cannot get the raw instruction of a if-eqz without knowing \
                the offset to the branch"
            )),
            Self::IfNeZ(_) => Err(anyhow!(
                "Cannot get the raw instruction of a if-nez without knowing \
                the offset to the branch"
            )),
            Self::IfLtZ(_) => Err(anyhow!(
                "Cannot get the raw instruction of a if-ltz without knowing \
                the offset to the branch"
            )),
            Self::IfGeZ(_) => Err(anyhow!(
                "Cannot get the raw instruction of a if-gez without knowing \
                the offset to the branch"
            )),
            Self::IfGtZ(_) => Err(anyhow!(
                "Cannot get the raw instruction of a if-gtz without knowing \
                the offset to the branch"
            )),
            Self::IfLeZ(_) => Err(anyhow!(
                "Cannot get the raw instruction of a if-lez without knowing \
                the offset to the branch"
            )),
            Self::AGet(ins) => Ok(ins.get_raw_ins()),
            Self::AGetWide(ins) => Ok(ins.get_raw_ins()),
            Self::AGetObject(ins) => Ok(ins.get_raw_ins()),
            Self::AGetBoolean(ins) => Ok(ins.get_raw_ins()),
            Self::AGetByte(ins) => Ok(ins.get_raw_ins()),
            Self::AGetChar(ins) => Ok(ins.get_raw_ins()),
            Self::AGetShort(ins) => Ok(ins.get_raw_ins()),
            Self::APut(ins) => Ok(ins.get_raw_ins()),
            Self::APutWide(ins) => Ok(ins.get_raw_ins()),
            Self::APutObject(ins) => Ok(ins.get_raw_ins()),
            Self::APutBoolean(ins) => Ok(ins.get_raw_ins()),
            Self::APutByte(ins) => Ok(ins.get_raw_ins()),
            Self::APutChar(ins) => Ok(ins.get_raw_ins()),
            Self::APutShort(ins) => Ok(ins.get_raw_ins()),
            Self::IGet(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iget without knowing the field idx"
            )),
            Self::IGetWide(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iget-wide without knowing the field idx"
            )),
            Self::IGetObject(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iget-object without knowing the field idx"
            )),
            Self::IGetBoolean(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iget-boolean without knowing the field idx"
            )),
            Self::IGetByte(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iget-byte without knowing the field idx"
            )),
            Self::IGetChar(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iget-char without knowing the field idx"
            )),
            Self::IGetShort(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iget-short without knowing the field idx"
            )),
            Self::IPut(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iput without knowing the field idx"
            )),
            Self::IPutWide(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iput-wide without knowing the field idx"
            )),
            Self::IPutObject(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iput-object without knowing the field idx"
            )),
            Self::IPutBoolean(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iput-boolean without knowing the field idx"
            )),
            Self::IPutByte(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iput-byte without knowing the field idx"
            )),
            Self::IPutChar(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iput-char without knowing the field idx"
            )),
            Self::IPutShort(_) => Err(anyhow!(
                "Cannot get the raw instruction of a iput-short without knowing the field idx"
            )),
            Self::SGet(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sget without knowing the field idx"
            )),
            Self::SGetWide(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sget-wide without knowing the field idx"
            )),
            Self::SGetObject(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sget-object without knowing the field idx"
            )),
            Self::SGetBoolean(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sget-boolean without knowing the field idx"
            )),
            Self::SGetByte(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sget-byte without knowing the field idx"
            )),
            Self::SGetChar(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sget-char without knowing the field idx"
            )),
            Self::SGetShort(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sget-short without knowing the field idx"
            )),
            Self::SPut(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sput without knowing the field idx"
            )),
            Self::SPutWide(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sput-wide without knowing the field idx"
            )),
            Self::SPutObject(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sput-object without knowing the field idx"
            )),
            Self::SPutBoolean(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sput-boolean without knowing the field idx"
            )),
            Self::SPutByte(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sput-byte without knowing the field idx"
            )),
            Self::SPutChar(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sput-char without knowing the field idx"
            )),
            Self::SPutShort(_) => Err(anyhow!(
                "Cannot get the raw instruction of a sput-short without knowing the field idx"
            )),
            Self::InvokeVirtual(_) => Err(anyhow!(
                "Cannot get the raw instruction of a invoke-virtual without knowing \
                the method idx"
            )),
            Self::InvokeSuper(_) => Err(anyhow!(
                "Cannot get the raw instruction of a invoke-super without knowing \
                the method idx"
            )),
            Self::InvokeDirect(_) => Err(anyhow!(
                "Cannot get the raw instruction of a invoke-direct without knowing \
                the method idx"
            )),
            Self::InvokeStatic(_) => Err(anyhow!(
                "Cannot get the raw instruction of a invoke-static without knowing \
                the method idx"
            )),
            Self::InvokeInterface(_) => Err(anyhow!(
                "Cannot get the raw instruction of a invoke-interface without knowing \
                the method idx"
            )),
            Self::NegInt(ins) => Ok(ins.get_raw_ins()),
            Self::NotInt(ins) => Ok(ins.get_raw_ins()),
            Self::NegLong(ins) => Ok(ins.get_raw_ins()),
            Self::NotLong(ins) => Ok(ins.get_raw_ins()),
            Self::NegFloat(ins) => Ok(ins.get_raw_ins()),
            Self::NegDouble(ins) => Ok(ins.get_raw_ins()),
            Self::IntToLong(ins) => Ok(ins.get_raw_ins()),
            Self::IntToFloat(ins) => Ok(ins.get_raw_ins()),
            Self::IntToDouble(ins) => Ok(ins.get_raw_ins()),
            Self::LongToInt(ins) => Ok(ins.get_raw_ins()),
            Self::LongToFloat(ins) => Ok(ins.get_raw_ins()),
            Self::LongToDouble(ins) => Ok(ins.get_raw_ins()),
            Self::FloatToInt(ins) => Ok(ins.get_raw_ins()),
            Self::FloatToLong(ins) => Ok(ins.get_raw_ins()),
            Self::FloatToDouble(ins) => Ok(ins.get_raw_ins()),
            Self::DoubleToInt(ins) => Ok(ins.get_raw_ins()),
            Self::DoubleToLong(ins) => Ok(ins.get_raw_ins()),
            Self::DoubleToFloat(ins) => Ok(ins.get_raw_ins()),
            Self::IntToByte(ins) => Ok(ins.get_raw_ins()),
            Self::IntToChar(ins) => Ok(ins.get_raw_ins()),
            Self::IntToShort(ins) => Ok(ins.get_raw_ins()),
            Self::AddInt(ins) => Ok(ins.get_raw_ins()),
            Self::SubInt(ins) => Ok(ins.get_raw_ins()),
            Self::MulInt(ins) => Ok(ins.get_raw_ins()),
            Self::DivInt(ins) => Ok(ins.get_raw_ins()),
            Self::RemInt(ins) => Ok(ins.get_raw_ins()),
            Self::AndInt(ins) => Ok(ins.get_raw_ins()),
            Self::OrInt(ins) => Ok(ins.get_raw_ins()),
            Self::XorInt(ins) => Ok(ins.get_raw_ins()),
            Self::ShlInt(ins) => Ok(ins.get_raw_ins()),
            Self::ShrInt(ins) => Ok(ins.get_raw_ins()),
            Self::UshrInt(ins) => Ok(ins.get_raw_ins()),
            Self::AddLong(ins) => Ok(ins.get_raw_ins()),
            Self::SubLong(ins) => Ok(ins.get_raw_ins()),
            Self::MulLong(ins) => Ok(ins.get_raw_ins()),
            Self::DivLong(ins) => Ok(ins.get_raw_ins()),
            Self::RemLong(ins) => Ok(ins.get_raw_ins()),
            Self::AndLong(ins) => Ok(ins.get_raw_ins()),
            Self::OrLong(ins) => Ok(ins.get_raw_ins()),
            Self::XorLong(ins) => Ok(ins.get_raw_ins()),
            Self::ShlLong(ins) => Ok(ins.get_raw_ins()),
            Self::ShrLong(ins) => Ok(ins.get_raw_ins()),
            Self::UshrLong(ins) => Ok(ins.get_raw_ins()),
            Self::AddFloat(ins) => Ok(ins.get_raw_ins()),
            Self::SubFloat(ins) => Ok(ins.get_raw_ins()),
            Self::MulFloat(ins) => Ok(ins.get_raw_ins()),
            Self::DivFloat(ins) => Ok(ins.get_raw_ins()),
            Self::RemFloat(ins) => Ok(ins.get_raw_ins()),
            Self::AddDouble(ins) => Ok(ins.get_raw_ins()),
            Self::SubDouble(ins) => Ok(ins.get_raw_ins()),
            Self::MulDouble(ins) => Ok(ins.get_raw_ins()),
            Self::DivDouble(ins) => Ok(ins.get_raw_ins()),
            Self::RemDouble(ins) => Ok(ins.get_raw_ins()),
            Self::AddInt2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::SubInt2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::MulInt2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::DivInt2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::RemInt2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::AndInt2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::OrInt2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::XorInt2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::ShlInt2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::ShrInt2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::UshrInt2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::AddLong2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::SubLong2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::MulLong2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::DivLong2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::RemLong2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::AndLong2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::OrLong2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::XorLong2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::ShlLong2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::ShrLong2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::UshrLong2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::AddFloat2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::SubFloat2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::MulFloat2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::DivFloat2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::RemFloat2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::AddDouble2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::SubDouble2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::MulDouble2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::DivDouble2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::RemDouble2Addr(ins) => Ok(ins.get_raw_ins()),
            Self::AddIntLit(ins) => Ok(ins.get_raw_ins()),
            Self::RsubIntLit(ins) => Ok(ins.get_raw_ins()),
            Self::MulIntLit(ins) => Ok(ins.get_raw_ins()),
            Self::DivIntLit(ins) => Ok(ins.get_raw_ins()),
            Self::RemIntLit(ins) => Ok(ins.get_raw_ins()),
            Self::AndIntLit(ins) => Ok(ins.get_raw_ins()),
            Self::OrIntLit(ins) => Ok(ins.get_raw_ins()),
            Self::XorIntLit(ins) => Ok(ins.get_raw_ins()),
            Self::ShlIntLit(ins) => Ok(ins.get_raw_ins()),
            Self::ShrIntLit(ins) => Ok(ins.get_raw_ins()),
            Self::UshrIntLit(ins) => Ok(ins.get_raw_ins()),
            Self::InvokePolymorphic(_) => Err(anyhow!(
                "Cannot get the raw instruction of a invoke-polymorphic without knowing \
                    the method idx and proto idx"
            )),
            Self::InvokeCustom(_) => Err(anyhow!(
                "Cannot get the raw instruction of a invoke-custom without knowing the \
                call site idx"
            )),
            Self::ConstMethodHandle(_) => Err(anyhow!(
                "Cannot get the raw instruction of a const-method-handle \
                    without knowing the method handle idx"
            )),
            Self::ConstMethodType(_) => Err(anyhow!(
                "Cannot get the raw instruction of a const-method-type \
                    without knowing the proto idx"
            )),
            Self::Try(_) => Err(anyhow!(
                "Try instruction cannot be converted to raw instruction"
            )),
            Self::Label(_) => Err(anyhow!("Label cannot be convertedto raw instruction")),
        }
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        match self {
            Self::Nop(ins) => ins.get_all_strings(),
            Self::Move(ins) => ins.get_all_strings(),
            Self::MoveWide(ins) => ins.get_all_strings(),
            Self::MoveObject(ins) => ins.get_all_strings(),
            Self::MoveResult(ins) => ins.get_all_strings(),
            Self::MoveResultWide(ins) => ins.get_all_strings(),
            Self::MoveException(ins) => ins.get_all_strings(),
            Self::MoveResultObject(ins) => ins.get_all_strings(),
            Self::ReturnVoid(ins) => ins.get_all_strings(),
            Self::Return(ins) => ins.get_all_strings(),
            Self::ReturnWide(ins) => ins.get_all_strings(),
            Self::ReturnObject(ins) => ins.get_all_strings(),
            Self::Const(ins) => ins.get_all_strings(),
            Self::ConstWide(ins) => ins.get_all_strings(),
            Self::ConstString(ins) => ins.get_all_strings(),
            Self::ConstClass(ins) => ins.get_all_strings(),
            Self::MonitorEnter(ins) => ins.get_all_strings(),
            Self::MonitorExit(ins) => ins.get_all_strings(),
            Self::CheckCast(ins) => ins.get_all_strings(),
            Self::InstanceOf(ins) => ins.get_all_strings(),
            Self::ArrayLength(ins) => ins.get_all_strings(),
            Self::NewInstance(ins) => ins.get_all_strings(),
            Self::NewArray(ins) => ins.get_all_strings(),
            Self::FilledNewArray(ins) => ins.get_all_strings(),
            Self::FillArrayData(ins) => ins.get_all_strings(),
            Self::Throw(ins) => ins.get_all_strings(),
            Self::Goto(ins) => ins.get_all_strings(),
            Self::Switch(ins) => ins.get_all_strings(),
            Self::CmpLFloat(ins) => ins.get_all_strings(),
            Self::CmpGFloat(ins) => ins.get_all_strings(),
            Self::CmpLDouble(ins) => ins.get_all_strings(),
            Self::CmpGDouble(ins) => ins.get_all_strings(),
            Self::CmpLong(ins) => ins.get_all_strings(),
            Self::IfEq(ins) => ins.get_all_strings(),
            Self::IfNe(ins) => ins.get_all_strings(),
            Self::IfLt(ins) => ins.get_all_strings(),
            Self::IfGe(ins) => ins.get_all_strings(),
            Self::IfGt(ins) => ins.get_all_strings(),
            Self::IfLe(ins) => ins.get_all_strings(),
            Self::IfEqZ(ins) => ins.get_all_strings(),
            Self::IfNeZ(ins) => ins.get_all_strings(),
            Self::IfLtZ(ins) => ins.get_all_strings(),
            Self::IfGeZ(ins) => ins.get_all_strings(),
            Self::IfGtZ(ins) => ins.get_all_strings(),
            Self::IfLeZ(ins) => ins.get_all_strings(),
            Self::AGet(ins) => ins.get_all_strings(),
            Self::AGetWide(ins) => ins.get_all_strings(),
            Self::AGetObject(ins) => ins.get_all_strings(),
            Self::AGetBoolean(ins) => ins.get_all_strings(),
            Self::AGetByte(ins) => ins.get_all_strings(),
            Self::AGetChar(ins) => ins.get_all_strings(),
            Self::AGetShort(ins) => ins.get_all_strings(),
            Self::APut(ins) => ins.get_all_strings(),
            Self::APutWide(ins) => ins.get_all_strings(),
            Self::APutObject(ins) => ins.get_all_strings(),
            Self::APutBoolean(ins) => ins.get_all_strings(),
            Self::APutByte(ins) => ins.get_all_strings(),
            Self::APutChar(ins) => ins.get_all_strings(),
            Self::APutShort(ins) => ins.get_all_strings(),
            Self::IGet(ins) => ins.get_all_strings(),
            Self::IGetWide(ins) => ins.get_all_strings(),
            Self::IGetObject(ins) => ins.get_all_strings(),
            Self::IGetBoolean(ins) => ins.get_all_strings(),
            Self::IGetByte(ins) => ins.get_all_strings(),
            Self::IGetChar(ins) => ins.get_all_strings(),
            Self::IGetShort(ins) => ins.get_all_strings(),
            Self::IPut(ins) => ins.get_all_strings(),
            Self::IPutWide(ins) => ins.get_all_strings(),
            Self::IPutObject(ins) => ins.get_all_strings(),
            Self::IPutBoolean(ins) => ins.get_all_strings(),
            Self::IPutByte(ins) => ins.get_all_strings(),
            Self::IPutChar(ins) => ins.get_all_strings(),
            Self::IPutShort(ins) => ins.get_all_strings(),
            Self::SGet(ins) => ins.get_all_strings(),
            Self::SGetWide(ins) => ins.get_all_strings(),
            Self::SGetObject(ins) => ins.get_all_strings(),
            Self::SGetBoolean(ins) => ins.get_all_strings(),
            Self::SGetByte(ins) => ins.get_all_strings(),
            Self::SGetChar(ins) => ins.get_all_strings(),
            Self::SGetShort(ins) => ins.get_all_strings(),
            Self::SPut(ins) => ins.get_all_strings(),
            Self::SPutWide(ins) => ins.get_all_strings(),
            Self::SPutObject(ins) => ins.get_all_strings(),
            Self::SPutBoolean(ins) => ins.get_all_strings(),
            Self::SPutByte(ins) => ins.get_all_strings(),
            Self::SPutChar(ins) => ins.get_all_strings(),
            Self::SPutShort(ins) => ins.get_all_strings(),
            Self::InvokeVirtual(ins) => ins.get_all_strings(),
            Self::InvokeSuper(ins) => ins.get_all_strings(),
            Self::InvokeDirect(ins) => ins.get_all_strings(),
            Self::InvokeStatic(ins) => ins.get_all_strings(),
            Self::InvokeInterface(ins) => ins.get_all_strings(),
            Self::NegInt(ins) => ins.get_all_strings(),
            Self::NotInt(ins) => ins.get_all_strings(),
            Self::NegLong(ins) => ins.get_all_strings(),
            Self::NotLong(ins) => ins.get_all_strings(),
            Self::NegFloat(ins) => ins.get_all_strings(),
            Self::NegDouble(ins) => ins.get_all_strings(),
            Self::IntToLong(ins) => ins.get_all_strings(),
            Self::IntToFloat(ins) => ins.get_all_strings(),
            Self::IntToDouble(ins) => ins.get_all_strings(),
            Self::LongToInt(ins) => ins.get_all_strings(),
            Self::LongToFloat(ins) => ins.get_all_strings(),
            Self::LongToDouble(ins) => ins.get_all_strings(),
            Self::FloatToInt(ins) => ins.get_all_strings(),
            Self::FloatToLong(ins) => ins.get_all_strings(),
            Self::FloatToDouble(ins) => ins.get_all_strings(),
            Self::DoubleToInt(ins) => ins.get_all_strings(),
            Self::DoubleToLong(ins) => ins.get_all_strings(),
            Self::DoubleToFloat(ins) => ins.get_all_strings(),
            Self::IntToByte(ins) => ins.get_all_strings(),
            Self::IntToChar(ins) => ins.get_all_strings(),
            Self::IntToShort(ins) => ins.get_all_strings(),
            Self::AddInt(ins) => ins.get_all_strings(),
            Self::SubInt(ins) => ins.get_all_strings(),
            Self::MulInt(ins) => ins.get_all_strings(),
            Self::DivInt(ins) => ins.get_all_strings(),
            Self::RemInt(ins) => ins.get_all_strings(),
            Self::AndInt(ins) => ins.get_all_strings(),
            Self::OrInt(ins) => ins.get_all_strings(),
            Self::XorInt(ins) => ins.get_all_strings(),
            Self::ShlInt(ins) => ins.get_all_strings(),
            Self::ShrInt(ins) => ins.get_all_strings(),
            Self::UshrInt(ins) => ins.get_all_strings(),
            Self::AddLong(ins) => ins.get_all_strings(),
            Self::SubLong(ins) => ins.get_all_strings(),
            Self::MulLong(ins) => ins.get_all_strings(),
            Self::DivLong(ins) => ins.get_all_strings(),
            Self::RemLong(ins) => ins.get_all_strings(),
            Self::AndLong(ins) => ins.get_all_strings(),
            Self::OrLong(ins) => ins.get_all_strings(),
            Self::XorLong(ins) => ins.get_all_strings(),
            Self::ShlLong(ins) => ins.get_all_strings(),
            Self::ShrLong(ins) => ins.get_all_strings(),
            Self::UshrLong(ins) => ins.get_all_strings(),
            Self::AddFloat(ins) => ins.get_all_strings(),
            Self::SubFloat(ins) => ins.get_all_strings(),
            Self::MulFloat(ins) => ins.get_all_strings(),
            Self::DivFloat(ins) => ins.get_all_strings(),
            Self::RemFloat(ins) => ins.get_all_strings(),
            Self::AddDouble(ins) => ins.get_all_strings(),
            Self::SubDouble(ins) => ins.get_all_strings(),
            Self::MulDouble(ins) => ins.get_all_strings(),
            Self::DivDouble(ins) => ins.get_all_strings(),
            Self::RemDouble(ins) => ins.get_all_strings(),
            Self::AddInt2Addr(ins) => ins.get_all_strings(),
            Self::SubInt2Addr(ins) => ins.get_all_strings(),
            Self::MulInt2Addr(ins) => ins.get_all_strings(),
            Self::DivInt2Addr(ins) => ins.get_all_strings(),
            Self::RemInt2Addr(ins) => ins.get_all_strings(),
            Self::AndInt2Addr(ins) => ins.get_all_strings(),
            Self::OrInt2Addr(ins) => ins.get_all_strings(),
            Self::XorInt2Addr(ins) => ins.get_all_strings(),
            Self::ShlInt2Addr(ins) => ins.get_all_strings(),
            Self::ShrInt2Addr(ins) => ins.get_all_strings(),
            Self::UshrInt2Addr(ins) => ins.get_all_strings(),
            Self::AddLong2Addr(ins) => ins.get_all_strings(),
            Self::SubLong2Addr(ins) => ins.get_all_strings(),
            Self::MulLong2Addr(ins) => ins.get_all_strings(),
            Self::DivLong2Addr(ins) => ins.get_all_strings(),
            Self::RemLong2Addr(ins) => ins.get_all_strings(),
            Self::AndLong2Addr(ins) => ins.get_all_strings(),
            Self::OrLong2Addr(ins) => ins.get_all_strings(),
            Self::XorLong2Addr(ins) => ins.get_all_strings(),
            Self::ShlLong2Addr(ins) => ins.get_all_strings(),
            Self::ShrLong2Addr(ins) => ins.get_all_strings(),
            Self::UshrLong2Addr(ins) => ins.get_all_strings(),
            Self::AddFloat2Addr(ins) => ins.get_all_strings(),
            Self::SubFloat2Addr(ins) => ins.get_all_strings(),
            Self::MulFloat2Addr(ins) => ins.get_all_strings(),
            Self::DivFloat2Addr(ins) => ins.get_all_strings(),
            Self::RemFloat2Addr(ins) => ins.get_all_strings(),
            Self::AddDouble2Addr(ins) => ins.get_all_strings(),
            Self::SubDouble2Addr(ins) => ins.get_all_strings(),
            Self::MulDouble2Addr(ins) => ins.get_all_strings(),
            Self::DivDouble2Addr(ins) => ins.get_all_strings(),
            Self::RemDouble2Addr(ins) => ins.get_all_strings(),
            Self::AddIntLit(ins) => ins.get_all_strings(),
            Self::RsubIntLit(ins) => ins.get_all_strings(),
            Self::MulIntLit(ins) => ins.get_all_strings(),
            Self::DivIntLit(ins) => ins.get_all_strings(),
            Self::RemIntLit(ins) => ins.get_all_strings(),
            Self::AndIntLit(ins) => ins.get_all_strings(),
            Self::OrIntLit(ins) => ins.get_all_strings(),
            Self::XorIntLit(ins) => ins.get_all_strings(),
            Self::ShlIntLit(ins) => ins.get_all_strings(),
            Self::ShrIntLit(ins) => ins.get_all_strings(),
            Self::UshrIntLit(ins) => ins.get_all_strings(),
            Self::InvokePolymorphic(ins) => ins.get_all_strings(),
            Self::InvokeCustom(ins) => ins.get_all_strings(),
            Self::ConstMethodHandle(ins) => ins.get_all_strings(),
            Self::ConstMethodType(ins) => ins.get_all_strings(),
            Self::Try(ins) => ins.get_all_strings(),
            Self::Label(ins) => ins.get_all_strings(),
        }
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        match self {
            Self::Nop(ins) => ins.get_all_types(),
            Self::Move(ins) => ins.get_all_types(),
            Self::MoveWide(ins) => ins.get_all_types(),
            Self::MoveObject(ins) => ins.get_all_types(),
            Self::MoveResult(ins) => ins.get_all_types(),
            Self::MoveResultWide(ins) => ins.get_all_types(),
            Self::MoveException(ins) => ins.get_all_types(),
            Self::MoveResultObject(ins) => ins.get_all_types(),
            Self::ReturnVoid(ins) => ins.get_all_types(),
            Self::Return(ins) => ins.get_all_types(),
            Self::ReturnWide(ins) => ins.get_all_types(),
            Self::ReturnObject(ins) => ins.get_all_types(),
            Self::Const(ins) => ins.get_all_types(),
            Self::ConstWide(ins) => ins.get_all_types(),
            Self::ConstString(ins) => ins.get_all_types(),
            Self::ConstClass(ins) => ins.get_all_types(),
            Self::MonitorEnter(ins) => ins.get_all_types(),
            Self::MonitorExit(ins) => ins.get_all_types(),
            Self::CheckCast(ins) => ins.get_all_types(),
            Self::InstanceOf(ins) => ins.get_all_types(),
            Self::ArrayLength(ins) => ins.get_all_types(),
            Self::NewInstance(ins) => ins.get_all_types(),
            Self::NewArray(ins) => ins.get_all_types(),
            Self::FilledNewArray(ins) => ins.get_all_types(),
            Self::FillArrayData(ins) => ins.get_all_types(),
            Self::Throw(ins) => ins.get_all_types(),
            Self::Goto(ins) => ins.get_all_types(),
            Self::Switch(ins) => ins.get_all_types(),
            Self::CmpLFloat(ins) => ins.get_all_types(),
            Self::CmpGFloat(ins) => ins.get_all_types(),
            Self::CmpLDouble(ins) => ins.get_all_types(),
            Self::CmpGDouble(ins) => ins.get_all_types(),
            Self::CmpLong(ins) => ins.get_all_types(),
            Self::IfEq(ins) => ins.get_all_types(),
            Self::IfNe(ins) => ins.get_all_types(),
            Self::IfLt(ins) => ins.get_all_types(),
            Self::IfGe(ins) => ins.get_all_types(),
            Self::IfGt(ins) => ins.get_all_types(),
            Self::IfLe(ins) => ins.get_all_types(),
            Self::IfEqZ(ins) => ins.get_all_types(),
            Self::IfNeZ(ins) => ins.get_all_types(),
            Self::IfLtZ(ins) => ins.get_all_types(),
            Self::IfGeZ(ins) => ins.get_all_types(),
            Self::IfGtZ(ins) => ins.get_all_types(),
            Self::IfLeZ(ins) => ins.get_all_types(),
            Self::AGet(ins) => ins.get_all_types(),
            Self::AGetWide(ins) => ins.get_all_types(),
            Self::AGetObject(ins) => ins.get_all_types(),
            Self::AGetBoolean(ins) => ins.get_all_types(),
            Self::AGetByte(ins) => ins.get_all_types(),
            Self::AGetChar(ins) => ins.get_all_types(),
            Self::AGetShort(ins) => ins.get_all_types(),
            Self::APut(ins) => ins.get_all_types(),
            Self::APutWide(ins) => ins.get_all_types(),
            Self::APutObject(ins) => ins.get_all_types(),
            Self::APutBoolean(ins) => ins.get_all_types(),
            Self::APutByte(ins) => ins.get_all_types(),
            Self::APutChar(ins) => ins.get_all_types(),
            Self::APutShort(ins) => ins.get_all_types(),
            Self::IGet(ins) => ins.get_all_types(),
            Self::IGetWide(ins) => ins.get_all_types(),
            Self::IGetObject(ins) => ins.get_all_types(),
            Self::IGetBoolean(ins) => ins.get_all_types(),
            Self::IGetByte(ins) => ins.get_all_types(),
            Self::IGetChar(ins) => ins.get_all_types(),
            Self::IGetShort(ins) => ins.get_all_types(),
            Self::IPut(ins) => ins.get_all_types(),
            Self::IPutWide(ins) => ins.get_all_types(),
            Self::IPutObject(ins) => ins.get_all_types(),
            Self::IPutBoolean(ins) => ins.get_all_types(),
            Self::IPutByte(ins) => ins.get_all_types(),
            Self::IPutChar(ins) => ins.get_all_types(),
            Self::IPutShort(ins) => ins.get_all_types(),
            Self::SGet(ins) => ins.get_all_types(),
            Self::SGetWide(ins) => ins.get_all_types(),
            Self::SGetObject(ins) => ins.get_all_types(),
            Self::SGetBoolean(ins) => ins.get_all_types(),
            Self::SGetByte(ins) => ins.get_all_types(),
            Self::SGetChar(ins) => ins.get_all_types(),
            Self::SGetShort(ins) => ins.get_all_types(),
            Self::SPut(ins) => ins.get_all_types(),
            Self::SPutWide(ins) => ins.get_all_types(),
            Self::SPutObject(ins) => ins.get_all_types(),
            Self::SPutBoolean(ins) => ins.get_all_types(),
            Self::SPutByte(ins) => ins.get_all_types(),
            Self::SPutChar(ins) => ins.get_all_types(),
            Self::SPutShort(ins) => ins.get_all_types(),
            Self::InvokeVirtual(ins) => ins.get_all_types(),
            Self::InvokeSuper(ins) => ins.get_all_types(),
            Self::InvokeDirect(ins) => ins.get_all_types(),
            Self::InvokeStatic(ins) => ins.get_all_types(),
            Self::InvokeInterface(ins) => ins.get_all_types(),
            Self::NegInt(ins) => ins.get_all_types(),
            Self::NotInt(ins) => ins.get_all_types(),
            Self::NegLong(ins) => ins.get_all_types(),
            Self::NotLong(ins) => ins.get_all_types(),
            Self::NegFloat(ins) => ins.get_all_types(),
            Self::NegDouble(ins) => ins.get_all_types(),
            Self::IntToLong(ins) => ins.get_all_types(),
            Self::IntToFloat(ins) => ins.get_all_types(),
            Self::IntToDouble(ins) => ins.get_all_types(),
            Self::LongToInt(ins) => ins.get_all_types(),
            Self::LongToFloat(ins) => ins.get_all_types(),
            Self::LongToDouble(ins) => ins.get_all_types(),
            Self::FloatToInt(ins) => ins.get_all_types(),
            Self::FloatToLong(ins) => ins.get_all_types(),
            Self::FloatToDouble(ins) => ins.get_all_types(),
            Self::DoubleToInt(ins) => ins.get_all_types(),
            Self::DoubleToLong(ins) => ins.get_all_types(),
            Self::DoubleToFloat(ins) => ins.get_all_types(),
            Self::IntToByte(ins) => ins.get_all_types(),
            Self::IntToChar(ins) => ins.get_all_types(),
            Self::IntToShort(ins) => ins.get_all_types(),
            Self::AddInt(ins) => ins.get_all_types(),
            Self::SubInt(ins) => ins.get_all_types(),
            Self::MulInt(ins) => ins.get_all_types(),
            Self::DivInt(ins) => ins.get_all_types(),
            Self::RemInt(ins) => ins.get_all_types(),
            Self::AndInt(ins) => ins.get_all_types(),
            Self::OrInt(ins) => ins.get_all_types(),
            Self::XorInt(ins) => ins.get_all_types(),
            Self::ShlInt(ins) => ins.get_all_types(),
            Self::ShrInt(ins) => ins.get_all_types(),
            Self::UshrInt(ins) => ins.get_all_types(),
            Self::AddLong(ins) => ins.get_all_types(),
            Self::SubLong(ins) => ins.get_all_types(),
            Self::MulLong(ins) => ins.get_all_types(),
            Self::DivLong(ins) => ins.get_all_types(),
            Self::RemLong(ins) => ins.get_all_types(),
            Self::AndLong(ins) => ins.get_all_types(),
            Self::OrLong(ins) => ins.get_all_types(),
            Self::XorLong(ins) => ins.get_all_types(),
            Self::ShlLong(ins) => ins.get_all_types(),
            Self::ShrLong(ins) => ins.get_all_types(),
            Self::UshrLong(ins) => ins.get_all_types(),
            Self::AddFloat(ins) => ins.get_all_types(),
            Self::SubFloat(ins) => ins.get_all_types(),
            Self::MulFloat(ins) => ins.get_all_types(),
            Self::DivFloat(ins) => ins.get_all_types(),
            Self::RemFloat(ins) => ins.get_all_types(),
            Self::AddDouble(ins) => ins.get_all_types(),
            Self::SubDouble(ins) => ins.get_all_types(),
            Self::MulDouble(ins) => ins.get_all_types(),
            Self::DivDouble(ins) => ins.get_all_types(),
            Self::RemDouble(ins) => ins.get_all_types(),
            Self::AddInt2Addr(ins) => ins.get_all_types(),
            Self::SubInt2Addr(ins) => ins.get_all_types(),
            Self::MulInt2Addr(ins) => ins.get_all_types(),
            Self::DivInt2Addr(ins) => ins.get_all_types(),
            Self::RemInt2Addr(ins) => ins.get_all_types(),
            Self::AndInt2Addr(ins) => ins.get_all_types(),
            Self::OrInt2Addr(ins) => ins.get_all_types(),
            Self::XorInt2Addr(ins) => ins.get_all_types(),
            Self::ShlInt2Addr(ins) => ins.get_all_types(),
            Self::ShrInt2Addr(ins) => ins.get_all_types(),
            Self::UshrInt2Addr(ins) => ins.get_all_types(),
            Self::AddLong2Addr(ins) => ins.get_all_types(),
            Self::SubLong2Addr(ins) => ins.get_all_types(),
            Self::MulLong2Addr(ins) => ins.get_all_types(),
            Self::DivLong2Addr(ins) => ins.get_all_types(),
            Self::RemLong2Addr(ins) => ins.get_all_types(),
            Self::AndLong2Addr(ins) => ins.get_all_types(),
            Self::OrLong2Addr(ins) => ins.get_all_types(),
            Self::XorLong2Addr(ins) => ins.get_all_types(),
            Self::ShlLong2Addr(ins) => ins.get_all_types(),
            Self::ShrLong2Addr(ins) => ins.get_all_types(),
            Self::UshrLong2Addr(ins) => ins.get_all_types(),
            Self::AddFloat2Addr(ins) => ins.get_all_types(),
            Self::SubFloat2Addr(ins) => ins.get_all_types(),
            Self::MulFloat2Addr(ins) => ins.get_all_types(),
            Self::DivFloat2Addr(ins) => ins.get_all_types(),
            Self::RemFloat2Addr(ins) => ins.get_all_types(),
            Self::AddDouble2Addr(ins) => ins.get_all_types(),
            Self::SubDouble2Addr(ins) => ins.get_all_types(),
            Self::MulDouble2Addr(ins) => ins.get_all_types(),
            Self::DivDouble2Addr(ins) => ins.get_all_types(),
            Self::RemDouble2Addr(ins) => ins.get_all_types(),
            Self::AddIntLit(ins) => ins.get_all_types(),
            Self::RsubIntLit(ins) => ins.get_all_types(),
            Self::MulIntLit(ins) => ins.get_all_types(),
            Self::DivIntLit(ins) => ins.get_all_types(),
            Self::RemIntLit(ins) => ins.get_all_types(),
            Self::AndIntLit(ins) => ins.get_all_types(),
            Self::OrIntLit(ins) => ins.get_all_types(),
            Self::XorIntLit(ins) => ins.get_all_types(),
            Self::ShlIntLit(ins) => ins.get_all_types(),
            Self::ShrIntLit(ins) => ins.get_all_types(),
            Self::UshrIntLit(ins) => ins.get_all_types(),
            Self::InvokePolymorphic(ins) => ins.get_all_types(),
            Self::InvokeCustom(ins) => ins.get_all_types(),
            Self::ConstMethodHandle(ins) => ins.get_all_types(),
            Self::ConstMethodType(ins) => ins.get_all_types(),
            Self::Try(ins) => ins.get_all_types(),
            Self::Label(ins) => ins.get_all_types(),
        }
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        match self {
            Self::Nop(ins) => ins.get_all_protos(),
            Self::Move(ins) => ins.get_all_protos(),
            Self::MoveWide(ins) => ins.get_all_protos(),
            Self::MoveObject(ins) => ins.get_all_protos(),
            Self::MoveResult(ins) => ins.get_all_protos(),
            Self::MoveResultWide(ins) => ins.get_all_protos(),
            Self::MoveException(ins) => ins.get_all_protos(),
            Self::MoveResultObject(ins) => ins.get_all_protos(),
            Self::ReturnVoid(ins) => ins.get_all_protos(),
            Self::Return(ins) => ins.get_all_protos(),
            Self::ReturnWide(ins) => ins.get_all_protos(),
            Self::ReturnObject(ins) => ins.get_all_protos(),
            Self::Const(ins) => ins.get_all_protos(),
            Self::ConstWide(ins) => ins.get_all_protos(),
            Self::ConstString(ins) => ins.get_all_protos(),
            Self::ConstClass(ins) => ins.get_all_protos(),
            Self::MonitorEnter(ins) => ins.get_all_protos(),
            Self::MonitorExit(ins) => ins.get_all_protos(),
            Self::CheckCast(ins) => ins.get_all_protos(),
            Self::InstanceOf(ins) => ins.get_all_protos(),
            Self::ArrayLength(ins) => ins.get_all_protos(),
            Self::NewInstance(ins) => ins.get_all_protos(),
            Self::NewArray(ins) => ins.get_all_protos(),
            Self::FilledNewArray(ins) => ins.get_all_protos(),
            Self::FillArrayData(ins) => ins.get_all_protos(),
            Self::Throw(ins) => ins.get_all_protos(),
            Self::Goto(ins) => ins.get_all_protos(),
            Self::Switch(ins) => ins.get_all_protos(),
            Self::CmpLFloat(ins) => ins.get_all_protos(),
            Self::CmpGFloat(ins) => ins.get_all_protos(),
            Self::CmpLDouble(ins) => ins.get_all_protos(),
            Self::CmpGDouble(ins) => ins.get_all_protos(),
            Self::CmpLong(ins) => ins.get_all_protos(),
            Self::IfEq(ins) => ins.get_all_protos(),
            Self::IfNe(ins) => ins.get_all_protos(),
            Self::IfLt(ins) => ins.get_all_protos(),
            Self::IfGe(ins) => ins.get_all_protos(),
            Self::IfGt(ins) => ins.get_all_protos(),
            Self::IfLe(ins) => ins.get_all_protos(),
            Self::IfEqZ(ins) => ins.get_all_protos(),
            Self::IfNeZ(ins) => ins.get_all_protos(),
            Self::IfLtZ(ins) => ins.get_all_protos(),
            Self::IfGeZ(ins) => ins.get_all_protos(),
            Self::IfGtZ(ins) => ins.get_all_protos(),
            Self::IfLeZ(ins) => ins.get_all_protos(),
            Self::AGet(ins) => ins.get_all_protos(),
            Self::AGetWide(ins) => ins.get_all_protos(),
            Self::AGetObject(ins) => ins.get_all_protos(),
            Self::AGetBoolean(ins) => ins.get_all_protos(),
            Self::AGetByte(ins) => ins.get_all_protos(),
            Self::AGetChar(ins) => ins.get_all_protos(),
            Self::AGetShort(ins) => ins.get_all_protos(),
            Self::APut(ins) => ins.get_all_protos(),
            Self::APutWide(ins) => ins.get_all_protos(),
            Self::APutObject(ins) => ins.get_all_protos(),
            Self::APutBoolean(ins) => ins.get_all_protos(),
            Self::APutByte(ins) => ins.get_all_protos(),
            Self::APutChar(ins) => ins.get_all_protos(),
            Self::APutShort(ins) => ins.get_all_protos(),
            Self::IGet(ins) => ins.get_all_protos(),
            Self::IGetWide(ins) => ins.get_all_protos(),
            Self::IGetObject(ins) => ins.get_all_protos(),
            Self::IGetBoolean(ins) => ins.get_all_protos(),
            Self::IGetByte(ins) => ins.get_all_protos(),
            Self::IGetChar(ins) => ins.get_all_protos(),
            Self::IGetShort(ins) => ins.get_all_protos(),
            Self::IPut(ins) => ins.get_all_protos(),
            Self::IPutWide(ins) => ins.get_all_protos(),
            Self::IPutObject(ins) => ins.get_all_protos(),
            Self::IPutBoolean(ins) => ins.get_all_protos(),
            Self::IPutByte(ins) => ins.get_all_protos(),
            Self::IPutChar(ins) => ins.get_all_protos(),
            Self::IPutShort(ins) => ins.get_all_protos(),
            Self::SGet(ins) => ins.get_all_protos(),
            Self::SGetWide(ins) => ins.get_all_protos(),
            Self::SGetObject(ins) => ins.get_all_protos(),
            Self::SGetBoolean(ins) => ins.get_all_protos(),
            Self::SGetByte(ins) => ins.get_all_protos(),
            Self::SGetChar(ins) => ins.get_all_protos(),
            Self::SGetShort(ins) => ins.get_all_protos(),
            Self::SPut(ins) => ins.get_all_protos(),
            Self::SPutWide(ins) => ins.get_all_protos(),
            Self::SPutObject(ins) => ins.get_all_protos(),
            Self::SPutBoolean(ins) => ins.get_all_protos(),
            Self::SPutByte(ins) => ins.get_all_protos(),
            Self::SPutChar(ins) => ins.get_all_protos(),
            Self::SPutShort(ins) => ins.get_all_protos(),
            Self::InvokeVirtual(ins) => ins.get_all_protos(),
            Self::InvokeSuper(ins) => ins.get_all_protos(),
            Self::InvokeDirect(ins) => ins.get_all_protos(),
            Self::InvokeStatic(ins) => ins.get_all_protos(),
            Self::InvokeInterface(ins) => ins.get_all_protos(),
            Self::NegInt(ins) => ins.get_all_protos(),
            Self::NotInt(ins) => ins.get_all_protos(),
            Self::NegLong(ins) => ins.get_all_protos(),
            Self::NotLong(ins) => ins.get_all_protos(),
            Self::NegFloat(ins) => ins.get_all_protos(),
            Self::NegDouble(ins) => ins.get_all_protos(),
            Self::IntToLong(ins) => ins.get_all_protos(),
            Self::IntToFloat(ins) => ins.get_all_protos(),
            Self::IntToDouble(ins) => ins.get_all_protos(),
            Self::LongToInt(ins) => ins.get_all_protos(),
            Self::LongToFloat(ins) => ins.get_all_protos(),
            Self::LongToDouble(ins) => ins.get_all_protos(),
            Self::FloatToInt(ins) => ins.get_all_protos(),
            Self::FloatToLong(ins) => ins.get_all_protos(),
            Self::FloatToDouble(ins) => ins.get_all_protos(),
            Self::DoubleToInt(ins) => ins.get_all_protos(),
            Self::DoubleToLong(ins) => ins.get_all_protos(),
            Self::DoubleToFloat(ins) => ins.get_all_protos(),
            Self::IntToByte(ins) => ins.get_all_protos(),
            Self::IntToChar(ins) => ins.get_all_protos(),
            Self::IntToShort(ins) => ins.get_all_protos(),
            Self::AddInt(ins) => ins.get_all_protos(),
            Self::SubInt(ins) => ins.get_all_protos(),
            Self::MulInt(ins) => ins.get_all_protos(),
            Self::DivInt(ins) => ins.get_all_protos(),
            Self::RemInt(ins) => ins.get_all_protos(),
            Self::AndInt(ins) => ins.get_all_protos(),
            Self::OrInt(ins) => ins.get_all_protos(),
            Self::XorInt(ins) => ins.get_all_protos(),
            Self::ShlInt(ins) => ins.get_all_protos(),
            Self::ShrInt(ins) => ins.get_all_protos(),
            Self::UshrInt(ins) => ins.get_all_protos(),
            Self::AddLong(ins) => ins.get_all_protos(),
            Self::SubLong(ins) => ins.get_all_protos(),
            Self::MulLong(ins) => ins.get_all_protos(),
            Self::DivLong(ins) => ins.get_all_protos(),
            Self::RemLong(ins) => ins.get_all_protos(),
            Self::AndLong(ins) => ins.get_all_protos(),
            Self::OrLong(ins) => ins.get_all_protos(),
            Self::XorLong(ins) => ins.get_all_protos(),
            Self::ShlLong(ins) => ins.get_all_protos(),
            Self::ShrLong(ins) => ins.get_all_protos(),
            Self::UshrLong(ins) => ins.get_all_protos(),
            Self::AddFloat(ins) => ins.get_all_protos(),
            Self::SubFloat(ins) => ins.get_all_protos(),
            Self::MulFloat(ins) => ins.get_all_protos(),
            Self::DivFloat(ins) => ins.get_all_protos(),
            Self::RemFloat(ins) => ins.get_all_protos(),
            Self::AddDouble(ins) => ins.get_all_protos(),
            Self::SubDouble(ins) => ins.get_all_protos(),
            Self::MulDouble(ins) => ins.get_all_protos(),
            Self::DivDouble(ins) => ins.get_all_protos(),
            Self::RemDouble(ins) => ins.get_all_protos(),
            Self::AddInt2Addr(ins) => ins.get_all_protos(),
            Self::SubInt2Addr(ins) => ins.get_all_protos(),
            Self::MulInt2Addr(ins) => ins.get_all_protos(),
            Self::DivInt2Addr(ins) => ins.get_all_protos(),
            Self::RemInt2Addr(ins) => ins.get_all_protos(),
            Self::AndInt2Addr(ins) => ins.get_all_protos(),
            Self::OrInt2Addr(ins) => ins.get_all_protos(),
            Self::XorInt2Addr(ins) => ins.get_all_protos(),
            Self::ShlInt2Addr(ins) => ins.get_all_protos(),
            Self::ShrInt2Addr(ins) => ins.get_all_protos(),
            Self::UshrInt2Addr(ins) => ins.get_all_protos(),
            Self::AddLong2Addr(ins) => ins.get_all_protos(),
            Self::SubLong2Addr(ins) => ins.get_all_protos(),
            Self::MulLong2Addr(ins) => ins.get_all_protos(),
            Self::DivLong2Addr(ins) => ins.get_all_protos(),
            Self::RemLong2Addr(ins) => ins.get_all_protos(),
            Self::AndLong2Addr(ins) => ins.get_all_protos(),
            Self::OrLong2Addr(ins) => ins.get_all_protos(),
            Self::XorLong2Addr(ins) => ins.get_all_protos(),
            Self::ShlLong2Addr(ins) => ins.get_all_protos(),
            Self::ShrLong2Addr(ins) => ins.get_all_protos(),
            Self::UshrLong2Addr(ins) => ins.get_all_protos(),
            Self::AddFloat2Addr(ins) => ins.get_all_protos(),
            Self::SubFloat2Addr(ins) => ins.get_all_protos(),
            Self::MulFloat2Addr(ins) => ins.get_all_protos(),
            Self::DivFloat2Addr(ins) => ins.get_all_protos(),
            Self::RemFloat2Addr(ins) => ins.get_all_protos(),
            Self::AddDouble2Addr(ins) => ins.get_all_protos(),
            Self::SubDouble2Addr(ins) => ins.get_all_protos(),
            Self::MulDouble2Addr(ins) => ins.get_all_protos(),
            Self::DivDouble2Addr(ins) => ins.get_all_protos(),
            Self::RemDouble2Addr(ins) => ins.get_all_protos(),
            Self::AddIntLit(ins) => ins.get_all_protos(),
            Self::RsubIntLit(ins) => ins.get_all_protos(),
            Self::MulIntLit(ins) => ins.get_all_protos(),
            Self::DivIntLit(ins) => ins.get_all_protos(),
            Self::RemIntLit(ins) => ins.get_all_protos(),
            Self::AndIntLit(ins) => ins.get_all_protos(),
            Self::OrIntLit(ins) => ins.get_all_protos(),
            Self::XorIntLit(ins) => ins.get_all_protos(),
            Self::ShlIntLit(ins) => ins.get_all_protos(),
            Self::ShrIntLit(ins) => ins.get_all_protos(),
            Self::UshrIntLit(ins) => ins.get_all_protos(),
            Self::InvokePolymorphic(ins) => ins.get_all_protos(),
            Self::InvokeCustom(ins) => ins.get_all_protos(),
            Self::ConstMethodHandle(ins) => ins.get_all_protos(),
            Self::ConstMethodType(ins) => ins.get_all_protos(),
            Self::Try(ins) => ins.get_all_protos(),
            Self::Label(ins) => ins.get_all_protos(),
        }
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        match self {
            Self::Nop(ins) => ins.get_all_field_ids(),
            Self::Move(ins) => ins.get_all_field_ids(),
            Self::MoveWide(ins) => ins.get_all_field_ids(),
            Self::MoveObject(ins) => ins.get_all_field_ids(),
            Self::MoveResult(ins) => ins.get_all_field_ids(),
            Self::MoveResultWide(ins) => ins.get_all_field_ids(),
            Self::MoveException(ins) => ins.get_all_field_ids(),
            Self::MoveResultObject(ins) => ins.get_all_field_ids(),
            Self::ReturnVoid(ins) => ins.get_all_field_ids(),
            Self::Return(ins) => ins.get_all_field_ids(),
            Self::ReturnWide(ins) => ins.get_all_field_ids(),
            Self::ReturnObject(ins) => ins.get_all_field_ids(),
            Self::Const(ins) => ins.get_all_field_ids(),
            Self::ConstWide(ins) => ins.get_all_field_ids(),
            Self::ConstString(ins) => ins.get_all_field_ids(),
            Self::ConstClass(ins) => ins.get_all_field_ids(),
            Self::MonitorEnter(ins) => ins.get_all_field_ids(),
            Self::MonitorExit(ins) => ins.get_all_field_ids(),
            Self::CheckCast(ins) => ins.get_all_field_ids(),
            Self::InstanceOf(ins) => ins.get_all_field_ids(),
            Self::ArrayLength(ins) => ins.get_all_field_ids(),
            Self::NewInstance(ins) => ins.get_all_field_ids(),
            Self::NewArray(ins) => ins.get_all_field_ids(),
            Self::FilledNewArray(ins) => ins.get_all_field_ids(),
            Self::FillArrayData(ins) => ins.get_all_field_ids(),
            Self::Throw(ins) => ins.get_all_field_ids(),
            Self::Goto(ins) => ins.get_all_field_ids(),
            Self::Switch(ins) => ins.get_all_field_ids(),
            Self::CmpLFloat(ins) => ins.get_all_field_ids(),
            Self::CmpGFloat(ins) => ins.get_all_field_ids(),
            Self::CmpLDouble(ins) => ins.get_all_field_ids(),
            Self::CmpGDouble(ins) => ins.get_all_field_ids(),
            Self::CmpLong(ins) => ins.get_all_field_ids(),
            Self::IfEq(ins) => ins.get_all_field_ids(),
            Self::IfNe(ins) => ins.get_all_field_ids(),
            Self::IfLt(ins) => ins.get_all_field_ids(),
            Self::IfGe(ins) => ins.get_all_field_ids(),
            Self::IfGt(ins) => ins.get_all_field_ids(),
            Self::IfLe(ins) => ins.get_all_field_ids(),
            Self::IfEqZ(ins) => ins.get_all_field_ids(),
            Self::IfNeZ(ins) => ins.get_all_field_ids(),
            Self::IfLtZ(ins) => ins.get_all_field_ids(),
            Self::IfGeZ(ins) => ins.get_all_field_ids(),
            Self::IfGtZ(ins) => ins.get_all_field_ids(),
            Self::IfLeZ(ins) => ins.get_all_field_ids(),
            Self::AGet(ins) => ins.get_all_field_ids(),
            Self::AGetWide(ins) => ins.get_all_field_ids(),
            Self::AGetObject(ins) => ins.get_all_field_ids(),
            Self::AGetBoolean(ins) => ins.get_all_field_ids(),
            Self::AGetByte(ins) => ins.get_all_field_ids(),
            Self::AGetChar(ins) => ins.get_all_field_ids(),
            Self::AGetShort(ins) => ins.get_all_field_ids(),
            Self::APut(ins) => ins.get_all_field_ids(),
            Self::APutWide(ins) => ins.get_all_field_ids(),
            Self::APutObject(ins) => ins.get_all_field_ids(),
            Self::APutBoolean(ins) => ins.get_all_field_ids(),
            Self::APutByte(ins) => ins.get_all_field_ids(),
            Self::APutChar(ins) => ins.get_all_field_ids(),
            Self::APutShort(ins) => ins.get_all_field_ids(),
            Self::IGet(ins) => ins.get_all_field_ids(),
            Self::IGetWide(ins) => ins.get_all_field_ids(),
            Self::IGetObject(ins) => ins.get_all_field_ids(),
            Self::IGetBoolean(ins) => ins.get_all_field_ids(),
            Self::IGetByte(ins) => ins.get_all_field_ids(),
            Self::IGetChar(ins) => ins.get_all_field_ids(),
            Self::IGetShort(ins) => ins.get_all_field_ids(),
            Self::IPut(ins) => ins.get_all_field_ids(),
            Self::IPutWide(ins) => ins.get_all_field_ids(),
            Self::IPutObject(ins) => ins.get_all_field_ids(),
            Self::IPutBoolean(ins) => ins.get_all_field_ids(),
            Self::IPutByte(ins) => ins.get_all_field_ids(),
            Self::IPutChar(ins) => ins.get_all_field_ids(),
            Self::IPutShort(ins) => ins.get_all_field_ids(),
            Self::SGet(ins) => ins.get_all_field_ids(),
            Self::SGetWide(ins) => ins.get_all_field_ids(),
            Self::SGetObject(ins) => ins.get_all_field_ids(),
            Self::SGetBoolean(ins) => ins.get_all_field_ids(),
            Self::SGetByte(ins) => ins.get_all_field_ids(),
            Self::SGetChar(ins) => ins.get_all_field_ids(),
            Self::SGetShort(ins) => ins.get_all_field_ids(),
            Self::SPut(ins) => ins.get_all_field_ids(),
            Self::SPutWide(ins) => ins.get_all_field_ids(),
            Self::SPutObject(ins) => ins.get_all_field_ids(),
            Self::SPutBoolean(ins) => ins.get_all_field_ids(),
            Self::SPutByte(ins) => ins.get_all_field_ids(),
            Self::SPutChar(ins) => ins.get_all_field_ids(),
            Self::SPutShort(ins) => ins.get_all_field_ids(),
            Self::InvokeVirtual(ins) => ins.get_all_field_ids(),
            Self::InvokeSuper(ins) => ins.get_all_field_ids(),
            Self::InvokeDirect(ins) => ins.get_all_field_ids(),
            Self::InvokeStatic(ins) => ins.get_all_field_ids(),
            Self::InvokeInterface(ins) => ins.get_all_field_ids(),
            Self::NegInt(ins) => ins.get_all_field_ids(),
            Self::NotInt(ins) => ins.get_all_field_ids(),
            Self::NegLong(ins) => ins.get_all_field_ids(),
            Self::NotLong(ins) => ins.get_all_field_ids(),
            Self::NegFloat(ins) => ins.get_all_field_ids(),
            Self::NegDouble(ins) => ins.get_all_field_ids(),
            Self::IntToLong(ins) => ins.get_all_field_ids(),
            Self::IntToFloat(ins) => ins.get_all_field_ids(),
            Self::IntToDouble(ins) => ins.get_all_field_ids(),
            Self::LongToInt(ins) => ins.get_all_field_ids(),
            Self::LongToFloat(ins) => ins.get_all_field_ids(),
            Self::LongToDouble(ins) => ins.get_all_field_ids(),
            Self::FloatToInt(ins) => ins.get_all_field_ids(),
            Self::FloatToLong(ins) => ins.get_all_field_ids(),
            Self::FloatToDouble(ins) => ins.get_all_field_ids(),
            Self::DoubleToInt(ins) => ins.get_all_field_ids(),
            Self::DoubleToLong(ins) => ins.get_all_field_ids(),
            Self::DoubleToFloat(ins) => ins.get_all_field_ids(),
            Self::IntToByte(ins) => ins.get_all_field_ids(),
            Self::IntToChar(ins) => ins.get_all_field_ids(),
            Self::IntToShort(ins) => ins.get_all_field_ids(),
            Self::AddInt(ins) => ins.get_all_field_ids(),
            Self::SubInt(ins) => ins.get_all_field_ids(),
            Self::MulInt(ins) => ins.get_all_field_ids(),
            Self::DivInt(ins) => ins.get_all_field_ids(),
            Self::RemInt(ins) => ins.get_all_field_ids(),
            Self::AndInt(ins) => ins.get_all_field_ids(),
            Self::OrInt(ins) => ins.get_all_field_ids(),
            Self::XorInt(ins) => ins.get_all_field_ids(),
            Self::ShlInt(ins) => ins.get_all_field_ids(),
            Self::ShrInt(ins) => ins.get_all_field_ids(),
            Self::UshrInt(ins) => ins.get_all_field_ids(),
            Self::AddLong(ins) => ins.get_all_field_ids(),
            Self::SubLong(ins) => ins.get_all_field_ids(),
            Self::MulLong(ins) => ins.get_all_field_ids(),
            Self::DivLong(ins) => ins.get_all_field_ids(),
            Self::RemLong(ins) => ins.get_all_field_ids(),
            Self::AndLong(ins) => ins.get_all_field_ids(),
            Self::OrLong(ins) => ins.get_all_field_ids(),
            Self::XorLong(ins) => ins.get_all_field_ids(),
            Self::ShlLong(ins) => ins.get_all_field_ids(),
            Self::ShrLong(ins) => ins.get_all_field_ids(),
            Self::UshrLong(ins) => ins.get_all_field_ids(),
            Self::AddFloat(ins) => ins.get_all_field_ids(),
            Self::SubFloat(ins) => ins.get_all_field_ids(),
            Self::MulFloat(ins) => ins.get_all_field_ids(),
            Self::DivFloat(ins) => ins.get_all_field_ids(),
            Self::RemFloat(ins) => ins.get_all_field_ids(),
            Self::AddDouble(ins) => ins.get_all_field_ids(),
            Self::SubDouble(ins) => ins.get_all_field_ids(),
            Self::MulDouble(ins) => ins.get_all_field_ids(),
            Self::DivDouble(ins) => ins.get_all_field_ids(),
            Self::RemDouble(ins) => ins.get_all_field_ids(),
            Self::AddInt2Addr(ins) => ins.get_all_field_ids(),
            Self::SubInt2Addr(ins) => ins.get_all_field_ids(),
            Self::MulInt2Addr(ins) => ins.get_all_field_ids(),
            Self::DivInt2Addr(ins) => ins.get_all_field_ids(),
            Self::RemInt2Addr(ins) => ins.get_all_field_ids(),
            Self::AndInt2Addr(ins) => ins.get_all_field_ids(),
            Self::OrInt2Addr(ins) => ins.get_all_field_ids(),
            Self::XorInt2Addr(ins) => ins.get_all_field_ids(),
            Self::ShlInt2Addr(ins) => ins.get_all_field_ids(),
            Self::ShrInt2Addr(ins) => ins.get_all_field_ids(),
            Self::UshrInt2Addr(ins) => ins.get_all_field_ids(),
            Self::AddLong2Addr(ins) => ins.get_all_field_ids(),
            Self::SubLong2Addr(ins) => ins.get_all_field_ids(),
            Self::MulLong2Addr(ins) => ins.get_all_field_ids(),
            Self::DivLong2Addr(ins) => ins.get_all_field_ids(),
            Self::RemLong2Addr(ins) => ins.get_all_field_ids(),
            Self::AndLong2Addr(ins) => ins.get_all_field_ids(),
            Self::OrLong2Addr(ins) => ins.get_all_field_ids(),
            Self::XorLong2Addr(ins) => ins.get_all_field_ids(),
            Self::ShlLong2Addr(ins) => ins.get_all_field_ids(),
            Self::ShrLong2Addr(ins) => ins.get_all_field_ids(),
            Self::UshrLong2Addr(ins) => ins.get_all_field_ids(),
            Self::AddFloat2Addr(ins) => ins.get_all_field_ids(),
            Self::SubFloat2Addr(ins) => ins.get_all_field_ids(),
            Self::MulFloat2Addr(ins) => ins.get_all_field_ids(),
            Self::DivFloat2Addr(ins) => ins.get_all_field_ids(),
            Self::RemFloat2Addr(ins) => ins.get_all_field_ids(),
            Self::AddDouble2Addr(ins) => ins.get_all_field_ids(),
            Self::SubDouble2Addr(ins) => ins.get_all_field_ids(),
            Self::MulDouble2Addr(ins) => ins.get_all_field_ids(),
            Self::DivDouble2Addr(ins) => ins.get_all_field_ids(),
            Self::RemDouble2Addr(ins) => ins.get_all_field_ids(),
            Self::AddIntLit(ins) => ins.get_all_field_ids(),
            Self::RsubIntLit(ins) => ins.get_all_field_ids(),
            Self::MulIntLit(ins) => ins.get_all_field_ids(),
            Self::DivIntLit(ins) => ins.get_all_field_ids(),
            Self::RemIntLit(ins) => ins.get_all_field_ids(),
            Self::AndIntLit(ins) => ins.get_all_field_ids(),
            Self::OrIntLit(ins) => ins.get_all_field_ids(),
            Self::XorIntLit(ins) => ins.get_all_field_ids(),
            Self::ShlIntLit(ins) => ins.get_all_field_ids(),
            Self::ShrIntLit(ins) => ins.get_all_field_ids(),
            Self::UshrIntLit(ins) => ins.get_all_field_ids(),
            Self::InvokePolymorphic(ins) => ins.get_all_field_ids(),
            Self::InvokeCustom(ins) => ins.get_all_field_ids(),
            Self::ConstMethodHandle(ins) => ins.get_all_field_ids(),
            Self::ConstMethodType(ins) => ins.get_all_field_ids(),
            Self::Try(ins) => ins.get_all_field_ids(),
            Self::Label(ins) => ins.get_all_field_ids(),
        }
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        match self {
            Self::Nop(ins) => ins.get_all_method_ids(),
            Self::Move(ins) => ins.get_all_method_ids(),
            Self::MoveWide(ins) => ins.get_all_method_ids(),
            Self::MoveObject(ins) => ins.get_all_method_ids(),
            Self::MoveResult(ins) => ins.get_all_method_ids(),
            Self::MoveResultWide(ins) => ins.get_all_method_ids(),
            Self::MoveException(ins) => ins.get_all_method_ids(),
            Self::MoveResultObject(ins) => ins.get_all_method_ids(),
            Self::ReturnVoid(ins) => ins.get_all_method_ids(),
            Self::Return(ins) => ins.get_all_method_ids(),
            Self::ReturnWide(ins) => ins.get_all_method_ids(),
            Self::ReturnObject(ins) => ins.get_all_method_ids(),
            Self::Const(ins) => ins.get_all_method_ids(),
            Self::ConstWide(ins) => ins.get_all_method_ids(),
            Self::ConstString(ins) => ins.get_all_method_ids(),
            Self::ConstClass(ins) => ins.get_all_method_ids(),
            Self::MonitorEnter(ins) => ins.get_all_method_ids(),
            Self::MonitorExit(ins) => ins.get_all_method_ids(),
            Self::CheckCast(ins) => ins.get_all_method_ids(),
            Self::InstanceOf(ins) => ins.get_all_method_ids(),
            Self::ArrayLength(ins) => ins.get_all_method_ids(),
            Self::NewInstance(ins) => ins.get_all_method_ids(),
            Self::NewArray(ins) => ins.get_all_method_ids(),
            Self::FilledNewArray(ins) => ins.get_all_method_ids(),
            Self::FillArrayData(ins) => ins.get_all_method_ids(),
            Self::Throw(ins) => ins.get_all_method_ids(),
            Self::Goto(ins) => ins.get_all_method_ids(),
            Self::Switch(ins) => ins.get_all_method_ids(),
            Self::CmpLFloat(ins) => ins.get_all_method_ids(),
            Self::CmpGFloat(ins) => ins.get_all_method_ids(),
            Self::CmpLDouble(ins) => ins.get_all_method_ids(),
            Self::CmpGDouble(ins) => ins.get_all_method_ids(),
            Self::CmpLong(ins) => ins.get_all_method_ids(),
            Self::IfEq(ins) => ins.get_all_method_ids(),
            Self::IfNe(ins) => ins.get_all_method_ids(),
            Self::IfLt(ins) => ins.get_all_method_ids(),
            Self::IfGe(ins) => ins.get_all_method_ids(),
            Self::IfGt(ins) => ins.get_all_method_ids(),
            Self::IfLe(ins) => ins.get_all_method_ids(),
            Self::IfEqZ(ins) => ins.get_all_method_ids(),
            Self::IfNeZ(ins) => ins.get_all_method_ids(),
            Self::IfLtZ(ins) => ins.get_all_method_ids(),
            Self::IfGeZ(ins) => ins.get_all_method_ids(),
            Self::IfGtZ(ins) => ins.get_all_method_ids(),
            Self::IfLeZ(ins) => ins.get_all_method_ids(),
            Self::AGet(ins) => ins.get_all_method_ids(),
            Self::AGetWide(ins) => ins.get_all_method_ids(),
            Self::AGetObject(ins) => ins.get_all_method_ids(),
            Self::AGetBoolean(ins) => ins.get_all_method_ids(),
            Self::AGetByte(ins) => ins.get_all_method_ids(),
            Self::AGetChar(ins) => ins.get_all_method_ids(),
            Self::AGetShort(ins) => ins.get_all_method_ids(),
            Self::APut(ins) => ins.get_all_method_ids(),
            Self::APutWide(ins) => ins.get_all_method_ids(),
            Self::APutObject(ins) => ins.get_all_method_ids(),
            Self::APutBoolean(ins) => ins.get_all_method_ids(),
            Self::APutByte(ins) => ins.get_all_method_ids(),
            Self::APutChar(ins) => ins.get_all_method_ids(),
            Self::APutShort(ins) => ins.get_all_method_ids(),
            Self::IGet(ins) => ins.get_all_method_ids(),
            Self::IGetWide(ins) => ins.get_all_method_ids(),
            Self::IGetObject(ins) => ins.get_all_method_ids(),
            Self::IGetBoolean(ins) => ins.get_all_method_ids(),
            Self::IGetByte(ins) => ins.get_all_method_ids(),
            Self::IGetChar(ins) => ins.get_all_method_ids(),
            Self::IGetShort(ins) => ins.get_all_method_ids(),
            Self::IPut(ins) => ins.get_all_method_ids(),
            Self::IPutWide(ins) => ins.get_all_method_ids(),
            Self::IPutObject(ins) => ins.get_all_method_ids(),
            Self::IPutBoolean(ins) => ins.get_all_method_ids(),
            Self::IPutByte(ins) => ins.get_all_method_ids(),
            Self::IPutChar(ins) => ins.get_all_method_ids(),
            Self::IPutShort(ins) => ins.get_all_method_ids(),
            Self::SGet(ins) => ins.get_all_method_ids(),
            Self::SGetWide(ins) => ins.get_all_method_ids(),
            Self::SGetObject(ins) => ins.get_all_method_ids(),
            Self::SGetBoolean(ins) => ins.get_all_method_ids(),
            Self::SGetByte(ins) => ins.get_all_method_ids(),
            Self::SGetChar(ins) => ins.get_all_method_ids(),
            Self::SGetShort(ins) => ins.get_all_method_ids(),
            Self::SPut(ins) => ins.get_all_method_ids(),
            Self::SPutWide(ins) => ins.get_all_method_ids(),
            Self::SPutObject(ins) => ins.get_all_method_ids(),
            Self::SPutBoolean(ins) => ins.get_all_method_ids(),
            Self::SPutByte(ins) => ins.get_all_method_ids(),
            Self::SPutChar(ins) => ins.get_all_method_ids(),
            Self::SPutShort(ins) => ins.get_all_method_ids(),
            Self::InvokeVirtual(ins) => ins.get_all_method_ids(),
            Self::InvokeSuper(ins) => ins.get_all_method_ids(),
            Self::InvokeDirect(ins) => ins.get_all_method_ids(),
            Self::InvokeStatic(ins) => ins.get_all_method_ids(),
            Self::InvokeInterface(ins) => ins.get_all_method_ids(),
            Self::NegInt(ins) => ins.get_all_method_ids(),
            Self::NotInt(ins) => ins.get_all_method_ids(),
            Self::NegLong(ins) => ins.get_all_method_ids(),
            Self::NotLong(ins) => ins.get_all_method_ids(),
            Self::NegFloat(ins) => ins.get_all_method_ids(),
            Self::NegDouble(ins) => ins.get_all_method_ids(),
            Self::IntToLong(ins) => ins.get_all_method_ids(),
            Self::IntToFloat(ins) => ins.get_all_method_ids(),
            Self::IntToDouble(ins) => ins.get_all_method_ids(),
            Self::LongToInt(ins) => ins.get_all_method_ids(),
            Self::LongToFloat(ins) => ins.get_all_method_ids(),
            Self::LongToDouble(ins) => ins.get_all_method_ids(),
            Self::FloatToInt(ins) => ins.get_all_method_ids(),
            Self::FloatToLong(ins) => ins.get_all_method_ids(),
            Self::FloatToDouble(ins) => ins.get_all_method_ids(),
            Self::DoubleToInt(ins) => ins.get_all_method_ids(),
            Self::DoubleToLong(ins) => ins.get_all_method_ids(),
            Self::DoubleToFloat(ins) => ins.get_all_method_ids(),
            Self::IntToByte(ins) => ins.get_all_method_ids(),
            Self::IntToChar(ins) => ins.get_all_method_ids(),
            Self::IntToShort(ins) => ins.get_all_method_ids(),
            Self::AddInt(ins) => ins.get_all_method_ids(),
            Self::SubInt(ins) => ins.get_all_method_ids(),
            Self::MulInt(ins) => ins.get_all_method_ids(),
            Self::DivInt(ins) => ins.get_all_method_ids(),
            Self::RemInt(ins) => ins.get_all_method_ids(),
            Self::AndInt(ins) => ins.get_all_method_ids(),
            Self::OrInt(ins) => ins.get_all_method_ids(),
            Self::XorInt(ins) => ins.get_all_method_ids(),
            Self::ShlInt(ins) => ins.get_all_method_ids(),
            Self::ShrInt(ins) => ins.get_all_method_ids(),
            Self::UshrInt(ins) => ins.get_all_method_ids(),
            Self::AddLong(ins) => ins.get_all_method_ids(),
            Self::SubLong(ins) => ins.get_all_method_ids(),
            Self::MulLong(ins) => ins.get_all_method_ids(),
            Self::DivLong(ins) => ins.get_all_method_ids(),
            Self::RemLong(ins) => ins.get_all_method_ids(),
            Self::AndLong(ins) => ins.get_all_method_ids(),
            Self::OrLong(ins) => ins.get_all_method_ids(),
            Self::XorLong(ins) => ins.get_all_method_ids(),
            Self::ShlLong(ins) => ins.get_all_method_ids(),
            Self::ShrLong(ins) => ins.get_all_method_ids(),
            Self::UshrLong(ins) => ins.get_all_method_ids(),
            Self::AddFloat(ins) => ins.get_all_method_ids(),
            Self::SubFloat(ins) => ins.get_all_method_ids(),
            Self::MulFloat(ins) => ins.get_all_method_ids(),
            Self::DivFloat(ins) => ins.get_all_method_ids(),
            Self::RemFloat(ins) => ins.get_all_method_ids(),
            Self::AddDouble(ins) => ins.get_all_method_ids(),
            Self::SubDouble(ins) => ins.get_all_method_ids(),
            Self::MulDouble(ins) => ins.get_all_method_ids(),
            Self::DivDouble(ins) => ins.get_all_method_ids(),
            Self::RemDouble(ins) => ins.get_all_method_ids(),
            Self::AddInt2Addr(ins) => ins.get_all_method_ids(),
            Self::SubInt2Addr(ins) => ins.get_all_method_ids(),
            Self::MulInt2Addr(ins) => ins.get_all_method_ids(),
            Self::DivInt2Addr(ins) => ins.get_all_method_ids(),
            Self::RemInt2Addr(ins) => ins.get_all_method_ids(),
            Self::AndInt2Addr(ins) => ins.get_all_method_ids(),
            Self::OrInt2Addr(ins) => ins.get_all_method_ids(),
            Self::XorInt2Addr(ins) => ins.get_all_method_ids(),
            Self::ShlInt2Addr(ins) => ins.get_all_method_ids(),
            Self::ShrInt2Addr(ins) => ins.get_all_method_ids(),
            Self::UshrInt2Addr(ins) => ins.get_all_method_ids(),
            Self::AddLong2Addr(ins) => ins.get_all_method_ids(),
            Self::SubLong2Addr(ins) => ins.get_all_method_ids(),
            Self::MulLong2Addr(ins) => ins.get_all_method_ids(),
            Self::DivLong2Addr(ins) => ins.get_all_method_ids(),
            Self::RemLong2Addr(ins) => ins.get_all_method_ids(),
            Self::AndLong2Addr(ins) => ins.get_all_method_ids(),
            Self::OrLong2Addr(ins) => ins.get_all_method_ids(),
            Self::XorLong2Addr(ins) => ins.get_all_method_ids(),
            Self::ShlLong2Addr(ins) => ins.get_all_method_ids(),
            Self::ShrLong2Addr(ins) => ins.get_all_method_ids(),
            Self::UshrLong2Addr(ins) => ins.get_all_method_ids(),
            Self::AddFloat2Addr(ins) => ins.get_all_method_ids(),
            Self::SubFloat2Addr(ins) => ins.get_all_method_ids(),
            Self::MulFloat2Addr(ins) => ins.get_all_method_ids(),
            Self::DivFloat2Addr(ins) => ins.get_all_method_ids(),
            Self::RemFloat2Addr(ins) => ins.get_all_method_ids(),
            Self::AddDouble2Addr(ins) => ins.get_all_method_ids(),
            Self::SubDouble2Addr(ins) => ins.get_all_method_ids(),
            Self::MulDouble2Addr(ins) => ins.get_all_method_ids(),
            Self::DivDouble2Addr(ins) => ins.get_all_method_ids(),
            Self::RemDouble2Addr(ins) => ins.get_all_method_ids(),
            Self::AddIntLit(ins) => ins.get_all_method_ids(),
            Self::RsubIntLit(ins) => ins.get_all_method_ids(),
            Self::MulIntLit(ins) => ins.get_all_method_ids(),
            Self::DivIntLit(ins) => ins.get_all_method_ids(),
            Self::RemIntLit(ins) => ins.get_all_method_ids(),
            Self::AndIntLit(ins) => ins.get_all_method_ids(),
            Self::OrIntLit(ins) => ins.get_all_method_ids(),
            Self::XorIntLit(ins) => ins.get_all_method_ids(),
            Self::ShlIntLit(ins) => ins.get_all_method_ids(),
            Self::ShrIntLit(ins) => ins.get_all_method_ids(),
            Self::UshrIntLit(ins) => ins.get_all_method_ids(),
            Self::InvokePolymorphic(ins) => ins.get_all_method_ids(),
            Self::InvokeCustom(ins) => ins.get_all_method_ids(),
            Self::ConstMethodHandle(ins) => ins.get_all_method_ids(),
            Self::ConstMethodType(ins) => ins.get_all_method_ids(),
            Self::Try(ins) => ins.get_all_method_ids(),
            Self::Label(ins) => ins.get_all_method_ids(),
        }
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        match self {
            Self::Nop(ins) => ins.get_all_method_handles(),
            Self::Move(ins) => ins.get_all_method_handles(),
            Self::MoveWide(ins) => ins.get_all_method_handles(),
            Self::MoveObject(ins) => ins.get_all_method_handles(),
            Self::MoveResult(ins) => ins.get_all_method_handles(),
            Self::MoveResultWide(ins) => ins.get_all_method_handles(),
            Self::MoveException(ins) => ins.get_all_method_handles(),
            Self::MoveResultObject(ins) => ins.get_all_method_handles(),
            Self::ReturnVoid(ins) => ins.get_all_method_handles(),
            Self::Return(ins) => ins.get_all_method_handles(),
            Self::ReturnWide(ins) => ins.get_all_method_handles(),
            Self::ReturnObject(ins) => ins.get_all_method_handles(),
            Self::Const(ins) => ins.get_all_method_handles(),
            Self::ConstWide(ins) => ins.get_all_method_handles(),
            Self::ConstString(ins) => ins.get_all_method_handles(),
            Self::ConstClass(ins) => ins.get_all_method_handles(),
            Self::MonitorEnter(ins) => ins.get_all_method_handles(),
            Self::MonitorExit(ins) => ins.get_all_method_handles(),
            Self::CheckCast(ins) => ins.get_all_method_handles(),
            Self::InstanceOf(ins) => ins.get_all_method_handles(),
            Self::ArrayLength(ins) => ins.get_all_method_handles(),
            Self::NewInstance(ins) => ins.get_all_method_handles(),
            Self::NewArray(ins) => ins.get_all_method_handles(),
            Self::FilledNewArray(ins) => ins.get_all_method_handles(),
            Self::FillArrayData(ins) => ins.get_all_method_handles(),
            Self::Throw(ins) => ins.get_all_method_handles(),
            Self::Goto(ins) => ins.get_all_method_handles(),
            Self::Switch(ins) => ins.get_all_method_handles(),
            Self::CmpLFloat(ins) => ins.get_all_method_handles(),
            Self::CmpGFloat(ins) => ins.get_all_method_handles(),
            Self::CmpLDouble(ins) => ins.get_all_method_handles(),
            Self::CmpGDouble(ins) => ins.get_all_method_handles(),
            Self::CmpLong(ins) => ins.get_all_method_handles(),
            Self::IfEq(ins) => ins.get_all_method_handles(),
            Self::IfNe(ins) => ins.get_all_method_handles(),
            Self::IfLt(ins) => ins.get_all_method_handles(),
            Self::IfGe(ins) => ins.get_all_method_handles(),
            Self::IfGt(ins) => ins.get_all_method_handles(),
            Self::IfLe(ins) => ins.get_all_method_handles(),
            Self::IfEqZ(ins) => ins.get_all_method_handles(),
            Self::IfNeZ(ins) => ins.get_all_method_handles(),
            Self::IfLtZ(ins) => ins.get_all_method_handles(),
            Self::IfGeZ(ins) => ins.get_all_method_handles(),
            Self::IfGtZ(ins) => ins.get_all_method_handles(),
            Self::IfLeZ(ins) => ins.get_all_method_handles(),
            Self::AGet(ins) => ins.get_all_method_handles(),
            Self::AGetWide(ins) => ins.get_all_method_handles(),
            Self::AGetObject(ins) => ins.get_all_method_handles(),
            Self::AGetBoolean(ins) => ins.get_all_method_handles(),
            Self::AGetByte(ins) => ins.get_all_method_handles(),
            Self::AGetChar(ins) => ins.get_all_method_handles(),
            Self::AGetShort(ins) => ins.get_all_method_handles(),
            Self::APut(ins) => ins.get_all_method_handles(),
            Self::APutWide(ins) => ins.get_all_method_handles(),
            Self::APutObject(ins) => ins.get_all_method_handles(),
            Self::APutBoolean(ins) => ins.get_all_method_handles(),
            Self::APutByte(ins) => ins.get_all_method_handles(),
            Self::APutChar(ins) => ins.get_all_method_handles(),
            Self::APutShort(ins) => ins.get_all_method_handles(),
            Self::IGet(ins) => ins.get_all_method_handles(),
            Self::IGetWide(ins) => ins.get_all_method_handles(),
            Self::IGetObject(ins) => ins.get_all_method_handles(),
            Self::IGetBoolean(ins) => ins.get_all_method_handles(),
            Self::IGetByte(ins) => ins.get_all_method_handles(),
            Self::IGetChar(ins) => ins.get_all_method_handles(),
            Self::IGetShort(ins) => ins.get_all_method_handles(),
            Self::IPut(ins) => ins.get_all_method_handles(),
            Self::IPutWide(ins) => ins.get_all_method_handles(),
            Self::IPutObject(ins) => ins.get_all_method_handles(),
            Self::IPutBoolean(ins) => ins.get_all_method_handles(),
            Self::IPutByte(ins) => ins.get_all_method_handles(),
            Self::IPutChar(ins) => ins.get_all_method_handles(),
            Self::IPutShort(ins) => ins.get_all_method_handles(),
            Self::SGet(ins) => ins.get_all_method_handles(),
            Self::SGetWide(ins) => ins.get_all_method_handles(),
            Self::SGetObject(ins) => ins.get_all_method_handles(),
            Self::SGetBoolean(ins) => ins.get_all_method_handles(),
            Self::SGetByte(ins) => ins.get_all_method_handles(),
            Self::SGetChar(ins) => ins.get_all_method_handles(),
            Self::SGetShort(ins) => ins.get_all_method_handles(),
            Self::SPut(ins) => ins.get_all_method_handles(),
            Self::SPutWide(ins) => ins.get_all_method_handles(),
            Self::SPutObject(ins) => ins.get_all_method_handles(),
            Self::SPutBoolean(ins) => ins.get_all_method_handles(),
            Self::SPutByte(ins) => ins.get_all_method_handles(),
            Self::SPutChar(ins) => ins.get_all_method_handles(),
            Self::SPutShort(ins) => ins.get_all_method_handles(),
            Self::InvokeVirtual(ins) => ins.get_all_method_handles(),
            Self::InvokeSuper(ins) => ins.get_all_method_handles(),
            Self::InvokeDirect(ins) => ins.get_all_method_handles(),
            Self::InvokeStatic(ins) => ins.get_all_method_handles(),
            Self::InvokeInterface(ins) => ins.get_all_method_handles(),
            Self::NegInt(ins) => ins.get_all_method_handles(),
            Self::NotInt(ins) => ins.get_all_method_handles(),
            Self::NegLong(ins) => ins.get_all_method_handles(),
            Self::NotLong(ins) => ins.get_all_method_handles(),
            Self::NegFloat(ins) => ins.get_all_method_handles(),
            Self::NegDouble(ins) => ins.get_all_method_handles(),
            Self::IntToLong(ins) => ins.get_all_method_handles(),
            Self::IntToFloat(ins) => ins.get_all_method_handles(),
            Self::IntToDouble(ins) => ins.get_all_method_handles(),
            Self::LongToInt(ins) => ins.get_all_method_handles(),
            Self::LongToFloat(ins) => ins.get_all_method_handles(),
            Self::LongToDouble(ins) => ins.get_all_method_handles(),
            Self::FloatToInt(ins) => ins.get_all_method_handles(),
            Self::FloatToLong(ins) => ins.get_all_method_handles(),
            Self::FloatToDouble(ins) => ins.get_all_method_handles(),
            Self::DoubleToInt(ins) => ins.get_all_method_handles(),
            Self::DoubleToLong(ins) => ins.get_all_method_handles(),
            Self::DoubleToFloat(ins) => ins.get_all_method_handles(),
            Self::IntToByte(ins) => ins.get_all_method_handles(),
            Self::IntToChar(ins) => ins.get_all_method_handles(),
            Self::IntToShort(ins) => ins.get_all_method_handles(),
            Self::AddInt(ins) => ins.get_all_method_handles(),
            Self::SubInt(ins) => ins.get_all_method_handles(),
            Self::MulInt(ins) => ins.get_all_method_handles(),
            Self::DivInt(ins) => ins.get_all_method_handles(),
            Self::RemInt(ins) => ins.get_all_method_handles(),
            Self::AndInt(ins) => ins.get_all_method_handles(),
            Self::OrInt(ins) => ins.get_all_method_handles(),
            Self::XorInt(ins) => ins.get_all_method_handles(),
            Self::ShlInt(ins) => ins.get_all_method_handles(),
            Self::ShrInt(ins) => ins.get_all_method_handles(),
            Self::UshrInt(ins) => ins.get_all_method_handles(),
            Self::AddLong(ins) => ins.get_all_method_handles(),
            Self::SubLong(ins) => ins.get_all_method_handles(),
            Self::MulLong(ins) => ins.get_all_method_handles(),
            Self::DivLong(ins) => ins.get_all_method_handles(),
            Self::RemLong(ins) => ins.get_all_method_handles(),
            Self::AndLong(ins) => ins.get_all_method_handles(),
            Self::OrLong(ins) => ins.get_all_method_handles(),
            Self::XorLong(ins) => ins.get_all_method_handles(),
            Self::ShlLong(ins) => ins.get_all_method_handles(),
            Self::ShrLong(ins) => ins.get_all_method_handles(),
            Self::UshrLong(ins) => ins.get_all_method_handles(),
            Self::AddFloat(ins) => ins.get_all_method_handles(),
            Self::SubFloat(ins) => ins.get_all_method_handles(),
            Self::MulFloat(ins) => ins.get_all_method_handles(),
            Self::DivFloat(ins) => ins.get_all_method_handles(),
            Self::RemFloat(ins) => ins.get_all_method_handles(),
            Self::AddDouble(ins) => ins.get_all_method_handles(),
            Self::SubDouble(ins) => ins.get_all_method_handles(),
            Self::MulDouble(ins) => ins.get_all_method_handles(),
            Self::DivDouble(ins) => ins.get_all_method_handles(),
            Self::RemDouble(ins) => ins.get_all_method_handles(),
            Self::AddInt2Addr(ins) => ins.get_all_method_handles(),
            Self::SubInt2Addr(ins) => ins.get_all_method_handles(),
            Self::MulInt2Addr(ins) => ins.get_all_method_handles(),
            Self::DivInt2Addr(ins) => ins.get_all_method_handles(),
            Self::RemInt2Addr(ins) => ins.get_all_method_handles(),
            Self::AndInt2Addr(ins) => ins.get_all_method_handles(),
            Self::OrInt2Addr(ins) => ins.get_all_method_handles(),
            Self::XorInt2Addr(ins) => ins.get_all_method_handles(),
            Self::ShlInt2Addr(ins) => ins.get_all_method_handles(),
            Self::ShrInt2Addr(ins) => ins.get_all_method_handles(),
            Self::UshrInt2Addr(ins) => ins.get_all_method_handles(),
            Self::AddLong2Addr(ins) => ins.get_all_method_handles(),
            Self::SubLong2Addr(ins) => ins.get_all_method_handles(),
            Self::MulLong2Addr(ins) => ins.get_all_method_handles(),
            Self::DivLong2Addr(ins) => ins.get_all_method_handles(),
            Self::RemLong2Addr(ins) => ins.get_all_method_handles(),
            Self::AndLong2Addr(ins) => ins.get_all_method_handles(),
            Self::OrLong2Addr(ins) => ins.get_all_method_handles(),
            Self::XorLong2Addr(ins) => ins.get_all_method_handles(),
            Self::ShlLong2Addr(ins) => ins.get_all_method_handles(),
            Self::ShrLong2Addr(ins) => ins.get_all_method_handles(),
            Self::UshrLong2Addr(ins) => ins.get_all_method_handles(),
            Self::AddFloat2Addr(ins) => ins.get_all_method_handles(),
            Self::SubFloat2Addr(ins) => ins.get_all_method_handles(),
            Self::MulFloat2Addr(ins) => ins.get_all_method_handles(),
            Self::DivFloat2Addr(ins) => ins.get_all_method_handles(),
            Self::RemFloat2Addr(ins) => ins.get_all_method_handles(),
            Self::AddDouble2Addr(ins) => ins.get_all_method_handles(),
            Self::SubDouble2Addr(ins) => ins.get_all_method_handles(),
            Self::MulDouble2Addr(ins) => ins.get_all_method_handles(),
            Self::DivDouble2Addr(ins) => ins.get_all_method_handles(),
            Self::RemDouble2Addr(ins) => ins.get_all_method_handles(),
            Self::AddIntLit(ins) => ins.get_all_method_handles(),
            Self::RsubIntLit(ins) => ins.get_all_method_handles(),
            Self::MulIntLit(ins) => ins.get_all_method_handles(),
            Self::DivIntLit(ins) => ins.get_all_method_handles(),
            Self::RemIntLit(ins) => ins.get_all_method_handles(),
            Self::AndIntLit(ins) => ins.get_all_method_handles(),
            Self::OrIntLit(ins) => ins.get_all_method_handles(),
            Self::XorIntLit(ins) => ins.get_all_method_handles(),
            Self::ShlIntLit(ins) => ins.get_all_method_handles(),
            Self::ShrIntLit(ins) => ins.get_all_method_handles(),
            Self::UshrIntLit(ins) => ins.get_all_method_handles(),
            Self::InvokePolymorphic(ins) => ins.get_all_method_handles(),
            Self::InvokeCustom(ins) => ins.get_all_method_handles(),
            Self::ConstMethodHandle(ins) => ins.get_all_method_handles(),
            Self::ConstMethodType(ins) => ins.get_all_method_handles(),
            Self::Try(ins) => ins.get_all_method_handles(),
            Self::Label(ins) => ins.get_all_method_handles(),
        }
    }
}

impl<'source> FromPyObject<'source> for Instruction {
    fn extract(ob: &'source PyAny) -> PyResult<Self> {
        if let Ok(ins) = Nop::extract(ob) {
            Ok(Self::Nop(ins))
        } else if let Ok(ins) = Move::extract(ob) {
            Ok(Self::Move(ins))
        } else if let Ok(ins) = MoveWide::extract(ob) {
            Ok(Self::MoveWide(ins))
        } else if let Ok(ins) = MoveObject::extract(ob) {
            Ok(Self::MoveObject(ins))
        } else if let Ok(ins) = MoveResult::extract(ob) {
            Ok(Self::MoveResult(ins))
        } else if let Ok(ins) = MoveResultWide::extract(ob) {
            Ok(Self::MoveResultWide(ins))
        } else if let Ok(ins) = MoveException::extract(ob) {
            Ok(Self::MoveException(ins))
        } else if let Ok(ins) = MoveResultObject::extract(ob) {
            Ok(Self::MoveResultObject(ins))
        } else if let Ok(ins) = ReturnVoid::extract(ob) {
            Ok(Self::ReturnVoid(ins))
        } else if let Ok(ins) = Return::extract(ob) {
            Ok(Self::Return(ins))
        } else if let Ok(ins) = ReturnWide::extract(ob) {
            Ok(Self::ReturnWide(ins))
        } else if let Ok(ins) = ReturnObject::extract(ob) {
            Ok(Self::ReturnObject(ins))
        } else if let Ok(ins) = Const::extract(ob) {
            Ok(Self::Const(ins))
        } else if let Ok(ins) = ConstWide::extract(ob) {
            Ok(Self::ConstWide(ins))
        } else if let Ok(ins) = ConstString::extract(ob) {
            Ok(Self::ConstString(ins))
        } else if let Ok(ins) = ConstClass::extract(ob) {
            Ok(Self::ConstClass(ins))
        } else if let Ok(ins) = MonitorEnter::extract(ob) {
            Ok(Self::MonitorEnter(ins))
        } else if let Ok(ins) = MonitorExit::extract(ob) {
            Ok(Self::MonitorExit(ins))
        } else if let Ok(ins) = CheckCast::extract(ob) {
            Ok(Self::CheckCast(ins))
        } else if let Ok(ins) = InstanceOf::extract(ob) {
            Ok(Self::InstanceOf(ins))
        } else if let Ok(ins) = ArrayLength::extract(ob) {
            Ok(Self::ArrayLength(ins))
        } else if let Ok(ins) = NewInstance::extract(ob) {
            Ok(Self::NewInstance(ins))
        } else if let Ok(ins) = NewArray::extract(ob) {
            Ok(Self::NewArray(ins))
        } else if let Ok(ins) = FilledNewArray::extract(ob) {
            Ok(Self::FilledNewArray(ins))
        } else if let Ok(ins) = FillArrayData::extract(ob) {
            Ok(Self::FillArrayData(ins))
        } else if let Ok(ins) = Throw::extract(ob) {
            Ok(Self::Throw(ins))
        } else if let Ok(ins) = Goto::extract(ob) {
            Ok(Self::Goto(ins))
        } else if let Ok(ins) = Switch::extract(ob) {
            Ok(Self::Switch(ins))
        } else if let Ok(ins) = CmpLFloat::extract(ob) {
            Ok(Self::CmpLFloat(ins))
        } else if let Ok(ins) = CmpGFloat::extract(ob) {
            Ok(Self::CmpGFloat(ins))
        } else if let Ok(ins) = CmpLDouble::extract(ob) {
            Ok(Self::CmpLDouble(ins))
        } else if let Ok(ins) = CmpGDouble::extract(ob) {
            Ok(Self::CmpGDouble(ins))
        } else if let Ok(ins) = CmpLong::extract(ob) {
            Ok(Self::CmpLong(ins))
        } else if let Ok(ins) = IfEq::extract(ob) {
            Ok(Self::IfEq(ins))
        } else if let Ok(ins) = IfNe::extract(ob) {
            Ok(Self::IfNe(ins))
        } else if let Ok(ins) = IfLt::extract(ob) {
            Ok(Self::IfLt(ins))
        } else if let Ok(ins) = IfGe::extract(ob) {
            Ok(Self::IfGe(ins))
        } else if let Ok(ins) = IfGt::extract(ob) {
            Ok(Self::IfGt(ins))
        } else if let Ok(ins) = IfLe::extract(ob) {
            Ok(Self::IfLe(ins))
        } else if let Ok(ins) = IfEqZ::extract(ob) {
            Ok(Self::IfEqZ(ins))
        } else if let Ok(ins) = IfNeZ::extract(ob) {
            Ok(Self::IfNeZ(ins))
        } else if let Ok(ins) = IfLtZ::extract(ob) {
            Ok(Self::IfLtZ(ins))
        } else if let Ok(ins) = IfGeZ::extract(ob) {
            Ok(Self::IfGeZ(ins))
        } else if let Ok(ins) = IfGtZ::extract(ob) {
            Ok(Self::IfGtZ(ins))
        } else if let Ok(ins) = IfLeZ::extract(ob) {
            Ok(Self::IfLeZ(ins))
        } else if let Ok(ins) = AGet::extract(ob) {
            Ok(Self::AGet(ins))
        } else if let Ok(ins) = AGetWide::extract(ob) {
            Ok(Self::AGetWide(ins))
        } else if let Ok(ins) = AGetObject::extract(ob) {
            Ok(Self::AGetObject(ins))
        } else if let Ok(ins) = AGetBoolean::extract(ob) {
            Ok(Self::AGetBoolean(ins))
        } else if let Ok(ins) = AGetByte::extract(ob) {
            Ok(Self::AGetByte(ins))
        } else if let Ok(ins) = AGetChar::extract(ob) {
            Ok(Self::AGetChar(ins))
        } else if let Ok(ins) = AGetShort::extract(ob) {
            Ok(Self::AGetShort(ins))
        } else if let Ok(ins) = APut::extract(ob) {
            Ok(Self::APut(ins))
        } else if let Ok(ins) = APutWide::extract(ob) {
            Ok(Self::APutWide(ins))
        } else if let Ok(ins) = APutObject::extract(ob) {
            Ok(Self::APutObject(ins))
        } else if let Ok(ins) = APutBoolean::extract(ob) {
            Ok(Self::APutBoolean(ins))
        } else if let Ok(ins) = APutByte::extract(ob) {
            Ok(Self::APutByte(ins))
        } else if let Ok(ins) = APutChar::extract(ob) {
            Ok(Self::APutChar(ins))
        } else if let Ok(ins) = APutShort::extract(ob) {
            Ok(Self::APutShort(ins))
        } else if let Ok(ins) = IGet::extract(ob) {
            Ok(Self::IGet(ins))
        } else if let Ok(ins) = IGetWide::extract(ob) {
            Ok(Self::IGetWide(ins))
        } else if let Ok(ins) = IGetObject::extract(ob) {
            Ok(Self::IGetObject(ins))
        } else if let Ok(ins) = IGetBoolean::extract(ob) {
            Ok(Self::IGetBoolean(ins))
        } else if let Ok(ins) = IGetByte::extract(ob) {
            Ok(Self::IGetByte(ins))
        } else if let Ok(ins) = IGetChar::extract(ob) {
            Ok(Self::IGetChar(ins))
        } else if let Ok(ins) = IGetShort::extract(ob) {
            Ok(Self::IGetShort(ins))
        } else if let Ok(ins) = IPut::extract(ob) {
            Ok(Self::IPut(ins))
        } else if let Ok(ins) = IPutWide::extract(ob) {
            Ok(Self::IPutWide(ins))
        } else if let Ok(ins) = IPutObject::extract(ob) {
            Ok(Self::IPutObject(ins))
        } else if let Ok(ins) = IPutBoolean::extract(ob) {
            Ok(Self::IPutBoolean(ins))
        } else if let Ok(ins) = IPutByte::extract(ob) {
            Ok(Self::IPutByte(ins))
        } else if let Ok(ins) = IPutChar::extract(ob) {
            Ok(Self::IPutChar(ins))
        } else if let Ok(ins) = IPutShort::extract(ob) {
            Ok(Self::IPutShort(ins))
        } else if let Ok(ins) = SGet::extract(ob) {
            Ok(Self::SGet(ins))
        } else if let Ok(ins) = SGetWide::extract(ob) {
            Ok(Self::SGetWide(ins))
        } else if let Ok(ins) = SGetObject::extract(ob) {
            Ok(Self::SGetObject(ins))
        } else if let Ok(ins) = SGetBoolean::extract(ob) {
            Ok(Self::SGetBoolean(ins))
        } else if let Ok(ins) = SGetByte::extract(ob) {
            Ok(Self::SGetByte(ins))
        } else if let Ok(ins) = SGetChar::extract(ob) {
            Ok(Self::SGetChar(ins))
        } else if let Ok(ins) = SGetShort::extract(ob) {
            Ok(Self::SGetShort(ins))
        } else if let Ok(ins) = SPut::extract(ob) {
            Ok(Self::SPut(ins))
        } else if let Ok(ins) = SPutWide::extract(ob) {
            Ok(Self::SPutWide(ins))
        } else if let Ok(ins) = SPutObject::extract(ob) {
            Ok(Self::SPutObject(ins))
        } else if let Ok(ins) = SPutBoolean::extract(ob) {
            Ok(Self::SPutBoolean(ins))
        } else if let Ok(ins) = SPutByte::extract(ob) {
            Ok(Self::SPutByte(ins))
        } else if let Ok(ins) = SPutChar::extract(ob) {
            Ok(Self::SPutChar(ins))
        } else if let Ok(ins) = SPutShort::extract(ob) {
            Ok(Self::SPutShort(ins))
        } else if let Ok(ins) = InvokeVirtual::extract(ob) {
            Ok(Self::InvokeVirtual(ins))
        } else if let Ok(ins) = InvokeSuper::extract(ob) {
            Ok(Self::InvokeSuper(ins))
        } else if let Ok(ins) = InvokeDirect::extract(ob) {
            Ok(Self::InvokeDirect(ins))
        } else if let Ok(ins) = InvokeStatic::extract(ob) {
            Ok(Self::InvokeStatic(ins))
        } else if let Ok(ins) = InvokeInterface::extract(ob) {
            Ok(Self::InvokeInterface(ins))
        } else if let Ok(ins) = NegInt::extract(ob) {
            Ok(Self::NegInt(ins))
        } else if let Ok(ins) = NotInt::extract(ob) {
            Ok(Self::NotInt(ins))
        } else if let Ok(ins) = NegLong::extract(ob) {
            Ok(Self::NegLong(ins))
        } else if let Ok(ins) = NotLong::extract(ob) {
            Ok(Self::NotLong(ins))
        } else if let Ok(ins) = NegFloat::extract(ob) {
            Ok(Self::NegFloat(ins))
        } else if let Ok(ins) = NegDouble::extract(ob) {
            Ok(Self::NegDouble(ins))
        } else if let Ok(ins) = IntToLong::extract(ob) {
            Ok(Self::IntToLong(ins))
        } else if let Ok(ins) = IntToFloat::extract(ob) {
            Ok(Self::IntToFloat(ins))
        } else if let Ok(ins) = IntToDouble::extract(ob) {
            Ok(Self::IntToDouble(ins))
        } else if let Ok(ins) = LongToInt::extract(ob) {
            Ok(Self::LongToInt(ins))
        } else if let Ok(ins) = LongToFloat::extract(ob) {
            Ok(Self::LongToFloat(ins))
        } else if let Ok(ins) = LongToDouble::extract(ob) {
            Ok(Self::LongToDouble(ins))
        } else if let Ok(ins) = FloatToInt::extract(ob) {
            Ok(Self::FloatToInt(ins))
        } else if let Ok(ins) = FloatToLong::extract(ob) {
            Ok(Self::FloatToLong(ins))
        } else if let Ok(ins) = FloatToDouble::extract(ob) {
            Ok(Self::FloatToDouble(ins))
        } else if let Ok(ins) = DoubleToInt::extract(ob) {
            Ok(Self::DoubleToInt(ins))
        } else if let Ok(ins) = DoubleToLong::extract(ob) {
            Ok(Self::DoubleToLong(ins))
        } else if let Ok(ins) = DoubleToFloat::extract(ob) {
            Ok(Self::DoubleToFloat(ins))
        } else if let Ok(ins) = IntToByte::extract(ob) {
            Ok(Self::IntToByte(ins))
        } else if let Ok(ins) = IntToChar::extract(ob) {
            Ok(Self::IntToChar(ins))
        } else if let Ok(ins) = IntToShort::extract(ob) {
            Ok(Self::IntToShort(ins))
        } else if let Ok(ins) = AddInt::extract(ob) {
            Ok(Self::AddInt(ins))
        } else if let Ok(ins) = SubInt::extract(ob) {
            Ok(Self::SubInt(ins))
        } else if let Ok(ins) = MulInt::extract(ob) {
            Ok(Self::MulInt(ins))
        } else if let Ok(ins) = DivInt::extract(ob) {
            Ok(Self::DivInt(ins))
        } else if let Ok(ins) = RemInt::extract(ob) {
            Ok(Self::RemInt(ins))
        } else if let Ok(ins) = AndInt::extract(ob) {
            Ok(Self::AndInt(ins))
        } else if let Ok(ins) = OrInt::extract(ob) {
            Ok(Self::OrInt(ins))
        } else if let Ok(ins) = XorInt::extract(ob) {
            Ok(Self::XorInt(ins))
        } else if let Ok(ins) = ShlInt::extract(ob) {
            Ok(Self::ShlInt(ins))
        } else if let Ok(ins) = ShrInt::extract(ob) {
            Ok(Self::ShrInt(ins))
        } else if let Ok(ins) = UshrInt::extract(ob) {
            Ok(Self::UshrInt(ins))
        } else if let Ok(ins) = AddLong::extract(ob) {
            Ok(Self::AddLong(ins))
        } else if let Ok(ins) = SubLong::extract(ob) {
            Ok(Self::SubLong(ins))
        } else if let Ok(ins) = MulLong::extract(ob) {
            Ok(Self::MulLong(ins))
        } else if let Ok(ins) = DivLong::extract(ob) {
            Ok(Self::DivLong(ins))
        } else if let Ok(ins) = RemLong::extract(ob) {
            Ok(Self::RemLong(ins))
        } else if let Ok(ins) = AndLong::extract(ob) {
            Ok(Self::AndLong(ins))
        } else if let Ok(ins) = OrLong::extract(ob) {
            Ok(Self::OrLong(ins))
        } else if let Ok(ins) = XorLong::extract(ob) {
            Ok(Self::XorLong(ins))
        } else if let Ok(ins) = ShlLong::extract(ob) {
            Ok(Self::ShlLong(ins))
        } else if let Ok(ins) = ShrLong::extract(ob) {
            Ok(Self::ShrLong(ins))
        } else if let Ok(ins) = UshrLong::extract(ob) {
            Ok(Self::UshrLong(ins))
        } else if let Ok(ins) = AddFloat::extract(ob) {
            Ok(Self::AddFloat(ins))
        } else if let Ok(ins) = SubFloat::extract(ob) {
            Ok(Self::SubFloat(ins))
        } else if let Ok(ins) = MulFloat::extract(ob) {
            Ok(Self::MulFloat(ins))
        } else if let Ok(ins) = DivFloat::extract(ob) {
            Ok(Self::DivFloat(ins))
        } else if let Ok(ins) = RemFloat::extract(ob) {
            Ok(Self::RemFloat(ins))
        } else if let Ok(ins) = AddDouble::extract(ob) {
            Ok(Self::AddDouble(ins))
        } else if let Ok(ins) = SubDouble::extract(ob) {
            Ok(Self::SubDouble(ins))
        } else if let Ok(ins) = MulDouble::extract(ob) {
            Ok(Self::MulDouble(ins))
        } else if let Ok(ins) = DivDouble::extract(ob) {
            Ok(Self::DivDouble(ins))
        } else if let Ok(ins) = RemDouble::extract(ob) {
            Ok(Self::RemDouble(ins))
        } else if let Ok(ins) = AddInt2Addr::extract(ob) {
            Ok(Self::AddInt2Addr(ins))
        } else if let Ok(ins) = SubInt2Addr::extract(ob) {
            Ok(Self::SubInt2Addr(ins))
        } else if let Ok(ins) = MulInt2Addr::extract(ob) {
            Ok(Self::MulInt2Addr(ins))
        } else if let Ok(ins) = DivInt2Addr::extract(ob) {
            Ok(Self::DivInt2Addr(ins))
        } else if let Ok(ins) = RemInt2Addr::extract(ob) {
            Ok(Self::RemInt2Addr(ins))
        } else if let Ok(ins) = AndInt2Addr::extract(ob) {
            Ok(Self::AndInt2Addr(ins))
        } else if let Ok(ins) = OrInt2Addr::extract(ob) {
            Ok(Self::OrInt2Addr(ins))
        } else if let Ok(ins) = XorInt2Addr::extract(ob) {
            Ok(Self::XorInt2Addr(ins))
        } else if let Ok(ins) = ShlInt2Addr::extract(ob) {
            Ok(Self::ShlInt2Addr(ins))
        } else if let Ok(ins) = ShrInt2Addr::extract(ob) {
            Ok(Self::ShrInt2Addr(ins))
        } else if let Ok(ins) = UshrInt2Addr::extract(ob) {
            Ok(Self::UshrInt2Addr(ins))
        } else if let Ok(ins) = AddLong2Addr::extract(ob) {
            Ok(Self::AddLong2Addr(ins))
        } else if let Ok(ins) = SubLong2Addr::extract(ob) {
            Ok(Self::SubLong2Addr(ins))
        } else if let Ok(ins) = MulLong2Addr::extract(ob) {
            Ok(Self::MulLong2Addr(ins))
        } else if let Ok(ins) = DivLong2Addr::extract(ob) {
            Ok(Self::DivLong2Addr(ins))
        } else if let Ok(ins) = RemLong2Addr::extract(ob) {
            Ok(Self::RemLong2Addr(ins))
        } else if let Ok(ins) = AndLong2Addr::extract(ob) {
            Ok(Self::AndLong2Addr(ins))
        } else if let Ok(ins) = OrLong2Addr::extract(ob) {
            Ok(Self::OrLong2Addr(ins))
        } else if let Ok(ins) = XorLong2Addr::extract(ob) {
            Ok(Self::XorLong2Addr(ins))
        } else if let Ok(ins) = ShlLong2Addr::extract(ob) {
            Ok(Self::ShlLong2Addr(ins))
        } else if let Ok(ins) = ShrLong2Addr::extract(ob) {
            Ok(Self::ShrLong2Addr(ins))
        } else if let Ok(ins) = UshrLong2Addr::extract(ob) {
            Ok(Self::UshrLong2Addr(ins))
        } else if let Ok(ins) = AddFloat2Addr::extract(ob) {
            Ok(Self::AddFloat2Addr(ins))
        } else if let Ok(ins) = SubFloat2Addr::extract(ob) {
            Ok(Self::SubFloat2Addr(ins))
        } else if let Ok(ins) = MulFloat2Addr::extract(ob) {
            Ok(Self::MulFloat2Addr(ins))
        } else if let Ok(ins) = DivFloat2Addr::extract(ob) {
            Ok(Self::DivFloat2Addr(ins))
        } else if let Ok(ins) = RemFloat2Addr::extract(ob) {
            Ok(Self::RemFloat2Addr(ins))
        } else if let Ok(ins) = AddDouble2Addr::extract(ob) {
            Ok(Self::AddDouble2Addr(ins))
        } else if let Ok(ins) = SubDouble2Addr::extract(ob) {
            Ok(Self::SubDouble2Addr(ins))
        } else if let Ok(ins) = MulDouble2Addr::extract(ob) {
            Ok(Self::MulDouble2Addr(ins))
        } else if let Ok(ins) = DivDouble2Addr::extract(ob) {
            Ok(Self::DivDouble2Addr(ins))
        } else if let Ok(ins) = RemDouble2Addr::extract(ob) {
            Ok(Self::RemDouble2Addr(ins))
        } else if let Ok(ins) = AddIntLit::extract(ob) {
            Ok(Self::AddIntLit(ins))
        } else if let Ok(ins) = RsubIntLit::extract(ob) {
            Ok(Self::RsubIntLit(ins))
        } else if let Ok(ins) = MulIntLit::extract(ob) {
            Ok(Self::MulIntLit(ins))
        } else if let Ok(ins) = DivIntLit::extract(ob) {
            Ok(Self::DivIntLit(ins))
        } else if let Ok(ins) = RemIntLit::extract(ob) {
            Ok(Self::RemIntLit(ins))
        } else if let Ok(ins) = AndIntLit::extract(ob) {
            Ok(Self::AndIntLit(ins))
        } else if let Ok(ins) = OrIntLit::extract(ob) {
            Ok(Self::OrIntLit(ins))
        } else if let Ok(ins) = XorIntLit::extract(ob) {
            Ok(Self::XorIntLit(ins))
        } else if let Ok(ins) = ShlIntLit::extract(ob) {
            Ok(Self::ShlIntLit(ins))
        } else if let Ok(ins) = ShrIntLit::extract(ob) {
            Ok(Self::ShrIntLit(ins))
        } else if let Ok(ins) = UshrIntLit::extract(ob) {
            Ok(Self::UshrIntLit(ins))
        } else if let Ok(ins) = InvokePolymorphic::extract(ob) {
            Ok(Self::InvokePolymorphic(ins))
        } else if let Ok(ins) = InvokeCustom::extract(ob) {
            Ok(Self::InvokeCustom(ins))
        } else if let Ok(ins) = ConstMethodHandle::extract(ob) {
            Ok(Self::ConstMethodHandle(ins))
        } else if let Ok(ins) = ConstMethodType::extract(ob) {
            Ok(Self::ConstMethodType(ins))
        } else if let Ok(ins) = Try::extract(ob) {
            Ok(Self::Try(ins))
        } else if let Ok(ins) = Label::extract(ob) {
            Ok(Self::Label(ins))
        } else {
            Err(PyErr::new::<PyTypeError, _>(format!(
                "{} is not a castable as an Instruction",
                ob.repr()?
            )))
        }
    }
}

impl IntoPy<PyObject> for Instruction {
    fn into_py(self, py: Python<'_>) -> PyObject {
        match self {
            Self::Nop(ins) => ins.into_py(py),
            Self::Move(ins) => ins.into_py(py),
            Self::MoveWide(ins) => ins.into_py(py),
            Self::MoveObject(ins) => ins.into_py(py),
            Self::MoveResult(ins) => ins.into_py(py),
            Self::MoveResultWide(ins) => ins.into_py(py),
            Self::MoveException(ins) => ins.into_py(py),
            Self::MoveResultObject(ins) => ins.into_py(py),
            Self::ReturnVoid(ins) => ins.into_py(py),
            Self::Return(ins) => ins.into_py(py),
            Self::ReturnWide(ins) => ins.into_py(py),
            Self::ReturnObject(ins) => ins.into_py(py),
            Self::Const(ins) => ins.into_py(py),
            Self::ConstWide(ins) => ins.into_py(py),
            Self::ConstString(ins) => ins.into_py(py),
            Self::ConstClass(ins) => ins.into_py(py),
            Self::MonitorEnter(ins) => ins.into_py(py),
            Self::MonitorExit(ins) => ins.into_py(py),
            Self::CheckCast(ins) => ins.into_py(py),
            Self::InstanceOf(ins) => ins.into_py(py),
            Self::ArrayLength(ins) => ins.into_py(py),
            Self::NewInstance(ins) => ins.into_py(py),
            Self::NewArray(ins) => ins.into_py(py),
            Self::FilledNewArray(ins) => ins.into_py(py),
            Self::FillArrayData(ins) => ins.into_py(py),
            Self::Throw(ins) => ins.into_py(py),
            Self::Goto(ins) => ins.into_py(py),
            Self::Switch(ins) => ins.into_py(py),
            Self::CmpLFloat(ins) => ins.into_py(py),
            Self::CmpGFloat(ins) => ins.into_py(py),
            Self::CmpLDouble(ins) => ins.into_py(py),
            Self::CmpGDouble(ins) => ins.into_py(py),
            Self::CmpLong(ins) => ins.into_py(py),
            Self::IfEq(ins) => ins.into_py(py),
            Self::IfNe(ins) => ins.into_py(py),
            Self::IfLt(ins) => ins.into_py(py),
            Self::IfGe(ins) => ins.into_py(py),
            Self::IfGt(ins) => ins.into_py(py),
            Self::IfLe(ins) => ins.into_py(py),
            Self::IfEqZ(ins) => ins.into_py(py),
            Self::IfNeZ(ins) => ins.into_py(py),
            Self::IfLtZ(ins) => ins.into_py(py),
            Self::IfGeZ(ins) => ins.into_py(py),
            Self::IfGtZ(ins) => ins.into_py(py),
            Self::IfLeZ(ins) => ins.into_py(py),
            Self::AGet(ins) => ins.into_py(py),
            Self::AGetWide(ins) => ins.into_py(py),
            Self::AGetObject(ins) => ins.into_py(py),
            Self::AGetBoolean(ins) => ins.into_py(py),
            Self::AGetByte(ins) => ins.into_py(py),
            Self::AGetChar(ins) => ins.into_py(py),
            Self::AGetShort(ins) => ins.into_py(py),
            Self::APut(ins) => ins.into_py(py),
            Self::APutWide(ins) => ins.into_py(py),
            Self::APutObject(ins) => ins.into_py(py),
            Self::APutBoolean(ins) => ins.into_py(py),
            Self::APutByte(ins) => ins.into_py(py),
            Self::APutChar(ins) => ins.into_py(py),
            Self::APutShort(ins) => ins.into_py(py),
            Self::IGet(ins) => ins.into_py(py),
            Self::IGetWide(ins) => ins.into_py(py),
            Self::IGetObject(ins) => ins.into_py(py),
            Self::IGetBoolean(ins) => ins.into_py(py),
            Self::IGetByte(ins) => ins.into_py(py),
            Self::IGetChar(ins) => ins.into_py(py),
            Self::IGetShort(ins) => ins.into_py(py),
            Self::IPut(ins) => ins.into_py(py),
            Self::IPutWide(ins) => ins.into_py(py),
            Self::IPutObject(ins) => ins.into_py(py),
            Self::IPutBoolean(ins) => ins.into_py(py),
            Self::IPutByte(ins) => ins.into_py(py),
            Self::IPutChar(ins) => ins.into_py(py),
            Self::IPutShort(ins) => ins.into_py(py),
            Self::SGet(ins) => ins.into_py(py),
            Self::SGetWide(ins) => ins.into_py(py),
            Self::SGetObject(ins) => ins.into_py(py),
            Self::SGetBoolean(ins) => ins.into_py(py),
            Self::SGetByte(ins) => ins.into_py(py),
            Self::SGetChar(ins) => ins.into_py(py),
            Self::SGetShort(ins) => ins.into_py(py),
            Self::SPut(ins) => ins.into_py(py),
            Self::SPutWide(ins) => ins.into_py(py),
            Self::SPutObject(ins) => ins.into_py(py),
            Self::SPutBoolean(ins) => ins.into_py(py),
            Self::SPutByte(ins) => ins.into_py(py),
            Self::SPutChar(ins) => ins.into_py(py),
            Self::SPutShort(ins) => ins.into_py(py),
            Self::InvokeVirtual(ins) => ins.into_py(py),
            Self::InvokeSuper(ins) => ins.into_py(py),
            Self::InvokeDirect(ins) => ins.into_py(py),
            Self::InvokeStatic(ins) => ins.into_py(py),
            Self::InvokeInterface(ins) => ins.into_py(py),
            Self::NegInt(ins) => ins.into_py(py),
            Self::NotInt(ins) => ins.into_py(py),
            Self::NegLong(ins) => ins.into_py(py),
            Self::NotLong(ins) => ins.into_py(py),
            Self::NegFloat(ins) => ins.into_py(py),
            Self::NegDouble(ins) => ins.into_py(py),
            Self::IntToLong(ins) => ins.into_py(py),
            Self::IntToFloat(ins) => ins.into_py(py),
            Self::IntToDouble(ins) => ins.into_py(py),
            Self::LongToInt(ins) => ins.into_py(py),
            Self::LongToFloat(ins) => ins.into_py(py),
            Self::LongToDouble(ins) => ins.into_py(py),
            Self::FloatToInt(ins) => ins.into_py(py),
            Self::FloatToLong(ins) => ins.into_py(py),
            Self::FloatToDouble(ins) => ins.into_py(py),
            Self::DoubleToInt(ins) => ins.into_py(py),
            Self::DoubleToLong(ins) => ins.into_py(py),
            Self::DoubleToFloat(ins) => ins.into_py(py),
            Self::IntToByte(ins) => ins.into_py(py),
            Self::IntToChar(ins) => ins.into_py(py),
            Self::IntToShort(ins) => ins.into_py(py),
            Self::AddInt(ins) => ins.into_py(py),
            Self::SubInt(ins) => ins.into_py(py),
            Self::MulInt(ins) => ins.into_py(py),
            Self::DivInt(ins) => ins.into_py(py),
            Self::RemInt(ins) => ins.into_py(py),
            Self::AndInt(ins) => ins.into_py(py),
            Self::OrInt(ins) => ins.into_py(py),
            Self::XorInt(ins) => ins.into_py(py),
            Self::ShlInt(ins) => ins.into_py(py),
            Self::ShrInt(ins) => ins.into_py(py),
            Self::UshrInt(ins) => ins.into_py(py),
            Self::AddLong(ins) => ins.into_py(py),
            Self::SubLong(ins) => ins.into_py(py),
            Self::MulLong(ins) => ins.into_py(py),
            Self::DivLong(ins) => ins.into_py(py),
            Self::RemLong(ins) => ins.into_py(py),
            Self::AndLong(ins) => ins.into_py(py),
            Self::OrLong(ins) => ins.into_py(py),
            Self::XorLong(ins) => ins.into_py(py),
            Self::ShlLong(ins) => ins.into_py(py),
            Self::ShrLong(ins) => ins.into_py(py),
            Self::UshrLong(ins) => ins.into_py(py),
            Self::AddFloat(ins) => ins.into_py(py),
            Self::SubFloat(ins) => ins.into_py(py),
            Self::MulFloat(ins) => ins.into_py(py),
            Self::DivFloat(ins) => ins.into_py(py),
            Self::RemFloat(ins) => ins.into_py(py),
            Self::AddDouble(ins) => ins.into_py(py),
            Self::SubDouble(ins) => ins.into_py(py),
            Self::MulDouble(ins) => ins.into_py(py),
            Self::DivDouble(ins) => ins.into_py(py),
            Self::RemDouble(ins) => ins.into_py(py),
            Self::AddInt2Addr(ins) => ins.into_py(py),
            Self::SubInt2Addr(ins) => ins.into_py(py),
            Self::MulInt2Addr(ins) => ins.into_py(py),
            Self::DivInt2Addr(ins) => ins.into_py(py),
            Self::RemInt2Addr(ins) => ins.into_py(py),
            Self::AndInt2Addr(ins) => ins.into_py(py),
            Self::OrInt2Addr(ins) => ins.into_py(py),
            Self::XorInt2Addr(ins) => ins.into_py(py),
            Self::ShlInt2Addr(ins) => ins.into_py(py),
            Self::ShrInt2Addr(ins) => ins.into_py(py),
            Self::UshrInt2Addr(ins) => ins.into_py(py),
            Self::AddLong2Addr(ins) => ins.into_py(py),
            Self::SubLong2Addr(ins) => ins.into_py(py),
            Self::MulLong2Addr(ins) => ins.into_py(py),
            Self::DivLong2Addr(ins) => ins.into_py(py),
            Self::RemLong2Addr(ins) => ins.into_py(py),
            Self::AndLong2Addr(ins) => ins.into_py(py),
            Self::OrLong2Addr(ins) => ins.into_py(py),
            Self::XorLong2Addr(ins) => ins.into_py(py),
            Self::ShlLong2Addr(ins) => ins.into_py(py),
            Self::ShrLong2Addr(ins) => ins.into_py(py),
            Self::UshrLong2Addr(ins) => ins.into_py(py),
            Self::AddFloat2Addr(ins) => ins.into_py(py),
            Self::SubFloat2Addr(ins) => ins.into_py(py),
            Self::MulFloat2Addr(ins) => ins.into_py(py),
            Self::DivFloat2Addr(ins) => ins.into_py(py),
            Self::RemFloat2Addr(ins) => ins.into_py(py),
            Self::AddDouble2Addr(ins) => ins.into_py(py),
            Self::SubDouble2Addr(ins) => ins.into_py(py),
            Self::MulDouble2Addr(ins) => ins.into_py(py),
            Self::DivDouble2Addr(ins) => ins.into_py(py),
            Self::RemDouble2Addr(ins) => ins.into_py(py),
            Self::AddIntLit(ins) => ins.into_py(py),
            Self::RsubIntLit(ins) => ins.into_py(py),
            Self::MulIntLit(ins) => ins.into_py(py),
            Self::DivIntLit(ins) => ins.into_py(py),
            Self::RemIntLit(ins) => ins.into_py(py),
            Self::AndIntLit(ins) => ins.into_py(py),
            Self::OrIntLit(ins) => ins.into_py(py),
            Self::XorIntLit(ins) => ins.into_py(py),
            Self::ShlIntLit(ins) => ins.into_py(py),
            Self::ShrIntLit(ins) => ins.into_py(py),
            Self::UshrIntLit(ins) => ins.into_py(py),
            Self::InvokePolymorphic(ins) => ins.into_py(py),
            Self::InvokeCustom(ins) => ins.into_py(py),
            Self::ConstMethodHandle(ins) => ins.into_py(py),
            Self::ConstMethodType(ins) => ins.into_py(py),
            Self::Try(ins) => ins.into_py(py),
            Self::Label(ins) => ins.into_py(py),
        }
    }
}

#[pyclass]
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct CallSite {
    #[pyo3(get)]
    pub method_handle: MethodHandle,
    #[pyo3(get)]
    pub name: DexString,
    #[pyo3(get)]
    pub type_: IdMethodType,
    #[pyo3(get)]
    pub args: Vec<DexValue>,
}

#[pymethods]
impl CallSite {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(
        method_handle: MethodHandle,
        name: DexString,
        type_: IdMethodType,
        args: Vec<DexValue>,
    ) -> Self {
        let args = args.to_vec();
        Self {
            method_handle,
            name,
            type_,
            args,
        }
    }

    pub fn __str__(&self) -> String {
        let args = self
            .args
            .iter()
            .map(|arg| arg.__str__())
            .collect::<Vec<_>>()
            .join(", ");
        format!(
            "call({} {} {} ({})",
            self.method_handle.__str__(),
            self.type_.__str__(),
            self.name.__str__(),
            args
        )
    }

    pub fn __repr__(&self) -> String {
        let args = self
            .args
            .iter()
            .map(|arg| arg.__str__())
            .collect::<Vec<_>>()
            .join(", ");
        format!(
            "CallSite({}, {}, {}, [{}]",
            self.method_handle.__repr__(),
            self.type_.__repr__(),
            self.name.__repr__(),
            args
        )
    }

    /// Return all strings referenced in the call site.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        let mut strings = HashSet::new();
        strings.insert(self.name.clone());
        strings.extend(self.method_handle.get_all_strings());
        strings.extend(self.type_.get_all_strings());
        for arg in &self.args {
            strings.extend(arg.get_all_strings());
        }
        strings
    }

    /// Return all types referenced in the call site.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        let mut type_ids = HashSet::new();
        type_ids.extend(self.method_handle.get_all_types());
        type_ids.extend(self.type_.get_all_types());
        for arg in &self.args {
            type_ids.extend(arg.get_all_types());
        }
        type_ids
    }

    /// Return all prototypes referenced in the call site.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        let mut protos = HashSet::new();
        protos.extend(self.method_handle.get_all_protos());
        protos.insert(self.type_.clone());
        for arg in &self.args {
            protos.extend(arg.get_all_protos());
        }
        protos
    }

    /// Return all field ids referenced in the call site.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.extend(self.method_handle.get_all_field_ids());
        for arg in &self.args {
            fields.extend(arg.get_all_field_ids());
        }
        fields
    }

    /// Return all method ids referenced in the call site.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        let mut methods = HashSet::new();
        methods.extend(self.method_handle.get_all_method_ids());
        for arg in &self.args {
            methods.extend(arg.get_all_method_ids());
        }
        methods
    }

    /// Return all method handles referenced in the call site.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        let mut handles = HashSet::new();
        handles.insert(self.method_handle.clone());
        for arg in &self.args {
            handles.extend(arg.get_all_method_handles());
        }
        handles
    }
}

/// Waste a cycle.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct Nop;

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

#[pymethods]
impl Nop {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new() -> Self {
        Self
    }

    pub fn __str__(&self) -> String {
        "nop".into()
    }

    pub fn __repr__(&self) -> String {
        "Instruction(Nop)".into()
    }

    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl Nop {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format10X { op: 0x00 }
    }
}

/// Move contents of a non object register to another.
///
/// Can represent several dalvik instructions :
///
/// move vA, vB
/// move/from16 vAA, vBBBB
/// move/16 vAAAA, vBBBB
///
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct Move {
    #[pyo3(get)]
    pub from: u16,
    #[pyo3(get)]
    pub to: u16,
}

#[pymethods]
impl Move {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u16, from: u16) -> Self {
        Self { from, to }
    }

    pub fn __str__(&self) -> String {
        format!("move {} {}", self.to, self.from)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(Move({}, {}))", self.to, self.from)
    }

    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl Move {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        match (self.to, self.from) {
            (0..=0b0000_1111, 0..=0b0000_1111) => InsFormat::Format12X {
                op: 0x01,
                va: self.to as u8,
                vb: self.from as u8,
            },
            (0..=0xff, _) => InsFormat::Format22X {
                op: 0x02,
                va: self.to as u8,
                vb: self.from,
            },
            (_, _) => InsFormat::Format32X {
                op: 0x03,
                va: self.to,
                vb: self.from,
            },
        }
    }
}

/// Move contents of a register pair to another.
///
/// Can represent several dalvik instructions :
///
/// move-wide vA, vB
/// move-wide/from16 vAA, vBBBB
/// move-wide/16 vAAAA, vBBBB
///
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MoveWide {
    #[pyo3(get)]
    pub from: u16,
    #[pyo3(get)]
    pub to: u16,
}

#[pymethods]
impl MoveWide {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u16, from: u16) -> Self {
        Self { from, to }
    }

    pub fn __str__(&self) -> String {
        format!("move-wide {} {}", self.to, self.from)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MoveWide({}, {}))", self.to, self.from)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MoveWide {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        match (self.to, self.from) {
            (0..=0b0000_1111, 0..=0b0000_1111) => InsFormat::Format12X {
                op: 0x04,
                va: self.to as u8,
                vb: self.from as u8,
            },
            (0..=0xff, _) => InsFormat::Format22X {
                op: 0x05,
                va: self.to as u8,
                vb: self.from,
            },
            (_, _) => InsFormat::Format32X {
                op: 0x06,
                va: self.to,
                vb: self.from,
            },
        }
    }
}

/// Move contents of an object bearing register to another.
///
/// Can represent several dalvik instructions :
///
/// move-object vA, vB
/// move-object/from16 vAA, vBBBB
/// move-object/16 vAAAA, vBBBB
///
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MoveObject {
    #[pyo3(get)]
    pub from: u16,
    #[pyo3(get)]
    pub to: u16,
}

#[pymethods]
impl MoveObject {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u16, from: u16) -> Self {
        Self { from, to }
    }

    pub fn __str__(&self) -> String {
        format!("move-object {} {}", self.to, self.from)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MoveObject({}, {}))", self.to, self.from)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MoveObject {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        match (self.to, self.from) {
            (0..=0b0000_1111, 0..=0b0000_1111) => InsFormat::Format12X {
                op: 0x07,
                va: self.to as u8,
                vb: self.from as u8,
            },
            (0..=0xff, _) => InsFormat::Format22X {
                op: 0x08,
                va: self.to as u8,
                vb: self.from,
            },
            (_, _) => InsFormat::Format32X {
                op: 0x09,
                va: self.to,
                vb: self.from,
            },
        }
    }
}

/// Move the single word non object result of the preciding invoke-kind into a register.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MoveResult {
    #[pyo3(get)]
    pub to: u8,
}

#[pymethods]
impl MoveResult {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8) -> Self {
        Self { to }
    }

    pub fn __str__(&self) -> String {
        format!("move-result {}", self.to)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MoveResult({}))", self.to)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MoveResult {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format11X {
            op: 0x0a,
            va: self.to,
        }
    }
}

/// Move the double word non object result of the preciding invoke-kind into a register pair.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MoveResultWide {
    #[pyo3(get)]
    pub to: u8,
}

#[pymethods]
impl MoveResultWide {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8) -> Self {
        Self { to }
    }

    pub fn __str__(&self) -> String {
        format!("move-result-wide {}", self.to)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MoveResultWide({}))", self.to)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MoveResultWide {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format11X {
            op: 0x0b,
            va: self.to,
        }
    }
}

/// Move the object result of the preciding invoke-kind or filled-new-array into a register.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MoveResultObject {
    #[pyo3(get)]
    pub to: u8,
}

#[pymethods]
impl MoveResultObject {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8) -> Self {
        Self { to }
    }

    pub fn __str__(&self) -> String {
        format!("move-result-object {}", self.to)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MoveResultObject({}))", self.to)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MoveResultObject {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format11X {
            op: 0x0c,
            va: self.to,
        }
    }
}

/// Move the just caught exception into a register.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MoveException {
    #[pyo3(get)]
    pub to: u8,
}

#[pymethods]
impl MoveException {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8) -> Self {
        Self { to }
    }

    pub fn __str__(&self) -> String {
        format!("move-exception {}", self.to)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MoveException({}))", self.to)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MoveException {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format11X {
            op: 0x0d,
            va: self.to,
        }
    }
}

/// Return a void method
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ReturnVoid;

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

#[pymethods]
impl ReturnVoid {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new() -> Self {
        Self
    }

    pub fn __str__(&self) -> String {
        "return-void".into()
    }

    pub fn __repr__(&self) -> String {
        "Instruction(ReturnVoid)".into()
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ReturnVoid {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format10X { op: 0x0e }
    }
}

/// Return the 32 bits non object value from the method.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct Return {
    #[pyo3(get)]
    pub reg: u8,
}

#[pymethods]
impl Return {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8) -> Self {
        Self { reg }
    }

    pub fn __str__(&self) -> String {
        format!("return {}", self.reg)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(Return({}))", self.reg)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl Return {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format11X {
            op: 0x0f,
            va: self.reg,
        }
    }
}

/// Return the 64 bits non object value from the method.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ReturnWide {
    #[pyo3(get)]
    pub reg: u8,
}

#[pymethods]
impl ReturnWide {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8) -> Self {
        Self { reg }
    }

    pub fn __str__(&self) -> String {
        format!("return-wide {}", self.reg)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(ReturnWide({}))", self.reg)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ReturnWide {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format11X {
            op: 0x10,
            va: self.reg,
        }
    }
}

/// Return the object value from the method.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ReturnObject {
    #[pyo3(get)]
    pub reg: u8,
}

#[pymethods]
impl ReturnObject {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8) -> Self {
        Self { reg }
    }

    pub fn __str__(&self) -> String {
        format!("return-object {}", self.reg)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(ReturnObject({}))", self.reg)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ReturnObject {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format11X {
            op: 0x11,
            va: self.reg,
        }
    }
}

/// Move a literal to a register.
///
/// Can represent several dalvik instructions :
///
/// const/4 vA #+B
/// const/16 vAA #+BBBB
/// const vAA #+BBBBBBBB
/// const/high 16 vAA #+BBBB0000
///
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct Const {
    #[pyo3(get)]
    pub reg: u8,
    #[pyo3(get)]
    pub lit: i32,
}

#[pymethods]
impl Const {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8, lit: i32) -> Self {
        Self { reg, lit }
    }

    pub fn __str__(&self) -> String {
        format!("const {} {}", self.reg, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(Const({}, {}))", self.reg, self.lit)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl Const {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        match (self.reg, self.lit) {
            (0..=0b0000_1111, -8..=7) => InsFormat::Format11N {
                op: 0x12,
                va: self.reg,
                b: self.lit as i8,
            },
            (_, I16_MIN_AS_I32..=I16_MAX_AS_I32) => InsFormat::Format21S {
                op: 0x13,
                va: self.reg,
                b: self.lit as i16,
            },
            (_, lit) if lit % 0x1_0000 == 0 => InsFormat::Format21H {
                op: 0x15,
                va: self.reg,
                b: (self.lit / 0x1_0000) as i16,
            },
            (_, _) => InsFormat::Format31I {
                op: 0x14,
                va: self.reg,
                b: self.lit,
            },
        }
    }
}

/// Move a literal to a register pair (64bits).
///
/// Can represent several dalvik instructions :
///
/// const-wide/16 vAA #+BBBB
/// const-wide/32 vAA #+BBBBBBBB
/// const-wide vAA #+BBBBBBBBBBBBBBBB
/// const-wide/hight 16 vAA #+BBBB000000000000
///
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ConstWide {
    #[pyo3(get)]
    pub reg: u8,
    #[pyo3(get)]
    pub lit: i64,
}

#[pymethods]
impl ConstWide {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8, lit: i64) -> Self {
        Self { reg, lit }
    }

    pub fn __str__(&self) -> String {
        format!("const-wide {} {}", self.reg, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(ConstWide({}, {}))", self.reg, self.lit)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ConstWide {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        match self.lit {
            I16_MIN_AS_I64..=I16_MAX_AS_I64 => InsFormat::Format21S {
                op: 0x16,
                va: self.reg,
                b: self.lit as i16,
            },
            I32_MIN_AS_I64..=I32_MAX_AS_I64 => InsFormat::Format31I {
                op: 0x17,
                va: self.reg,
                b: self.lit as i32,
            },
            lit if lit % 0x1_0000_0000_0000 == 0 => InsFormat::Format21H {
                op: 0x19,
                va: self.reg,
                b: (self.lit / 0x1_0000_0000_0000) as i16,
            },
            _ => InsFormat::Format51L {
                op: 0x18,
                va: self.reg,
                b: self.lit,
            },
        }
    }
}

/// Move a reference to a string in a register.
///
/// Can represent several dalvik instructions :
///
/// const-string vAA string@BBBB
/// const-string/jumbo vAA string@BBBBBBBB
///
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct ConstString {
    #[pyo3(get)]
    pub reg: u8,
    #[pyo3(get)]
    pub lit: DexString,
}

#[pymethods]
impl ConstString {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8, lit: DexString) -> Self {
        Self { reg, lit }
    }

    pub fn __str__(&self) -> String {
        format!("const-string {} \"{}\"", self.reg, self.lit.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(ConstString({}, {}))",
            self.reg,
            self.lit.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        4
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        6
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        let mut strings = HashSet::new();
        strings.insert(self.lit.clone());
        strings
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ConstString {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self, string_idx: usize) -> InsFormat {
        match string_idx {
            0..=U16_MAX_AS_USIZE => InsFormat::Format21C {
                op: 0x1a,
                va: self.reg,
                b: string_idx as u16,
            },
            _ => InsFormat::Format31C {
                op: 0x1b,
                va: self.reg,
                b: string_idx as u32,
            },
        }
    }
}

/// Move a reference to a class in a register.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct ConstClass {
    #[pyo3(get)]
    pub reg: u8,
    #[pyo3(get)]
    pub lit: IdType,
}

#[pymethods]
impl ConstClass {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8, lit: IdType) -> Self {
        Self { reg, lit }
    }

    pub fn __str__(&self) -> String {
        format!("const-class {} \"{}\"", self.reg, self.lit.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(ConstClass({}, {}))",
            self.reg,
            self.lit.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.lit.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        let mut types = HashSet::new();
        types.insert(self.lit.clone());
        types
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ConstClass {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self, class_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x1c,
            va: self.reg,
            b: class_idx as u16,
        }
    }
}

/// Acquire the monitor for the object in the register.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MonitorEnter {
    #[pyo3(get)]
    pub reg: u8,
}

#[pymethods]
impl MonitorEnter {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8) -> Self {
        Self { reg }
    }

    pub fn __str__(&self) -> String {
        format!("monitor-enter {}", self.reg)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MonitorEnter({}))", self.reg)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MonitorEnter {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format11X {
            op: 0x1d,
            va: self.reg,
        }
    }
}

/// Release the monitor for the object in the register.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MonitorExit {
    #[pyo3(get)]
    pub reg: u8,
}

#[pymethods]
impl MonitorExit {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8) -> Self {
        Self { reg }
    }

    pub fn __str__(&self) -> String {
        format!("monitor-exit {}", self.reg)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MonitorExit({}))", self.reg)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MonitorExit {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format11X {
            op: 0x1e,
            va: self.reg,
        }
    }
}

/// Check if the object in the register can be cast to the type.
/// (Raise a `ClassCastException` if not)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct CheckCast {
    #[pyo3(get)]
    pub reg: u8,
    #[pyo3(get)]
    pub lit: IdType,
}

#[pymethods]
impl CheckCast {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8, lit: IdType) -> Self {
        Self { reg, lit }
    }

    pub fn __str__(&self) -> String {
        format!("check-cast {} {}", self.reg, self.lit.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(CheckCast({}, {}))",
            self.reg,
            self.lit.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.lit.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        let mut types = HashSet::new();
        types.insert(self.lit.clone());
        types
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl CheckCast {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self, type_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x1f,
            va: self.reg,
            b: type_idx as u16,
        }
    }
}

/// Check if an object if an instance of a type.
/// (put 1 in the dest register if yes, else 0)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct InstanceOf {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub lit: IdType,
}

#[pymethods]
impl InstanceOf {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, obj: u8, lit: IdType) -> Result<Self> {
        let ins = Self { dest, obj, lit };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "The registers of InstanceOf are indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "The registers of InstanceOf are indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "instance-of {} {} {}",
            self.dest,
            self.obj,
            self.lit.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(InstanceOf({}, {}, {}))",
            self.dest,
            self.obj,
            self.lit.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.lit.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        let mut types = HashSet::new();
        types.insert(self.lit.clone());
        types
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl InstanceOf {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self, type_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x20,
            va: self.dest,
            vb: self.obj,
            c: type_idx as u16,
        }
    }
}

/// Get the number of item in an array.
/// (put the lenght in the dest register)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ArrayLength {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub arr: u8,
}

#[pymethods]
impl ArrayLength {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, arr: u8) -> Result<Self> {
        let ins = Self { dest, arr };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "The registers of ArrayLength are indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.arr & 0b1111_0000 != 0 {
            Err(anyhow!(
                "The registers of ArrayLength are indexed on 4 bits, found {}",
                self.arr
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("array-length{} {}", self.dest, self.arr,)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(ArrayLength({}, {}))", self.dest, self.arr,)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ArrayLength {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x21,
            va: self.dest,
            vb: self.arr,
        }
    }
}

/// Construct a new instance of the indicated type and store a reference to it.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct NewInstance {
    #[pyo3(get)]
    pub reg: u8,
    #[pyo3(get)]
    pub lit: IdType,
}

#[pymethods]
impl NewInstance {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8, lit: IdType) -> Self {
        Self { reg, lit }
    }

    pub fn __str__(&self) -> String {
        format!("new-instance {} {}", self.reg, self.lit.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(NewInstance({}, {}))",
            self.reg,
            self.lit.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.lit.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        let mut types = HashSet::new();
        types.insert(self.lit.clone());
        types
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl NewInstance {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self, type_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x22,
            va: self.reg,
            b: type_idx as u16,
        }
    }
}

/// Construct a new array of the indicated type and size in size_reg and store a reference to it.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct NewArray {
    #[pyo3(get)]
    pub reg: u8,
    #[pyo3(get)]
    pub size_reg: u8,
    #[pyo3(get)]
    pub lit: IdType,
}

#[pymethods]
impl NewArray {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8, size_reg: u8, lit: IdType) -> Result<Self> {
        let ins = Self { reg, size_reg, lit };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn __str__(&self) -> String {
        format!(
            "new-array {} {} {}",
            self.reg,
            self.size_reg,
            self.lit.__str__()
        )
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.reg & 0b1111_0000 != 0 {
            Err(anyhow!(
                "The registers of NewArray are indexed on 4 bits, found {}",
                self.reg
            ))
        } else if self.size_reg & 0b1111_0000 != 0 {
            Err(anyhow!(
                "The registers of NewArray are indexed on 4 bits, found {}",
                self.size_reg
            ))
        } else {
            Ok(())
        }
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(NewArray({}, {}, {}))",
            self.reg,
            self.size_reg,
            self.lit.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.lit.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        let mut types = HashSet::new();
        types.insert(self.lit.clone());
        types
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl NewArray {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self, type_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x23,
            va: self.reg,
            vb: self.size_reg,
            c: type_idx as u16,
        }
    }
}

/// Construct a new array of the indicated type and size fill it with the values of the
/// given registers.
///
/// The elements of the array must fit in 32bits.
///
/// The registers must either be less to 5 and their number must fit on 4 bits each, or
/// be less than 256, their the number of the smallest must fit on 16 bits and they must
/// be sorted an directly adjascent.
///
/// The newly created array can be retreived with a move-result-object instruction.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct FilledNewArray {
    #[pyo3(get)]
    pub type_: IdType,
    #[pyo3(get)]
    pub reg_values: Vec<u16>,
}

#[pymethods]
impl FilledNewArray {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(type_: IdType, reg_values: Vec<u16>) -> Result<Self> {
        let array = Self { type_, reg_values };
        array.sanity_check()?;
        Ok(array)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut last = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.reg_values.len();
        for r in self.reg_values.iter().cloned() {
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if (four_bites && len <= 5) || (consec && len <= 255) {
            Ok(())
        } else {
            Err(anyhow!(
                "filled-new-array instruction must either use 5 or \
                less register indexed on 4 bites or less than 255 \
                consecutive registers"
            ))
        }
    }

    pub fn __str__(&self) -> String {
        let args = if self.reg_values.len() >= 5 {
            format!(
                "{} .. {}",
                self.reg_values[0],
                self.reg_values[self.reg_values.len() - 1]
            )
        } else {
            self.reg_values
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!("filled-new-array {{{}}} {}", args, self.type_.__str__())
    }

    pub fn __repr__(&self) -> String {
        let args = if self.reg_values.len() >= 5 {
            format!(
                "{}, ..., {}",
                self.reg_values[0],
                self.reg_values[self.reg_values.len() - 1]
            )
        } else {
            self.reg_values
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(", ")
        };
        format!(
            "Instruction(FilledNewArray([{}], {}))",
            args,
            self.type_.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        6
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.type_.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        let mut types = HashSet::new();
        types.insert(self.type_.clone());
        types
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl FilledNewArray {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self, type_idx: usize) -> InsFormat {
        let mut last = None;
        let mut first = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.reg_values.len();
        for r in self.reg_values.iter().cloned() {
            if first.is_none() {
                first = Some(r);
            }
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if four_bites && len <= 5 {
            let mut regs = vec![];
            for reg in self.reg_values.iter().cloned() {
                regs.push(reg);
            }
            while regs.len() != 5 {
                regs.push(0);
            }
            let [vc, vd, ve, vf, vg]: [u8; 5] = regs
                .into_iter()
                .map(|r| r as u8)
                .collect::<Vec<_>>()
                .try_into()
                .ok()
                .unwrap();
            let a = self.reg_values.len() as u8;
            InsFormat::Format35C {
                op: 0x24,
                a,
                vc,
                ve,
                vd,
                vf,
                vg,
                b: type_idx as u16,
            }
        } else if consec && len <= 255 {
            let a = self.reg_values.len() as u8;
            let vc = if let Some(vc) = first { vc } else { 0 };
            InsFormat::Format3RC {
                op: 0x25,
                a,
                vc,
                b: type_idx as u16,
            }
        } else {
            // Not supposed to happend with a sanitized array
            panic!("Invalid NewArrayInstruction {self:?}")
        }
    }
}

#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct FillArrayData {
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub elt_width: u16,
    #[pyo3(get)]
    pub data: Vec<u8>,
}

#[pymethods]
impl FillArrayData {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(arr: u8, elt_width: u16, data: Vec<u8>) -> Self {
        Self {
            arr,
            elt_width,
            data,
        }
    }

    pub fn __str__(&self) -> String {
        let data: String = if self.data.len() / self.elt_width as usize == 0 {
            "".into()
        } else if self.data.len() / self.elt_width as usize <= 2 {
            let mut arr = "".into();
            for (i, v) in self.data.iter().enumerate() {
                if i == 0 {
                    arr += "0x"
                } else if i % self.elt_width as usize == 0 {
                    arr += " 0x"
                }
                arr += format!("{v:02x}").as_str();
            }
            arr
        } else {
            let mut arr = "0x".into();
            for v in &self.data[..self.elt_width as usize] {
                arr += format!("{v:02x}").as_str();
            }
            arr += " ... 0x";
            for v in &self.data[self.data.len() - self.elt_width as usize..] {
                arr += format!("{v:02x}").as_str();
            }

            arr
        };
        format!("fill-array-data {} {}", self.arr, data)
    }

    pub fn __repr__(&self) -> String {
        let data: String = if self.data.len() / self.elt_width as usize == 0 {
            "".into()
        } else if self.data.len() / self.elt_width as usize <= 2 {
            let mut arr = "".into();
            for (i, v) in self.data.iter().enumerate() {
                if i == 0 {
                    arr += "0x"
                } else if i % self.elt_width as usize == 0 {
                    arr += ", 0x"
                }
                arr += format!("{v:02x}").as_str();
            }
            arr
        } else {
            let mut arr = "0x".into();
            for v in &self.data[..self.elt_width as usize] {
                arr += format!("{v:02x}").as_str();
            }
            arr += ", ..., 0x";
            for v in &self.data[self.data.len() - self.elt_width as usize..] {
                arr += format!("{v:02x}").as_str();
            }

            arr
        };
        format!("Instruction(FillArrayData({}, [{}]))", self.arr, data)
    }

    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        6
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl FillArrayData {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `data_offset` is the offset to the address where the data are located.
    pub fn get_raw_ins(&self, data_offset: i32) -> InsFormat {
        InsFormat::Format31T {
            op: 0x26,
            va: self.arr,
            b: data_offset,
        }
    }
}

/// Throws the exception in the register.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct Throw {
    #[pyo3(get)]
    pub reg: u8,
}

#[pymethods]
impl Throw {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8) -> Self {
        Self { reg }
    }

    pub fn __str__(&self) -> String {
        format!("throw {}", self.reg)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(Throw({}))", self.reg)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl Throw {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format11X {
            op: 0x27,
            va: self.reg,
        }
    }
}

/// Jump to the label.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct Goto {
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl Goto {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(label: String) -> Self {
        Self { label }
    }

    pub fn __str__(&self) -> String {
        format!("goto {}", self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(Goto({}))", self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        2
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        6
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl Goto {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// - `branch_offset` is the offset to the address.
    /// - `goto_size` is the estimated size of the goto used to compute the offset.
    ///     It may not be the optimal size, but the instructions of the associtated
    ///     size will be used, and the offset is expected to fit in the instruction.
    ///
    /// Possible goto size and associated offset size:
    /// - `2`: offset must fit in an i8
    /// - `4`: offset must fit in an i16
    /// - `6`: offset must fit in an i32
    pub fn get_raw_ins(&self, data_offset: i32, goto_size: usize) -> InsFormat {
        if goto_size == 2 && (I8_MIN_AS_I32..=I8_MAX_AS_I32).contains(&data_offset) {
            InsFormat::Format10T {
                op: 0x28,
                a: data_offset as i8,
            }
        } else if goto_size == 4 && (I16_MIN_AS_I32..=I16_MAX_AS_I32).contains(&data_offset) {
            InsFormat::Format20T {
                op: 0x29,
                a: data_offset as i16,
            }
        } else if goto_size == 6 {
            InsFormat::Format30T {
                op: 0x2a,
                a: data_offset,
            }
        } else {
            panic!("Invalid goto_size and/or data_offset value")
        }
    }

    /// Return the size of a goto instruction from the goto instruction address and
    /// and interval for the branch address. The size will be selected to be the smallest
    /// possible size that fit the offset for the worst case scenario.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    ///
    /// The address for the intervals are in code address units `u16`.
    pub fn size_from_branch_offset_interval(
        addr_goto: usize,
        min_addr_branch: usize,
        max_addr_branch: usize,
    ) -> Result<usize> {
        let worst_offset = if max_addr_branch as i32 - addr_goto as i32
            > addr_goto as i32 - min_addr_branch as i32
        {
            max_addr_branch as i32 - addr_goto as i32
        } else {
            min_addr_branch as i32 - addr_goto as i32
        };
        match worst_offset {
            I8_MIN_AS_I32..=I8_MAX_AS_I32 => Ok(2),
            I16_MIN_AS_I32..=I16_MAX_AS_I32 => Ok(4),
            _ => Ok(6),
        }
    }
}

/// Jump to a label depending on the value of a register. If the value
/// is not matched, continue the extecution at the next instruction.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct Switch {
    #[pyo3(get)]
    pub reg: u8,
    #[pyo3(get)]
    #[serde(with = "hashmap_vectorize")]
    pub branches: HashMap<i32, String>,
}

#[pymethods]
impl Switch {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(reg: u8, branches: HashMap<i32, String>) -> Self {
        Self { reg, branches }
    }

    pub fn __str__(&self) -> String {
        let mut branches_str: String = "".into();
        let mut branches: Vec<_> = self.branches.iter().collect();
        branches.sort_by_key(|(key, _)| **key);
        for (key, label) in branches {
            branches_str += &format!("\n    {key}: goto {label}");
        }
        format!("switch {} {}", self.reg, branches_str)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(Switch({}, ...))", self.reg)
    }

    /// Test if the swith is packed of pack
    pub fn is_packed(&self) -> bool {
        let mut last = None;
        let mut keys = self.branches.keys().collect::<Vec<_>>();
        keys.sort();
        for key in keys.into_iter().cloned() {
            if let Some(last) = &last {
                if *last != key - 1 {
                    return false;
                }
            }
            last = Some(key);
        }
        true
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        6
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl Switch {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `table_offset` is the offset to the address where the branche table located.
    pub fn get_raw_ins(&self, data_offset: i32) -> InsFormat {
        InsFormat::Format31T {
            op: if self.is_packed() { 0x2b } else { 0x2c },
            va: self.reg,
            b: data_offset,
        }
    }
}
/// Store the result of the comparison between the registers.
///
/// - b < c: a = -1
/// - b == c: a = 0
/// - b > c : a = 1
/// - b == c == Nan: a = -1
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct CmpLFloat {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub b: u8,
    #[pyo3(get)]
    pub c: u8,
}

#[pymethods]
impl CmpLFloat {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("cmpl-float {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(CmpLFloat({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl CmpLFloat {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x2d,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Store the result of the comparison between the registers.
///
/// - b < c: a = -1
/// - b == c: a = 0
/// - b > c : a = 1
/// - b == c == Nan: a = 1
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct CmpGFloat {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub b: u8,
    #[pyo3(get)]
    pub c: u8,
}

#[pymethods]
impl CmpGFloat {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("cmpg-float {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(CmpGFloat({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl CmpGFloat {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x2e,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Store the result of the comparison between the registers.
///
/// - b < c: a = -1
/// - b == c: a = 0
/// - b > c : a = 1
/// - b == c == Nan: a = -1
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct CmpLDouble {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub b: u8,
    #[pyo3(get)]
    pub c: u8,
}

#[pymethods]
impl CmpLDouble {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("cmpl-double {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(CmpLDouble({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl CmpLDouble {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x2f,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Store the result of the comparison between the registers.
///
/// - b < c: a = -1
/// - b == c: a = 0
/// - b > c : a = 1
/// - b == c == Nan: a = 1
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct CmpGDouble {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub b: u8,
    #[pyo3(get)]
    pub c: u8,
}

#[pymethods]
impl CmpGDouble {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("cmpg-double {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(CmpGDouble({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl CmpGDouble {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x30,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Store the result of the comparison between the registers.
///
/// - b < c: a = -1
/// - b == c: a = 0
/// - b > c : a = 1
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct CmpLong {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub b: u8,
    #[pyo3(get)]
    pub c: u8,
}

#[pymethods]
impl CmpLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("cmp-long {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(CmpLong({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl CmpLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x31,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Jump to the label if a == b
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IfEq {
    #[pyo3(get)]
    pub a: u8,
    #[pyo3(get)]
    pub b: u8,
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl IfEq {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(a: u8, b: u8, label: String) -> Result<Self> {
        let ins = Self { a, b, label };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.a & 0b1111_0000 != 0 {
            Err(anyhow!(
                "if-eq uses registers indexed on 4 bits, found {}",
                self.a
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "if-eq uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("if-eq {} {} {}", self.a, self.b, self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IfEq({}, {}, {}))", self.a, self.b, self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}
impl IfEq {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `branch_offset` is the offset to the address to jump to if
    /// the test is true.
    pub fn get_raw_ins(&self, branch_offset: i16) -> InsFormat {
        InsFormat::Format22T {
            op: 0x32,
            va: self.a,
            vb: self.b,
            c: branch_offset,
        }
    }
}

/// Jump to the label if a != b
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IfNe {
    #[pyo3(get)]
    pub a: u8,
    #[pyo3(get)]
    pub b: u8,
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl IfNe {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(a: u8, b: u8, label: String) -> Result<Self> {
        let ins = Self { a, b, label };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.a & 0b1111_0000 != 0 {
            Err(anyhow!(
                "if-neq uses registers indexed on 4 bits, found {}",
                self.a
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "if-neq uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("if-neq {} {} {}", self.a, self.b, self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IfNe({}, {}, {}))", self.a, self.b, self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IfNe {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `branch_offset` is the offset to the address to jump to if
    /// the test is true.
    pub fn get_raw_ins(&self, branch_offset: i16) -> InsFormat {
        InsFormat::Format22T {
            op: 0x33,
            va: self.a,
            vb: self.b,
            c: branch_offset,
        }
    }
}

/// Jump to the label if a < b
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IfLt {
    #[pyo3(get)]
    pub a: u8,
    #[pyo3(get)]
    pub b: u8,
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl IfLt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(a: u8, b: u8, label: String) -> Result<Self> {
        let ins = Self { a, b, label };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.a & 0b1111_0000 != 0 {
            Err(anyhow!(
                "if-lt uses registers indexed on 4 bits, found {}",
                self.a
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "if-lt uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("if-lt {} {} {}", self.a, self.b, self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IfLt({}, {}, {}))", self.a, self.b, self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IfLt {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `branch_offset` is the offset to the address to jump to if
    /// the test is true.
    pub fn get_raw_ins(&self, branch_offset: i16) -> InsFormat {
        InsFormat::Format22T {
            op: 0x34,
            va: self.a,
            vb: self.b,
            c: branch_offset,
        }
    }
}

/// Jump to the label if a >= b
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IfGe {
    #[pyo3(get)]
    pub a: u8,
    #[pyo3(get)]
    pub b: u8,
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl IfGe {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(a: u8, b: u8, label: String) -> Result<Self> {
        let ins = Self { a, b, label };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.a & 0b1111_0000 != 0 {
            Err(anyhow!(
                "if-ge uses registers indexed on 4 bits, found {}",
                self.a
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "if-ge uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("if-ge {} {} {}", self.a, self.b, self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IfGe({}, {}, {}))", self.a, self.b, self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IfGe {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `branch_offset` is the offset to the address to jump to if
    /// the test is true.
    pub fn get_raw_ins(&self, branch_offset: i16) -> InsFormat {
        InsFormat::Format22T {
            op: 0x35,
            va: self.a,
            vb: self.b,
            c: branch_offset,
        }
    }
}

/// Jump to the label if a > b
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IfGt {
    #[pyo3(get)]
    pub a: u8,
    #[pyo3(get)]
    pub b: u8,
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl IfGt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(a: u8, b: u8, label: String) -> Result<Self> {
        let ins = Self { a, b, label };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.a & 0b1111_0000 != 0 {
            Err(anyhow!(
                "if-gt uses registers indexed on 4 bits, found {}",
                self.a
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "if-gt uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("if-gt {} {} {}", self.a, self.b, self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IfGt({}, {}, {}))", self.a, self.b, self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IfGt {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `branch_offset` is the offset to the address to jump to if
    /// the test is true.
    pub fn get_raw_ins(&self, branch_offset: i16) -> InsFormat {
        InsFormat::Format22T {
            op: 0x36,
            va: self.a,
            vb: self.b,
            c: branch_offset,
        }
    }
}

/// Jump to the label if a <= b
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IfLe {
    #[pyo3(get)]
    pub a: u8,
    #[pyo3(get)]
    pub b: u8,
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl IfLe {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(a: u8, b: u8, label: String) -> Result<Self> {
        let ins = Self { a, b, label };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.a & 0b1111_0000 != 0 {
            Err(anyhow!(
                "if-le uses registers indexed on 4 bits, found {}",
                self.a
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "if-le uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("if-le {} {} {}", self.a, self.b, self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IfLe({}, {}, {}))", self.a, self.b, self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IfLe {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `branch_offset` is the offset to the address to jump to if
    /// the test is true.
    pub fn get_raw_ins(&self, branch_offset: i16) -> InsFormat {
        InsFormat::Format22T {
            op: 0x37,
            va: self.a,
            vb: self.b,
            c: branch_offset,
        }
    }
}

/// Jump to the label if a == 0
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IfEqZ {
    #[pyo3(get)]
    pub a: u8,
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl IfEqZ {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(a: u8, label: String) -> Result<Self> {
        let ins = Self { a, label };
        Ok(ins)
    }

    pub fn __str__(&self) -> String {
        format!("if-eqz {} {}", self.a, self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IfEqZ({}, {}))", self.a, self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IfEqZ {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `branch_offset` is the offset to the address to jump to if
    /// the test is true.
    pub fn get_raw_ins(&self, branch_offset: i16) -> InsFormat {
        InsFormat::Format21T {
            op: 0x38,
            va: self.a,
            b: branch_offset,
        }
    }
}

/// Jump to the label if a != 0
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IfNeZ {
    #[pyo3(get)]
    pub a: u8,
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl IfNeZ {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(a: u8, label: String) -> Result<Self> {
        let ins = Self { a, label };
        Ok(ins)
    }

    pub fn __str__(&self) -> String {
        format!("if-neq {} {}", self.a, self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IfNe({}, {}))", self.a, self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IfNeZ {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `branch_offset` is the offset to the address to jump to if
    /// the test is true.
    pub fn get_raw_ins(&self, branch_offset: i16) -> InsFormat {
        InsFormat::Format21T {
            op: 0x39,
            va: self.a,
            b: branch_offset,
        }
    }
}

/// Jump to the label if a < 0
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IfLtZ {
    #[pyo3(get)]
    pub a: u8,
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl IfLtZ {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(a: u8, label: String) -> Result<Self> {
        let ins = Self { a, label };
        Ok(ins)
    }

    pub fn __str__(&self) -> String {
        format!("if-lt {} {}", self.a, self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IfLt({}, {}))", self.a, self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IfLtZ {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `branch_offset` is the offset to the address to jump to if
    /// the test is true.
    pub fn get_raw_ins(&self, branch_offset: i16) -> InsFormat {
        InsFormat::Format21T {
            op: 0x3a,
            va: self.a,
            b: branch_offset,
        }
    }
}

/// Jump to the label if a >= 0
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IfGeZ {
    #[pyo3(get)]
    pub a: u8,
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl IfGeZ {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(a: u8, label: String) -> Result<Self> {
        let ins = Self { a, label };
        Ok(ins)
    }

    pub fn __str__(&self) -> String {
        format!("if-ge {} {}", self.a, self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IfGe({}, {}))", self.a, self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IfGeZ {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `branch_offset` is the offset to the address to jump to if
    /// the test is true.
    pub fn get_raw_ins(&self, branch_offset: i16) -> InsFormat {
        InsFormat::Format21T {
            op: 0x3b,
            va: self.a,
            b: branch_offset,
        }
    }
}

/// Jump to the label if a > 0
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IfGtZ {
    #[pyo3(get)]
    pub a: u8,
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl IfGtZ {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(a: u8, label: String) -> Result<Self> {
        let ins = Self { a, label };
        Ok(ins)
    }

    pub fn __str__(&self) -> String {
        format!("if-gt {} {}", self.a, self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IfGt({}, {}))", self.a, self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IfGtZ {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `branch_offset` is the offset to the address to jump to if
    /// the test is true.
    pub fn get_raw_ins(&self, branch_offset: i16) -> InsFormat {
        InsFormat::Format21T {
            op: 0x3c,
            va: self.a,
            b: branch_offset,
        }
    }
}

/// Jump to the label if a <= 0
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IfLeZ {
    #[pyo3(get)]
    pub a: u8,
    #[pyo3(get)]
    pub label: String,
}

#[pymethods]
impl IfLeZ {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(a: u8, label: String) -> Result<Self> {
        let ins = Self { a, label };
        Ok(ins)
    }

    pub fn __str__(&self) -> String {
        format!("if-le {} {}", self.a, self.label)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IfLe({}, {}))", self.a, self.label)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IfLeZ {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `branch_offset` is the offset to the address to jump to if
    /// the test is true.
    pub fn get_raw_ins(&self, branch_offset: i16) -> InsFormat {
        InsFormat::Format21T {
            op: 0x3d,
            va: self.a,
            b: branch_offset,
        }
    }
}

/// Put the value at `arr[idx]` in register dest (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AGet {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl AGet {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, arr: u8, idx: u8) -> Self {
        Self { dest, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aget {} {} {}", self.dest, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AGet({}, {}, {}))",
            self.dest, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AGet {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x44,
            va: self.dest,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the value at `arr[idx]` in register pair dest (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AGetWide {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl AGetWide {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, arr: u8, idx: u8) -> Self {
        Self { dest, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aget-wide {} {} {}", self.dest, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AGetWide({}, {}, {}))",
            self.dest, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AGetWide {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x45,
            va: self.dest,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the reference at `arr[idx]` in register (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AGetObject {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl AGetObject {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, arr: u8, idx: u8) -> Self {
        Self { dest, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aget-object {} {} {}", self.dest, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AGetObject({}, {}, {}))",
            self.dest, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AGetObject {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x46,
            va: self.dest,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the boolean at `arr[idx]` in register dest (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AGetBoolean {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl AGetBoolean {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, arr: u8, idx: u8) -> Self {
        Self { dest, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aget-boolean {} {} {}", self.dest, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AGetBoolean({}, {}, {}))",
            self.dest, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AGetBoolean {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x47,
            va: self.dest,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the byte at `arr[idx]` in register dest (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AGetByte {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl AGetByte {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, arr: u8, idx: u8) -> Self {
        Self { dest, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aget-byte {} {} {}", self.dest, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AGetByte({}, {}, {}))",
            self.dest, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AGetByte {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x48,
            va: self.dest,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the char at `arr[idx]` in register dest (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AGetChar {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl AGetChar {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, arr: u8, idx: u8) -> Self {
        Self { dest, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aget-char {} {} {}", self.dest, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AGetChar({}, {}, {}))",
            self.dest, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AGetChar {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x49,
            va: self.dest,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the short at `arr[idx]` in register dest (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AGetShort {
    #[pyo3(get)]
    pub dest: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl AGetShort {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, arr: u8, idx: u8) -> Self {
        Self { dest, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aget-short {} {} {}", self.dest, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AGetShort({}, {}, {}))",
            self.dest, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AGetShort {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x4a,
            va: self.dest,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the value of register 'from' in `arr[idx]` (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct APut {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl APut {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, arr: u8, idx: u8) -> Self {
        Self { from, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aput {} {} {}", self.from, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(APut({}, {}, {}))",
            self.from, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl APut {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x4b,
            va: self.from,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the value of the register pair 'from' in `arr[idx]` (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct APutWide {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl APutWide {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, arr: u8, idx: u8) -> Self {
        Self { from, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aput-wide {} {} {}", self.from, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(APutWide({}, {}, {}))",
            self.from, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl APutWide {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x4c,
            va: self.from,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the object reference in 'from' in `arr[idx]` (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct APutObject {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl APutObject {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, arr: u8, idx: u8) -> Self {
        Self { from, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aput-object {} {} {}", self.from, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(APutObject({}, {}, {}))",
            self.from, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl APutObject {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x4d,
            va: self.from,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the boolean in 'from' in `arr[idx]` (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct APutBoolean {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl APutBoolean {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, arr: u8, idx: u8) -> Self {
        Self { from, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aput-boolean {} {} {}", self.from, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(APutBoolean({}, {}, {}))",
            self.from, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl APutBoolean {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x4e,
            va: self.from,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the byte in 'from' in `arr[idx]` (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct APutByte {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl APutByte {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, arr: u8, idx: u8) -> Self {
        Self { from, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aput-byte {} {} {}", self.from, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(APutByte({}, {}, {}))",
            self.from, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl APutByte {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x4f,
            va: self.from,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the char in 'from' in `arr[idx]` (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct APutChar {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl APutChar {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, arr: u8, idx: u8) -> Self {
        Self { from, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aput-char {} {} {}", self.from, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(APutChar({}, {}, {}))",
            self.from, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl APutChar {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x50,
            va: self.from,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the short in 'from' in `arr[idx]` (all values are in registers)
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct APutShort {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub arr: u8,
    #[pyo3(get)]
    pub idx: u8,
}

#[pymethods]
impl APutShort {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, arr: u8, idx: u8) -> Self {
        Self { from, arr, idx }
    }

    pub fn __str__(&self) -> String {
        format!("aput-short {} {} {}", self.from, self.arr, self.idx)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(APutShort({}, {}, {}))",
            self.from, self.arr, self.idx
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl APutShort {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x51,
            va: self.from,
            vb: self.arr,
            vc: self.idx,
        }
    }
}

/// Put the value in 'to' in the instance field 'field' of 'obj' ('to' and 'obj' are register)
///
/// The registers 'to' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IGet {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IGet {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { to, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.to & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget uses registers indexed on 4 bits, found {}",
                self.to
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("iget {} {} {}", self.to, self.obj, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IGet({}, {}, {}))",
            self.to,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IGet {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x52,
            va: self.to,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the value in the register pair 'to' in the instance field 'field' of 'obj' ('to'
/// and 'obj' are register)
///
/// The registers 'to' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IGetWide {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IGetWide {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { to, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.to & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget-wide uses registers indexed on 4 bits, found {}",
                self.to
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget-wide uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "iget-wide {} {} {}",
            self.to,
            self.obj,
            self.field.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IGetWide({}, {}, {}))",
            self.to,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IGetWide {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x53,
            va: self.to,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the object reference 'to' in the instance field 'field' of 'obj' ('to' and 'obj' are register)
///
/// The registers 'to' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IGetObject {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IGetObject {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { to, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.to & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget-object uses registers indexed on 4 bits, found {}",
                self.to
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget-object uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "iget-object {} {} {}",
            self.to,
            self.obj,
            self.field.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IGetObject({}, {}, {}))",
            self.to,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IGetObject {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x54,
            va: self.to,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the boolean in 'to' in the instance field 'field' of 'obj' ('to' and 'obj' are register)
///
/// The registers 'to' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IGetBoolean {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IGetBoolean {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { to, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.to & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget-boolean uses registers indexed on 4 bits, found {}",
                self.to
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget-boolean uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "iget-boolean {} {} {}",
            self.to,
            self.obj,
            self.field.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IGetBoolean({}, {}, {}))",
            self.to,
            self.obj,
            self.field.__repr__()
        )
    }

    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IGetBoolean {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x55,
            va: self.to,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the byte in 'to' in the instance field 'field' of 'obj' ('to' and 'obj' are register)
///
/// The registers 'to' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IGetByte {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IGetByte {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { to, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.to & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget-byte uses registers indexed on 4 bits, found {}",
                self.to
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget-byte uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "iget-byte {} {} {}",
            self.to,
            self.obj,
            self.field.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IGetByte({}, {}, {}))",
            self.to,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IGetByte {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x56,
            va: self.to,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the char in 'to' in the instance field 'field' of 'obj' ('to' and 'obj' are register)
///
/// The registers 'to' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IGetChar {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IGetChar {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { to, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.to & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget-char uses registers indexed on 4 bits, found {}",
                self.to
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget-char uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "iget-char {} {} {}",
            self.to,
            self.obj,
            self.field.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IGetChar({}, {}, {}))",
            self.to,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IGetChar {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x57,
            va: self.to,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the short in 'to' in the instance field 'field' of 'obj' ('to' and 'obj' are register)
///
/// The registers 'to' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IGetShort {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IGetShort {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { to, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.to & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget-short uses registers indexed on 4 bits, found {}",
                self.to
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iget-short uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "iget-short {} {} {}",
            self.to,
            self.obj,
            self.field.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IGetShort({}, {}, {}))",
            self.to,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IGetShort {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x58,
            va: self.to,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the value in the instance field 'field' of 'obj' in 'from' ('from' and 'obj' are register)
///
/// The registers 'from' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IPut {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IPut {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { from, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.from & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput uses registers indexed on 4 bits, found {}",
                self.from
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("iput {} {} {}", self.from, self.obj, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IPut({}, {}, {}))",
            self.from,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IPut {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x59,
            va: self.from,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the value in the instance field 'field' of 'obj' in the register pair 'from'
/// ('from' and 'obj' are register)
///
/// The registers 'from' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IPutWide {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IPutWide {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { from, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.from & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput-wide uses registers indexed on 4 bits, found {}",
                self.from
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput-wide uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "iput-wide {} {} {}",
            self.from,
            self.obj,
            self.field.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IPutWide({}, {}, {}))",
            self.from,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IPutWide {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x5a,
            va: self.from,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the object reference in the instance field 'field' of 'obj' in 'from' ('from' and 'obj' are register)
///
/// The registers 'from' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IPutObject {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IPutObject {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { from, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.from & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput-object uses registers indexed on 4 bits, found {}",
                self.from
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput-object uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "iput-object {} {} {}",
            self.from,
            self.obj,
            self.field.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IPutObject({}, {}, {}))",
            self.from,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IPutObject {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x5b,
            va: self.from,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the boolean in the instance field 'field' of 'obj' in 'from' ('from' and 'obj' are register)
///
/// The registers 'from' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IPutBoolean {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IPutBoolean {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { from, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.from & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput-boolean uses registers indexed on 4 bits, found {}",
                self.from
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput-boolean uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "iput-boolean {} {} {}",
            self.from,
            self.obj,
            self.field.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IPutBoolean({}, {}, {}))",
            self.from,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IPutBoolean {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x5c,
            va: self.from,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the byte in the instance field 'field' of 'obj' in 'from' ('from' and 'obj' are register)
///
/// The registers 'from' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IPutByte {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IPutByte {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { from, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.from & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput-byte uses registers indexed on 4 bits, found {}",
                self.from
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput-byte uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "iput-byte {} {} {}",
            self.from,
            self.obj,
            self.field.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IPutByte({}, {}, {}))",
            self.from,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IPutByte {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x5d,
            va: self.from,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the char in the instance field 'field' of 'obj' in 'from' ('from' and 'obj' are register)
///
/// The registers 'from' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IPutChar {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IPutChar {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { from, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.from & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput-char uses registers indexed on 4 bits, found {}",
                self.from
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput-char uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "iput-char {} {} {}",
            self.from,
            self.obj,
            self.field.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IPutChar({}, {}, {}))",
            self.from,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IPutChar {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x5e,
            va: self.from,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the short in the instance field 'field' of 'obj' in 'from' ('from' and 'obj' are register)
///
/// The registers 'from' and 'obj' are idexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IPutShort {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub obj: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl IPutShort {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, obj: u8, field: IdField) -> Result<Self> {
        let ins = Self { from, obj, field };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.from & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput-short uses registers indexed on 4 bits, found {}",
                self.from
            ))
        } else if self.obj & 0b1111_0000 != 0 {
            Err(anyhow!(
                "iput-short uses registers indexed on 4 bits, found {}",
                self.obj
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!(
            "iput-short {} {} {}",
            self.from,
            self.obj,
            self.field.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(IPutShort({}, {}, {}))",
            self.from,
            self.obj,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IPutShort {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format22C {
            op: 0x5f,
            va: self.from,
            vb: self.obj,
            c: field_idx as u16,
        }
    }
}

/// Put the value in 'to' in the static field 'field' ('to' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SGet {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SGet {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, field: IdField) -> Self {
        Self { to, field }
    }

    pub fn __str__(&self) -> String {
        format!("sget {} {}", self.to, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(SGet({}, {}))", self.to, self.field.__repr__())
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SGet {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x60,
            va: self.to,
            b: field_idx as u16,
        }
    }
}

/// Put the value in the register pair 'to' in the static field 'field' ('to' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SGetWide {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SGetWide {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, field: IdField) -> Self {
        Self { to, field }
    }

    pub fn __str__(&self) -> String {
        format!("sget-wide {} {}", self.to, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SGetWide({}, {}))",
            self.to,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SGetWide {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x61,
            va: self.to,
            b: field_idx as u16,
        }
    }
}

/// Put the object reference 'to' in the static field 'field' ('to' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SGetObject {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SGetObject {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, field: IdField) -> Self {
        Self { to, field }
    }

    pub fn __str__(&self) -> String {
        format!("sget-object {} {}", self.to, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SGetObject({}, {}))",
            self.to,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SGetObject {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x62,
            va: self.to,
            b: field_idx as u16,
        }
    }
}

/// Put the boolean in 'to' in the static field 'field' ('to' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SGetBoolean {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SGetBoolean {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, field: IdField) -> Self {
        Self { to, field }
    }

    pub fn __str__(&self) -> String {
        format!("sget-boolean {} {}", self.to, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SGetBoolean({}, {}))",
            self.to,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SGetBoolean {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x63,
            va: self.to,
            b: field_idx as u16,
        }
    }
}

/// Put the byte in 'to' in the static field 'field' ('to' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SGetByte {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SGetByte {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, field: IdField) -> Self {
        Self { to, field }
    }

    pub fn __str__(&self) -> String {
        format!("sget-byte {} {}", self.to, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SGetByte({}, {}))",
            self.to,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SGetByte {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x64,
            va: self.to,
            b: field_idx as u16,
        }
    }
}

/// Put the char in 'to' in the static field 'field' ('to' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SGetChar {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SGetChar {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, field: IdField) -> Self {
        Self { to, field }
    }

    pub fn __str__(&self) -> String {
        format!("sget-char {} {}", self.to, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SGetChar({}, {}))",
            self.to,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SGetChar {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x65,
            va: self.to,
            b: field_idx as u16,
        }
    }
}

/// Put the short in 'to' in the static field 'field' ('to' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SGetShort {
    #[pyo3(get)]
    pub to: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SGetShort {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, field: IdField) -> Self {
        Self { to, field }
    }

    pub fn __str__(&self) -> String {
        format!("sget-short {} {}", self.to, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SGetShort({}, {}))",
            self.to,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SGetShort {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x66,
            va: self.to,
            b: field_idx as u16,
        }
    }
}

/// Put the value in the static field 'field' in 'from' ('from' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SPut {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SPut {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, field: IdField) -> Self {
        Self { from, field }
    }

    pub fn __str__(&self) -> String {
        format!("sput {} {}", self.from, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SPut({}, {}))",
            self.from,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SPut {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x67,
            va: self.from,
            b: field_idx as u16,
        }
    }
}

/// Put the value in the static field 'field' in the register pair 'from'
/// ('from' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SPutWide {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SPutWide {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, field: IdField) -> Self {
        Self { from, field }
    }

    pub fn __str__(&self) -> String {
        format!("sput-wide {} {}", self.from, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SPutWide({}, {}))",
            self.from,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SPutWide {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x68,
            va: self.from,
            b: field_idx as u16,
        }
    }
}

/// Put the object reference in the static field 'field' in 'from' ('from' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SPutObject {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SPutObject {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, field: IdField) -> Self {
        Self { from, field }
    }

    pub fn __str__(&self) -> String {
        format!("sput-object {} {}", self.from, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SPutObject({}, {}))",
            self.from,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SPutObject {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x69,
            va: self.from,
            b: field_idx as u16,
        }
    }
}

/// Put the boolean in the static field 'field' in 'from' ('from' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SPutBoolean {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SPutBoolean {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, field: IdField) -> Self {
        Self { from, field }
    }

    pub fn __str__(&self) -> String {
        format!("sput-boolean {} {}", self.from, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SPutBoolean({}, {}))",
            self.from,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SPutBoolean {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x6a,
            va: self.from,
            b: field_idx as u16,
        }
    }
}

/// Put the byte in the static field 'field' in 'from' ('from' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SPutByte {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SPutByte {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, field: IdField) -> Self {
        Self { from, field }
    }

    pub fn __str__(&self) -> String {
        format!("sput-byte {} {}", self.from, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SPutByte({}, {}))",
            self.from,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SPutByte {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x6b,
            va: self.from,
            b: field_idx as u16,
        }
    }
}

/// Put the char in the static field 'field' in 'from' ('from' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SPutChar {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SPutChar {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, field: IdField) -> Self {
        Self { from, field }
    }

    pub fn __str__(&self) -> String {
        format!("sput-char {} {}", self.from, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SPutChar({}, {}))",
            self.from,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SPutChar {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x6c,
            va: self.from,
            b: field_idx as u16,
        }
    }
}

/// Put the short in the static field 'field' in 'from' ('from' is a register)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SPutShort {
    #[pyo3(get)]
    pub from: u8,
    #[pyo3(get)]
    pub field: IdField,
}

#[pymethods]
impl SPutShort {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(from: u8, field: IdField) -> Self {
        Self { from, field }
    }

    pub fn __str__(&self) -> String {
        format!("sput-short {} {}", self.from, self.field.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SPutShort({}, {}))",
            self.from,
            self.field.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.field.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.field.get_all_types()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        let mut fields = HashSet::new();
        fields.insert(self.field.clone());
        fields
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SPutShort {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `field_idx` is the index of the field refered to.
    pub fn get_raw_ins(&self, field_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0x6d,
            va: self.from,
            b: field_idx as u16,
        }
    }
}

/// Call a normal virtual method.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct InvokeVirtual {
    #[pyo3(get)]
    pub method: IdMethod,
    #[pyo3(get)]
    pub args: Vec<u16>,
}

#[pymethods]
impl InvokeVirtual {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(method: IdMethod, args: Vec<u16>) -> Result<Self> {
        let invoke = Self { method, args };
        invoke.sanity_check()?;
        Ok(invoke)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut last = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if (four_bites && len <= 5) || (consec && len <= 255) {
            Ok(())
        } else {
            Err(anyhow!(
                "invoke-virtual instruction must either use 5 or \
                less register indexed on 4 bites or less than 255 \
                consecutive registers"
            ))
        }
    }

    pub fn __str__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!("invoke-virtual {{{}}} {}", args, self.method.__str__())
    }

    pub fn __repr__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!(
            "Instruction(InvokeVirtual({}, {}))",
            args,
            self.method.__str__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        6
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.method.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.method.get_all_types()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        self.method.get_all_protos()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        let mut methods = HashSet::new();
        methods.insert(self.method.clone());
        methods
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl InvokeVirtual {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `method_idx` is the index of the refered method.
    pub fn get_raw_ins(&self, meth_idx: u16) -> InsFormat {
        let mut last = None;
        let mut first = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if first.is_none() {
                first = Some(r);
            }
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if four_bites && len <= 5 {
            let mut regs = vec![];
            for reg in self.args.iter().cloned() {
                regs.push(reg);
            }
            while regs.len() != 5 {
                regs.push(0);
            }
            let [vc, vd, ve, vf, vg]: [u8; 5] = regs
                .into_iter()
                .map(|r| r as u8)
                .collect::<Vec<_>>()
                .try_into()
                .ok()
                .unwrap();
            let a = self.args.len() as u8;
            InsFormat::Format35C {
                op: 0x6e,
                a,
                vc,
                ve,
                vd,
                vf,
                vg,
                b: meth_idx as u16,
            }
        } else if consec && len <= 255 {
            let a = self.args.len() as u8;
            let vc = if let Some(vc) = first { vc } else { 0 };
            InsFormat::Format3RC {
                op: 0x74,
                a,
                vc,
                b: meth_idx,
            }
        } else {
            // Not supposed to happend with a sanitized invoke
            panic!("Invalid Invoke instruction {self:?}")
        }
    }
}

/// Call the closest superclass's virtual method of a non interface class.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct InvokeSuper {
    #[pyo3(get)]
    pub method: IdMethod,
    #[pyo3(get)]
    pub args: Vec<u16>,
}

#[pymethods]
impl InvokeSuper {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(method: IdMethod, args: Vec<u16>) -> Result<Self> {
        let invoke = Self { method, args };
        invoke.sanity_check()?;
        Ok(invoke)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut last = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if (four_bites && len <= 5) || (consec && len <= 255) {
            Ok(())
        } else {
            Err(anyhow!(
                "invoke-super instruction must either use 5 or \
                less register indexed on 4 bites or less than 255 \
                consecutive registers"
            ))
        }
    }

    pub fn __str__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!("invoke-super {{{}}} {}", args, self.method.__str__())
    }

    pub fn __repr__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!(
            "Instruction(InvokeSuper({}, {}))",
            args,
            self.method.__str__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        6
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.method.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.method.get_all_types()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        self.method.get_all_protos()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        let mut methods = HashSet::new();
        methods.insert(self.method.clone());
        methods
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl InvokeSuper {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `method_idx` is the index of the refered method.
    pub fn get_raw_ins(&self, meth_idx: u16) -> InsFormat {
        let mut last = None;
        let mut first = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if first.is_none() {
                first = Some(r);
            }
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if four_bites && len <= 5 {
            let mut regs = vec![];
            for reg in self.args.iter().cloned() {
                regs.push(reg);
            }
            while regs.len() != 5 {
                regs.push(0);
            }
            let [vc, vd, ve, vf, vg]: [u8; 5] = regs
                .into_iter()
                .map(|r| r as u8)
                .collect::<Vec<_>>()
                .try_into()
                .ok()
                .unwrap();
            let a = self.args.len() as u8;
            InsFormat::Format35C {
                op: 0x6f,
                a,
                vc,
                ve,
                vd,
                vf,
                vg,
                b: meth_idx,
            }
        } else if consec && len <= 255 {
            let a = self.args.len() as u8;
            let vc = if let Some(vc) = first { vc } else { 0 };
            InsFormat::Format3RC {
                op: 0x75,
                a,
                vc,
                b: meth_idx,
            }
        } else {
            // Not supposed to happend with a sanitized invoke
            panic!("Invalid Invoke instruction {self:?}")
        }
    }
}

/// Call a direct method (non static non overridable, like private).
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct InvokeDirect {
    #[pyo3(get)]
    pub method: IdMethod,
    #[pyo3(get)]
    pub args: Vec<u16>,
}

#[pymethods]
impl InvokeDirect {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(method: IdMethod, args: Vec<u16>) -> Result<Self> {
        let invoke = Self { method, args };
        invoke.sanity_check()?;
        Ok(invoke)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut last = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if (four_bites && len <= 5) || (consec && len <= 255) {
            Ok(())
        } else {
            Err(anyhow!(
                "invoke-direct instruction must either use 5 or \
                less register indexed on 4 bites or less than 255 \
                consecutive registers"
            ))
        }
    }

    pub fn __str__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!("invoke-direct {{{}}} {}", args, self.method.__str__())
    }

    pub fn __repr__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!(
            "Instruction(InvokeDirect({}, {}))",
            args,
            self.method.__str__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        6
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.method.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.method.get_all_types()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        self.method.get_all_protos()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        let mut methods = HashSet::new();
        methods.insert(self.method.clone());
        methods
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl InvokeDirect {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `method_idx` is the index of the refered method.
    pub fn get_raw_ins(&self, meth_idx: u16) -> InsFormat {
        let mut last = None;
        let mut first = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if first.is_none() {
                first = Some(r);
            }
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if four_bites && len <= 5 {
            let mut regs = vec![];
            for reg in self.args.iter().cloned() {
                regs.push(reg);
            }
            while regs.len() != 5 {
                regs.push(0);
            }
            let [vc, vd, ve, vf, vg]: [u8; 5] = regs
                .into_iter()
                .map(|r| r as u8)
                .collect::<Vec<_>>()
                .try_into()
                .ok()
                .unwrap();
            let a = self.args.len() as u8;
            InsFormat::Format35C {
                op: 0x70,
                a,
                vc,
                ve,
                vd,
                vf,
                vg,
                b: meth_idx,
            }
        } else if consec && len <= 255 {
            let a = self.args.len() as u8;
            let vc = if let Some(vc) = first { vc } else { 0 };
            InsFormat::Format3RC {
                op: 0x76,
                a,
                vc,
                b: meth_idx,
            }
        } else {
            // Not supposed to happend with a sanitized invoke
            panic!("Invalid Invoke instruction {self:?}")
        }
    }
}

/// Call a static method.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct InvokeStatic {
    #[pyo3(get)]
    pub method: IdMethod,
    #[pyo3(get)]
    pub args: Vec<u16>,
}

#[pymethods]
impl InvokeStatic {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(method: IdMethod, args: Vec<u16>) -> Result<Self> {
        let invoke = Self { method, args };
        invoke.sanity_check()?;
        Ok(invoke)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut last = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if (four_bites && len <= 5) || (consec && len <= 255) {
            Ok(())
        } else {
            Err(anyhow!(
                "invoke-static instruction must either use 5 or \
                less register indexed on 4 bites or less than 255 \
                consecutive registers"
            ))
        }
    }

    pub fn __str__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!("invoke-static {{{}}} {}", args, self.method.__str__())
    }

    pub fn __repr__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!(
            "Instruction(InvokeStatic({}, {}))",
            args,
            self.method.__str__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        6
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.method.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.method.get_all_types()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        self.method.get_all_protos()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        let mut methods = HashSet::new();
        methods.insert(self.method.clone());
        methods
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl InvokeStatic {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `method_idx` is the index of the refered method.
    pub fn get_raw_ins(&self, meth_idx: u16) -> InsFormat {
        let mut last = None;
        let mut first = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if first.is_none() {
                first = Some(r);
            }
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if four_bites && len <= 5 {
            let mut regs = vec![];
            for reg in self.args.iter().cloned() {
                regs.push(reg);
            }
            while regs.len() != 5 {
                regs.push(0);
            }
            let [vc, vd, ve, vf, vg]: [u8; 5] = regs
                .into_iter()
                .map(|r| r as u8)
                .collect::<Vec<_>>()
                .try_into()
                .ok()
                .unwrap();
            let a = self.args.len() as u8;
            InsFormat::Format35C {
                op: 0x71,
                a,
                vc,
                ve,
                vd,
                vf,
                vg,
                b: meth_idx,
            }
        } else if consec && len <= 255 {
            let a = self.args.len() as u8;
            let vc = if let Some(vc) = first { vc } else { 0 };
            InsFormat::Format3RC {
                op: 0x77,
                a,
                vc,
                b: meth_idx,
            }
        } else {
            // Not supposed to happend with a sanitized invoke
            panic!("Invalid Invoke instruction {self:?}")
        }
    }
}

/// Call a interface method (method from an interface on an object whose class is unknown)
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct InvokeInterface {
    #[pyo3(get)]
    pub method: IdMethod,
    #[pyo3(get)]
    pub args: Vec<u16>,
}

#[pymethods]
impl InvokeInterface {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(method: IdMethod, args: Vec<u16>) -> Result<Self> {
        let invoke = Self { method, args };
        invoke.sanity_check()?;
        Ok(invoke)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut last = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if (four_bites && len <= 5) || (consec && len <= 255) {
            Ok(())
        } else {
            Err(anyhow!(
                "invoke-interface instruction must either use 5 or \
                less register indexed on 4 bites or less than 255 \
                consecutive registers"
            ))
        }
    }

    pub fn __str__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!("invoke-interface {{{}}} {}", args, self.method.__str__())
    }

    pub fn __repr__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!(
            "Instruction(InvokeInterface({}, {}))",
            args,
            self.method.__str__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        6
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.method.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.method.get_all_types()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        self.method.get_all_protos()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        let mut methods = HashSet::new();
        methods.insert(self.method.clone());
        methods
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl InvokeInterface {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// `method_idx` is the index of the refered method.
    pub fn get_raw_ins(&self, meth_idx: u16) -> InsFormat {
        let mut last = None;
        let mut first = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if first.is_none() {
                first = Some(r);
            }
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if four_bites && len <= 5 {
            let mut regs = vec![];
            for reg in self.args.iter().cloned() {
                regs.push(reg);
            }
            while regs.len() != 5 {
                regs.push(0);
            }
            let [vc, vd, ve, vf, vg]: [u8; 5] = regs
                .into_iter()
                .map(|r| r as u8)
                .collect::<Vec<_>>()
                .try_into()
                .ok()
                .unwrap();
            let a = self.args.len() as u8;
            InsFormat::Format35C {
                op: 0x72,
                a,
                vc,
                ve,
                vd,
                vf,
                vg,
                b: meth_idx,
            }
        } else if consec && len <= 255 {
            let a = self.args.len() as u8;
            let vc = if let Some(vc) = first { vc } else { 0 };
            InsFormat::Format3RC {
                op: 0x78,
                a,
                vc,
                b: meth_idx,
            }
        } else {
            // Not supposed to happend with a sanitized invoke
            panic!("Invalid Invoke instruction {self:?}")
        }
    }
}

/// Put -val in dest.
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct NegInt {
    dest: u8,
    val: u8,
}

#[pymethods]
impl NegInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "neg-int uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "neg-int uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("neg-int {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(NegInt({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl NegInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x7b,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Put ~val in dest
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct NotInt {
    dest: u8,
    val: u8,
}

#[pymethods]
impl NotInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "not-int uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "not-int uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("not-int {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(NotInt({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl NotInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x7c,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Put -val in dest. dest and val are pair registers.
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct NegLong {
    dest: u8,
    val: u8,
}

#[pymethods]
impl NegLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "neg-long uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "neg-long uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("neg-long {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(NegLong({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl NegLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x7d,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Put ~val in dest. dest and val are pair registers.
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct NotLong {
    dest: u8,
    val: u8,
}

#[pymethods]
impl NotLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "not-long uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "not-long uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("not-long {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(NotLong({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl NotLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x7e,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Put -val in dest.
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct NegFloat {
    dest: u8,
    val: u8,
}

#[pymethods]
impl NegFloat {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "neg-float uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "neg-float uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("neg-float {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(NegFloat({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl NegFloat {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x7f,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Put -val in dest.
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct NegDouble {
    dest: u8,
    val: u8,
}

#[pymethods]
impl NegDouble {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "neg-double uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "neg-double uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("neg-double {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(NegDouble({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl NegDouble {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x80,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Convert copy val to dest an convert to long.
///
/// TODO: dest is probably a pair register, but is val to?
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct IntToLong {
    dest: u8,
    val: u8,
}

#[pymethods]
impl IntToLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "int-to-long uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "int-to-long uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("int-to-long {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IntToLong({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IntToLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x81,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the integer value to a float.
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct IntToFloat {
    dest: u8,
    val: u8,
}

#[pymethods]
impl IntToFloat {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "int-to-float uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "int-to-float uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("int-to-float {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IntToFloat({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IntToFloat {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x82,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the integer value to a double
///
/// TODO: dest is probably a pair register, but is val to?
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct IntToDouble {
    dest: u8,
    val: u8,
}

#[pymethods]
impl IntToDouble {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "int-to-double uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "int-to-double uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("int-to-double {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IntToDouble({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IntToDouble {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x83,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the long value to an integer.
///
/// TODO: val is probably a pair register, but is dest to?
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct LongToInt {
    dest: u8,
    val: u8,
}

#[pymethods]
impl LongToInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "long-to-int uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "long-to-int uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("long-to-int {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(LongToInt({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl LongToInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x84,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the long value to a float.
///
/// TODO: val is probably a pair register, but is dest to?
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct LongToFloat {
    dest: u8,
    val: u8,
}

#[pymethods]
impl LongToFloat {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "long-to-float uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "long-to-float uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("long-to-float {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(LongToFloat({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl LongToFloat {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x85,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the long value to a double.
///
/// dest and val are pair registers.
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct LongToDouble {
    dest: u8,
    val: u8,
}

#[pymethods]
impl LongToDouble {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "long-to-double uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "long-to-double uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("long-to-double {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(LongToDouble({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl LongToDouble {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x86,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the float value to an integer.
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct FloatToInt {
    dest: u8,
    val: u8,
}

#[pymethods]
impl FloatToInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "float-to-int uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "float-to-int uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("float-to-int {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(FloatToInt({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl FloatToInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x87,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the float value to a long.
///
/// TODO: dest is probably a pair register, but is val too?
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct FloatToLong {
    dest: u8,
    val: u8,
}

#[pymethods]
impl FloatToLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "float-to-long uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "float-to-long uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("float-to-long {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(FloatToLong({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl FloatToLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x88,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the float value to a double.
///
/// TODO: dest is probably a pair register, but is val too?
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct FloatToDouble {
    dest: u8,
    val: u8,
}

#[pymethods]
impl FloatToDouble {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "float-to-double uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "float-to-double uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("float-to-double {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(FloatToDouble({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl FloatToDouble {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x89,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the double value to an integer.
///
/// TODO: val is probably a pair register, but is dest too?
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct DoubleToInt {
    dest: u8,
    val: u8,
}

#[pymethods]
impl DoubleToInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "double-to-int uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "double-to-int uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("double-to-int {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(DoubleToInt({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl DoubleToInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x8a,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the double value to a long.
///
/// val and dest are pair registers.
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct DoubleToLong {
    dest: u8,
    val: u8,
}

#[pymethods]
impl DoubleToLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "double-to-long uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "double-to-long uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("double-to-long {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(DoubleToLong({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl DoubleToLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x8b,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the double value to a float.
///
/// TODO: val is probably a pair register, but is dest too?
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct DoubleToFloat {
    dest: u8,
    val: u8,
}

#[pymethods]
impl DoubleToFloat {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "double-to-float uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "double-to-float uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("double-to-float {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(DoubleToFloat({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl DoubleToFloat {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x8c,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the integer value to a byte.
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct IntToByte {
    dest: u8,
    val: u8,
}

#[pymethods]
impl IntToByte {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "int-to-byte uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "int-to-byte uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("int-to-byte {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IntToByte({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IntToByte {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x8d,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the integer value to a char.
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct IntToChar {
    dest: u8,
    val: u8,
}

#[pymethods]
impl IntToChar {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "int-to-char uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "int-to-char uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("int-to-char {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IntToChar({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IntToChar {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x8e,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Copy val to dest and convert the integer value to a short.
///
/// `dest` and `val` are registered indexed on 4 bits.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct IntToShort {
    dest: u8,
    val: u8,
}

#[pymethods]
impl IntToShort {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, val: u8) -> Result<Self> {
        let ins = Self { dest, val };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "int-to-short uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.val & 0b1111_0000 != 0 {
            Err(anyhow!(
                "int-to-short uses registers indexed on 4 bits, found {}",
                self.val
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("int-to-short {} {}", self.dest, self.val)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(IntToShort({}, {}))", self.dest, self.val)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl IntToShort {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0x8f,
            va: self.dest,
            vb: self.val,
        }
    }
}

/// Put a + b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AddInt {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl AddInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("add-int {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(AddInt({}, {}, {}))", self.dest, self.b, self.c)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AddInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x90,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a - b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct SubInt {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl SubInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("sub-int {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(SubInt({}, {}, {}))", self.dest, self.b, self.c)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SubInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x91,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a * b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MulInt {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl MulInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("mul-int {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MulInt({}, {}, {}))", self.dest, self.b, self.c)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MulInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x92,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a / b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct DivInt {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl DivInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("div-int {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(DivInt({}, {}, {}))", self.dest, self.b, self.c)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl DivInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x93,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a % b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct RemInt {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl RemInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("rem-int {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(RemInt({}, {}, {}))", self.dest, self.b, self.c)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl RemInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x94,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a & b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AndInt {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl AndInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("and-int {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(AndInt({}, {}, {}))", self.dest, self.b, self.c)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AndInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x95,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a | b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct OrInt {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl OrInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("or-int {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(OrInt({}, {}, {}))", self.dest, self.b, self.c)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl OrInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x96,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a ^ b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct XorInt {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl XorInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("xor-int {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(XorInt({}, {}, {}))", self.dest, self.b, self.c)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl XorInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x97,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a << b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ShlInt {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl ShlInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("shl-int {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(ShlInt({}, {}, {}))", self.dest, self.b, self.c)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ShlInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x98,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a >> b (signed) in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ShrInt {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl ShrInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("shr-int {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(ShrInt({}, {}, {}))", self.dest, self.b, self.c)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ShrInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x99,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a >> b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct UshrInt {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl UshrInt {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("ushr-int {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(UshrInt({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl UshrInt {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x9a,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a + b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AddLong {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl AddLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("add-long {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AddLong({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AddLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x9b,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a - b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct SubLong {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl SubLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("sub-long {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SubLong({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SubLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x9c,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a * b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MulLong {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl MulLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("mul-long {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(MulLong({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MulLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x9d,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a / b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct DivLong {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl DivLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("div-long {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(DivLong({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl DivLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x9e,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a % b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct RemLong {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl RemLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("rem-long {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(RemLong({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl RemLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0x9f,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a & b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AndLong {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl AndLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("and-long {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AndLong({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AndLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xa0,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a | b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct OrLong {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl OrLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("or-long {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(OrLong({}, {}, {}))", self.dest, self.b, self.c)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl OrLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xa1,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a ^ b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct XorLong {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl XorLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("xor-long {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(XorLong({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl XorLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xa2,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a << b in dest.
///
/// dest, and b are register pairs, and c is a single register
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ShlLong {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl ShlLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("shl-long {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(ShlLong({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ShlLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xa3,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a >> b (signed) in dest.
///
/// dest, and b are register pairs, and c is a single register
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ShrLong {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl ShrLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("shr-long {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(ShrLong({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ShrLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xa4,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a >> b in dest.
///
/// dest, and b are register pairs, and c is a single register
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct UshrLong {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl UshrLong {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("ushr-long {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(UshrLong({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl UshrLong {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xa5,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a + b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AddFloat {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl AddFloat {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("add-float {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AddFloat({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AddFloat {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xa6,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a - b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct SubFloat {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl SubFloat {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("sub-float {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SubFloat({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SubFloat {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xa7,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a * b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MulFloat {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl MulFloat {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("mul-float {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(MulFloat({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MulFloat {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xa8,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a / b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct DivFloat {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl DivFloat {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("div-float {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(DivFloat({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl DivFloat {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xa9,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a % b in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct RemFloat {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl RemFloat {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("rem-float {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(RemFloat({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl RemFloat {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xaa,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a + b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AddDouble {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl AddDouble {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("add-double {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AddDouble({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AddDouble {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xab,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a - b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct SubDouble {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl SubDouble {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("sub-double {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(SubDouble({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SubDouble {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xac,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a * b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MulDouble {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl MulDouble {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("mul-double {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(MulDouble({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MulDouble {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xad,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a / b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct DivDouble {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl DivDouble {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("div-double {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(DivDouble({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl DivDouble {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xae,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put a % b in dest.
///
/// dest, b and c are register pairs
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct RemDouble {
    dest: u8,
    b: u8,
    c: u8,
}

#[pymethods]
impl RemDouble {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, c: u8) -> Self {
        Self { dest, b, c }
    }

    pub fn __str__(&self) -> String {
        format!("rem-double {} {} {}", self.dest, self.b, self.c)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(RemDouble({}, {}, {}))",
            self.dest, self.b, self.c
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl RemDouble {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format23X {
            op: 0xaf,
            va: self.dest,
            vb: self.b,
            vc: self.c,
        }
    }
}

/// Put dest + b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AddInt2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl AddInt2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "add-int/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "add-int/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("add-int/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(AddInt2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AddInt2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xb0,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest - b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct SubInt2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl SubInt2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "sub-int/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "sub-int/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("sub-int/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(SubInt2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SubInt2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xb1,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest * b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MulInt2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl MulInt2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "mul-int/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "mul-int/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("mul-int/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MulInt2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MulInt2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xb2,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest / b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct DivInt2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl DivInt2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "div-int/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "div-int/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("div-int/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(DivInt2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl DivInt2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xb3,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest % b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct RemInt2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl RemInt2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "rem-int/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "rem-int/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("rem-int/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(RemInt2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl RemInt2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xb4,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest & b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AndInt2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl AndInt2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "and-int/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "and-int/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("and-int/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(AndInt2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AndInt2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xb5,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest | b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct OrInt2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl OrInt2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "or-int/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "or-int/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("or-int/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(OrInt2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl OrInt2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xb6,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest ^ b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct XorInt2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl XorInt2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "xor-int/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "xor-int/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("xor-int/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(XorInt2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl XorInt2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xb7,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest << b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ShlInt2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl ShlInt2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "shl-int/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "shl-int/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("shl-int/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(ShlInt2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ShlInt2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xb8,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest >> b (signed) in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ShrInt2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl ShrInt2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "shr-int/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "shr-int/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("shr-int/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(ShrInt2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ShrInt2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xb9,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest >> b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct UshrInt2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl UshrInt2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "ushr-int/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "ushr-int/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("ushr-int/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(UshrInt2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl UshrInt2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xba,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest + b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AddLong2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl AddLong2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "add-long/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "add-long/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("add-long/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(AddLong2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AddLong2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xbb,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest - b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct SubLong2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl SubLong2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "sub-long/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "sub-long/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("sub-long/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(SubLong2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SubLong2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xbc,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest * b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MulLong2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl MulLong2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "mul-long/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "mul-long/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("mul-long/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MulLong2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MulLong2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xbd,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest / b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct DivLong2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl DivLong2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "div-long/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "div-long/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("div-long/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(DivLong2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl DivLong2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xbe,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest % b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct RemLong2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl RemLong2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "rem-long/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "rem-long/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("rem-long/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(RemLong2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl RemLong2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xbf,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest & b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AndLong2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl AndLong2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "and-long/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "and-long/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("and-long/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(AndLong2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AndLong2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xc0,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest | b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct OrLong2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl OrLong2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "or-long/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "or-long/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("or-long/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(OrLong2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl OrLong2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xc1,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest ^ b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct XorLong2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl XorLong2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "xor-long/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "xor-long/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("xor-long/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(XorLong2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl XorLong2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xc2,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest << b in dest.
///
/// dest is a register pair, and b is a single register
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ShlLong2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl ShlLong2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "shl-long/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "shl-long/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("shl-long/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(ShlLong2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ShlLong2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xc3,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest >> b (signed) in dest.
///
/// dest is a register pair, and b is a single register
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ShrLong2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl ShrLong2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "shr-long/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "shr-long/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("shr-long/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(ShrLong2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ShrLong2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xc4,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest >> b in dest.
///
/// dest is a register pair, and b is a single register
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct UshrLong2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl UshrLong2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "ushr-long/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "ushr-long/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("ushr-long/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(UshrLong2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl UshrLong2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xc5,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest + b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AddFloat2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl AddFloat2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "add-float/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "add-float/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("add-float/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(AddFloat2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AddFloat2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xc6,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest - b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct SubFloat2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl SubFloat2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "sub-float/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "sub-float/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("sub-float/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(SubFloat2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SubFloat2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xc7,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest * b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MulFloat2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl MulFloat2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "mul-float/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "mul-float/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("mul-float/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MulFloat2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MulFloat2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xc8,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest / b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct DivFloat2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl DivFloat2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "div-float/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "div-float/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("div-float/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(DivFloat2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl DivFloat2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xc9,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest % b in dest.
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct RemFloat2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl RemFloat2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "rem-float/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "rem-float/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("rem-float/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(RemFloat2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl RemFloat2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xca,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest + b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AddDouble2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl AddDouble2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "add-double/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "add-double/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("add-double/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(AddDouble2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AddDouble2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xcb,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest - b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct SubDouble2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl SubDouble2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "sub-double/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "sub-double/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("sub-double/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(SubDouble2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl SubDouble2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xcc,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest * b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MulDouble2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl MulDouble2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "mul-double/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "mul-double/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("mul-double/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(MulDouble2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MulDouble2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xcd,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest / b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct DivDouble2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl DivDouble2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "div-double/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "div-double/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("div-double/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(DivDouble2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl DivDouble2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xce,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put dest % b in dest.
///
/// dest and b are register pairs
///
/// `dest` and `b` are registers indexed on 4 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct RemDouble2Addr {
    dest: u8,
    b: u8,
}

#[pymethods]
impl RemDouble2Addr {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8) -> Result<Self> {
        let ins = Self { dest, b };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        if self.dest & 0b1111_0000 != 0 {
            Err(anyhow!(
                "rem-double/2addr uses registers indexed on 4 bits, found {}",
                self.dest
            ))
        } else if self.b & 0b1111_0000 != 0 {
            Err(anyhow!(
                "rem-double/2addr uses registers indexed on 4 bits, found {}",
                self.b
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("rem-double/2addr {} {}", self.dest, self.b)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(RemDouble2Addr({}, {}))", self.dest, self.b)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl RemDouble2Addr {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format12X {
            op: 0xcf,
            va: self.dest,
            vb: self.b,
        }
    }
}

/// Put b + lit in dest.
///
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AddIntLit {
    dest: u8,
    b: u8,
    lit: i16,
}

#[pymethods]
impl AddIntLit {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, lit: i16) -> Result<Self> {
        let ins = Self { dest, b, lit };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            Err(anyhow!(
                "add-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (add-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (add-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest,
                self.b,
                self.lit
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("add-int/lit {} {} {}", self.dest, self.b, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AddIntLit({}, {}, {}))",
            self.dest, self.b, self.lit
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AddIntLit {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            // Should not happen on a sanitized op
            panic!(
                "add-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (add-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (add-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest, self.b, self.lit
            )
        } else if lit_on_8_bits {
            InsFormat::Format22B {
                op: 0xd8,
                va: self.dest,
                vb: self.b,
                c: self.lit as i8,
            }
        } else {
            InsFormat::Format22S {
                op: 0xd0,
                va: self.dest,
                vb: self.b,
                c: self.lit,
            }
        }
    }
}

/// Put lit - b in dest.
///
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct RsubIntLit {
    dest: u8,
    b: u8,
    lit: i16,
}

#[pymethods]
impl RsubIntLit {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, lit: i16) -> Result<Self> {
        let ins = Self { dest, b, lit };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            Err(anyhow!(
                "rsub-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (rsub-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (rsub-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest,
                self.b,
                self.lit
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("rsub-int/lit {} {} {}", self.dest, self.b, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(RsubIntLit({}, {}, {}))",
            self.dest, self.b, self.lit
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl RsubIntLit {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            // Should not happen on a sanitized op
            panic!(
                "sub-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (sub-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (sub-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest, self.b, self.lit
            )
        } else if lit_on_8_bits {
            InsFormat::Format22B {
                op: 0xd9,
                va: self.dest,
                vb: self.b,
                c: self.lit as i8,
            }
        } else {
            InsFormat::Format22S {
                op: 0xd1,
                va: self.dest,
                vb: self.b,
                c: self.lit,
            }
        }
    }
}

/// Put b * lit in dest.
///
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct MulIntLit {
    dest: u8,
    b: u8,
    lit: i16,
}

#[pymethods]
impl MulIntLit {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, lit: i16) -> Result<Self> {
        let ins = Self { dest, b, lit };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            Err(anyhow!(
                "mul-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (mul-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (mul-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest,
                self.b,
                self.lit
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("mul-int/lit {} {} {}", self.dest, self.b, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(MulIntLit({}, {}, {}))",
            self.dest, self.b, self.lit
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl MulIntLit {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            // Should not happen on a sanitized op
            panic!(
                "mul-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (mul-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (mul-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest, self.b, self.lit
            )
        } else if lit_on_8_bits {
            InsFormat::Format22B {
                op: 0xda,
                va: self.dest,
                vb: self.b,
                c: self.lit as i8,
            }
        } else {
            InsFormat::Format22S {
                op: 0xd2,
                va: self.dest,
                vb: self.b,
                c: self.lit,
            }
        }
    }
}

/// Put b / lit in dest.
///
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct DivIntLit {
    dest: u8,
    b: u8,
    lit: i16,
}

#[pymethods]
impl DivIntLit {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, lit: i16) -> Result<Self> {
        let ins = Self { dest, b, lit };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            Err(anyhow!(
                "div-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (div-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (div-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest,
                self.b,
                self.lit
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("div-int/lit {} {} {}", self.dest, self.b, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(DivIntLit({}, {}, {}))",
            self.dest, self.b, self.lit
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl DivIntLit {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            // Should not happen on a sanitized op
            panic!(
                "div-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (div-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (div-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest, self.b, self.lit
            )
        } else if lit_on_8_bits {
            InsFormat::Format22B {
                op: 0xdb,
                va: self.dest,
                vb: self.b,
                c: self.lit as i8,
            }
        } else {
            InsFormat::Format22S {
                op: 0xd3,
                va: self.dest,
                vb: self.b,
                c: self.lit,
            }
        }
    }
}

/// Put b % lit in dest.
///
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct RemIntLit {
    dest: u8,
    b: u8,
    lit: i16,
}

#[pymethods]
impl RemIntLit {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, lit: i16) -> Result<Self> {
        let ins = Self { dest, b, lit };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            Err(anyhow!(
                "rem-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (rem-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (rem-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest,
                self.b,
                self.lit
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("rem-int/lit {} {} {}", self.dest, self.b, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(RemIntLit({}, {}, {}))",
            self.dest, self.b, self.lit
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl RemIntLit {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            // Should not happen on a sanitized op
            panic!(
                "rem-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (rem-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (rem-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest, self.b, self.lit
            )
        } else if lit_on_8_bits {
            InsFormat::Format22B {
                op: 0xdc,
                va: self.dest,
                vb: self.b,
                c: self.lit as i8,
            }
        } else {
            InsFormat::Format22S {
                op: 0xd4,
                va: self.dest,
                vb: self.b,
                c: self.lit,
            }
        }
    }
}

/// Put b & lit in dest.
///
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct AndIntLit {
    dest: u8,
    b: u8,
    lit: i16,
}

#[pymethods]
impl AndIntLit {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, lit: i16) -> Result<Self> {
        let ins = Self { dest, b, lit };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            Err(anyhow!(
                "and-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (and-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (and-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest,
                self.b,
                self.lit
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("and-int/lit {} {} {}", self.dest, self.b, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(AndIntLit({}, {}, {}))",
            self.dest, self.b, self.lit
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl AndIntLit {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            // Should not happen on a sanitized op
            panic!(
                "and-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (and-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (and-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest, self.b, self.lit
            )
        } else if lit_on_8_bits {
            InsFormat::Format22B {
                op: 0xdd,
                va: self.dest,
                vb: self.b,
                c: self.lit as i8,
            }
        } else {
            InsFormat::Format22S {
                op: 0xd5,
                va: self.dest,
                vb: self.b,
                c: self.lit,
            }
        }
    }
}

/// Put b | lit in dest.
///
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct OrIntLit {
    dest: u8,
    b: u8,
    lit: i16,
}

#[pymethods]
impl OrIntLit {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, lit: i16) -> Result<Self> {
        let ins = Self { dest, b, lit };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            Err(anyhow!(
                "or-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (or-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (or-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest,
                self.b,
                self.lit
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("or-int/lit {} {} {}", self.dest, self.b, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(OrIntLit({}, {}, {}))",
            self.dest, self.b, self.lit
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl OrIntLit {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            // Should not happen on a sanitized op
            panic!(
                "or-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (or-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (or-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest, self.b, self.lit
            )
        } else if lit_on_8_bits {
            InsFormat::Format22B {
                op: 0xde,
                va: self.dest,
                vb: self.b,
                c: self.lit as i8,
            }
        } else {
            InsFormat::Format22S {
                op: 0xd6,
                va: self.dest,
                vb: self.b,
                c: self.lit,
            }
        }
    }
}

/// Put b ^ lit in dest.
///
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct XorIntLit {
    dest: u8,
    b: u8,
    lit: i16,
}

#[pymethods]
impl XorIntLit {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, lit: i16) -> Result<Self> {
        let ins = Self { dest, b, lit };
        ins.sanity_check()?;
        Ok(ins)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            Err(anyhow!(
                "xor-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (xor-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (xor-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest,
                self.b,
                self.lit
            ))
        } else {
            Ok(())
        }
    }

    pub fn __str__(&self) -> String {
        format!("xor-int/lit {} {} {}", self.dest, self.b, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(XorIntLit({}, {}, {}))",
            self.dest, self.b, self.lit
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl XorIntLit {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        let mut reg_on_4_bit = true;
        let mut lit_on_8_bits = true;
        if self.dest & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.b & 0b1111_0000 != 0 {
            reg_on_4_bit = false;
        }
        if self.lit < I8_MIN_AS_I16 || self.lit > I8_MAX_AS_I16 {
            lit_on_8_bits = false;
        }
        if !reg_on_4_bit && !lit_on_8_bits {
            // Should not happen on a sanitized op
            panic!(
                "xor-int/lit uses either registers indexed on 4 bits, and a literal \
                encoded on 16 bits (xor-int/lit16), or registers indexed on 8 bits and \
                a literal encoded on 8 bits (xor-int/lit8). Found reg {} and {}, and lit \
                {}",
                self.dest, self.b, self.lit
            )
        } else if lit_on_8_bits {
            InsFormat::Format22B {
                op: 0xdf,
                va: self.dest,
                vb: self.b,
                c: self.lit as i8,
            }
        } else {
            InsFormat::Format22S {
                op: 0xd7,
                va: self.dest,
                vb: self.b,
                c: self.lit,
            }
        }
    }
}

/// Put b << lit in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ShlIntLit {
    dest: u8,
    b: u8,
    lit: i8,
}

#[pymethods]
impl ShlIntLit {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, lit: i8) -> Self {
        Self { dest, b, lit }
    }

    pub fn __str__(&self) -> String {
        format!("shl-int/lit {} {} {}", self.dest, self.b, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(ShlIntLit({}, {}, {}))",
            self.dest, self.b, self.lit
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ShlIntLit {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format22B {
            op: 0xe0,
            va: self.dest,
            vb: self.b,
            c: self.lit,
        }
    }
}

/// Put b >> lit (signed) in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct ShrIntLit {
    dest: u8,
    b: u8,
    lit: i8,
}

#[pymethods]
impl ShrIntLit {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, lit: i8) -> Self {
        Self { dest, b, lit }
    }

    pub fn __str__(&self) -> String {
        format!("shr-int/lit {} {} {}", self.dest, self.b, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(ShrIntLit({}, {}, {}))",
            self.dest, self.b, self.lit
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ShrIntLit {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format22B {
            op: 0xe1,
            va: self.dest,
            vb: self.b,
            c: self.lit,
        }
    }
}

/// Put b >> lit in dest.
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub struct UshrIntLit {
    dest: u8,
    b: u8,
    lit: i8,
}

#[pymethods]
impl UshrIntLit {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(dest: u8, b: u8, lit: i8) -> Self {
        Self { dest, b, lit }
    }

    pub fn __str__(&self) -> String {
        format!("ushr-int/lit {} {} {}", self.dest, self.b, self.lit)
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(UshrIntLit({}, {}, {}))",
            self.dest, self.b, self.lit
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        self.get_raw_ins().size()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl UshrIntLit {
    /// Return the raw instruction ([`InsFormat`]).
    pub fn get_raw_ins(&self) -> InsFormat {
        InsFormat::Format22B {
            op: 0xe2,
            va: self.dest,
            vb: self.b,
            c: self.lit,
        }
    }
}

/// Call a polymorphic method.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct InvokePolymorphic {
    #[pyo3(get)]
    pub method: IdMethod,
    #[pyo3(get)]
    pub proto: IdMethodType,
    #[pyo3(get)]
    pub args: Vec<u16>,
}

#[pymethods]
impl InvokePolymorphic {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(method: IdMethod, proto: IdMethodType, args: Vec<u16>) -> Result<Self> {
        let invoke = Self {
            method,
            proto,
            args,
        };
        invoke.sanity_check()?;
        Ok(invoke)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut last = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if (four_bites && len <= 5) || (consec && len <= 255) {
            Ok(())
        } else {
            Err(anyhow!(
                "invoke-polymorphic instruction must either use 5 or \
                less register indexed on 4 bites or less than 255 \
                consecutive registers"
            ))
        }
    }

    pub fn __str__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!(
            "invoke-polymorphic {{{}}} {} {}",
            args,
            self.method.__str__(),
            self.proto.__str__()
        )
    }

    pub fn __repr__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!(
            "Instruction(InvokePolymorphic({}, {}, {}))",
            args,
            self.method.__str__(),
            self.proto.__str__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        8
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        let mut strings = self.method.get_all_strings();
        strings.extend(self.proto.get_all_strings());
        strings
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        let mut types = self.method.get_all_types();
        types.extend(self.proto.get_all_types());
        types
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        let mut protos = self.method.get_all_protos();
        protos.insert(self.proto.clone());
        protos
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        let mut methods = HashSet::new();
        methods.insert(self.method.clone());
        methods
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl InvokePolymorphic {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// - `method_idx` is the index of the refered method.
    /// - `proto_idx` is the index of the protoype used.
    pub fn get_raw_ins(&self, meth_idx: u16, proto_idx: u16) -> InsFormat {
        let mut last = None;
        let mut first = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if first.is_none() {
                first = Some(r);
            }
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if four_bites && len <= 5 {
            let mut regs = vec![];
            for reg in self.args.iter().cloned() {
                regs.push(reg);
            }
            while regs.len() != 5 {
                regs.push(0);
            }
            let [vc, vd, ve, vf, vg]: [u8; 5] = regs
                .into_iter()
                .map(|r| r as u8)
                .collect::<Vec<_>>()
                .try_into()
                .ok()
                .unwrap();
            let a = self.args.len() as u8;
            InsFormat::Format45CC {
                op: 0xfa,
                a,
                vc,
                ve,
                vd,
                vf,
                vg,
                b: meth_idx,
                h: proto_idx,
            }
        } else if consec && len <= 255 {
            let a = self.args.len() as u8;
            let vc = if let Some(vc) = first { vc } else { 0 };
            InsFormat::Format4RCC {
                op: 0xfb,
                a,
                vc,
                b: meth_idx,
                h: proto_idx,
            }
        } else {
            // Not supposed to happend with a sanitized invoke
            panic!("Invalid Invoke instruction {self:?}")
        }
    }
}

/// Invoke a method from a call site.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct InvokeCustom {
    #[pyo3(get)]
    pub call_site: CallSite,
    #[pyo3(get)]
    pub args: Vec<u16>,
}

#[pymethods]
impl InvokeCustom {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(call_site: CallSite, args: Vec<u16>) -> Result<Self> {
        let invoke = Self { call_site, args };
        invoke.sanity_check()?;
        Ok(invoke)
    }

    pub fn sanity_check(&self) -> Result<()> {
        let mut last = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if (four_bites && len <= 5) || (consec && len <= 255) {
            Ok(())
        } else {
            Err(anyhow!(
                "invoke-custom instruction must either use 5 or \
                less register indexed on 4 bites or less than 255 \
                consecutive registers"
            ))
        }
    }

    pub fn __str__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!("invoke-custom {{{}}} {}", args, self.call_site.__str__(),)
    }

    pub fn __repr__(&self) -> String {
        let args = if self.args.len() >= 5 {
            format!("{} .. {}", self.args[0], self.args[self.args.len() - 1])
        } else {
            self.args
                .iter()
                .map(|v| format!("{v}"))
                .collect::<Vec<_>>()
                .join(" ")
        };
        format!(
            "Instruction(InvokeCustom({}, {}))",
            args,
            self.call_site.__str__(),
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        6
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.call_site.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.call_site.get_all_types()
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        self.call_site.get_all_protos()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        self.call_site.get_all_field_ids()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        self.call_site.get_all_method_ids()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        self.call_site.get_all_method_handles()
    }
}

impl InvokeCustom {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// - `call_site_idx` is the index of the call site.
    pub fn get_raw_ins(&self, call_site_idx: usize) -> InsFormat {
        let mut last = None;
        let mut first = None;
        let mut consec = true;
        let mut four_bites = true;
        let len = self.args.len();
        for r in self.args.iter().cloned() {
            if first.is_none() {
                first = Some(r);
            }
            if let Some(last) = last {
                if r != last + 1 {
                    consec = false;
                }
            }
            if r & 0b1111_0000 != 0 {
                four_bites = false;
            }
            last = Some(r);
        }
        if four_bites && len <= 5 {
            let mut regs = vec![];
            for reg in self.args.iter().cloned() {
                regs.push(reg);
            }
            while regs.len() != 5 {
                regs.push(0);
            }
            let [vc, vd, ve, vf, vg]: [u8; 5] = regs
                .into_iter()
                .map(|r| r as u8)
                .collect::<Vec<_>>()
                .try_into()
                .ok()
                .unwrap();
            let a = self.args.len() as u8;
            InsFormat::Format35C {
                op: 0xfc,
                a,
                vc,
                ve,
                vd,
                vf,
                vg,
                b: call_site_idx as u16,
            }
        } else if consec && len <= 255 {
            let a = self.args.len() as u8;
            let vc = if let Some(vc) = first { vc } else { 0 };
            InsFormat::Format3RC {
                op: 0xfd,
                a,
                vc,
                b: call_site_idx as u16,
            }
        } else {
            // Not supposed to happend with a sanitized invoke
            panic!("Invalid Invoke instruction {self:?}")
        }
    }
}

/// Put contents reference to the method handle in the register.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct ConstMethodHandle {
    #[pyo3(get)]
    pub handle: MethodHandle,
    #[pyo3(get)]
    pub to: u8,
}

#[pymethods]
impl ConstMethodHandle {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, handle: MethodHandle) -> Self {
        Self { to, handle }
    }

    pub fn __str__(&self) -> String {
        format!("const-method-handle {} {}", self.to, self.handle.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(ConstMethodHandle({}, {}))",
            self.to,
            self.handle.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.handle.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.handle.get_all_types()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        self.handle.get_all_protos()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        self.handle.get_all_field_ids()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        self.handle.get_all_method_ids()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        self.handle.get_all_method_handles()
    }
}

impl ConstMethodHandle {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// - `method_handle_idx` is the index of the method handle.
    pub fn get_raw_ins(&self, method_handle_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0xfe,
            va: self.to,
            b: method_handle_idx as u16,
        }
    }
}

/// Put contents reference to the prototype in the register.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct ConstMethodType {
    #[pyo3(get)]
    pub proto: IdMethodType,
    #[pyo3(get)]
    pub to: u8,
}

#[pymethods]
impl ConstMethodType {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(to: u8, proto: IdMethodType) -> Self {
        Self { to, proto }
    }

    pub fn __str__(&self) -> String {
        format!("const-method-type {} {}", self.to, self.proto.__str__())
    }

    pub fn __repr__(&self) -> String {
        format!(
            "Instruction(ConstMethodType({}, {}))",
            self.to,
            self.proto.__repr__()
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        4
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        self.proto.get_all_strings()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        self.proto.get_all_types()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        let mut protos = HashSet::new();
        protos.insert(self.proto.clone());
        protos
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

impl ConstMethodType {
    /// Return the raw instruction ([`InsFormat`]).
    ///
    /// - `proto_idx` is the index of the method protoype.
    pub fn get_raw_ins(&self, proto_idx: usize) -> InsFormat {
        InsFormat::Format21C {
            op: 0xff,
            va: self.to,
            b: proto_idx as u16,
        }
    }
}

/// Try block. It does not match an dalvik instruction but is derived from the code item struct.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct Try {
    #[pyo3(get)]
    pub end_label: String,
    /// The  list of exceptions and their associated handler label.
    ///
    /// # Warning
    ///
    /// The handler are sorted: if severat Exception Type match an exceptions, the
    /// the handler used is the first in the list.
    #[pyo3(get)]
    pub handlers: Vec<(IdType, String)>,
    #[pyo3(get)]
    pub default_handler: Option<String>,
}

#[pymethods]
impl Try {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(
        end_label: String,
        handlers: Vec<(IdType, String)>,
        default_handler: Option<String>,
    ) -> Self {
        Self {
            end_label,
            default_handler,
            handlers,
        }
    }

    pub fn __str__(&self) -> String {
        let handlers = self
            .handlers
            .iter()
            .map(|(ty, label)| format!("    {}: {label}", ty.__str__()))
            .collect::<Vec<_>>()
            .join("\n    ");
        let default_handler = if let Some(label) = &self.default_handler {
            format!("    default: {label}")
        } else {
            "".into()
        };

        format!(
            "try until {}{}{}",
            self.end_label, handlers, default_handler
        )
    }

    pub fn __repr__(&self) -> String {
        let handlers = self
            .handlers
            .iter()
            .map(|(ty, label)| format!("{}, {label}", ty.__repr__()))
            .collect::<Vec<_>>()
            .join(", ");
        format!(
            "Instruction(Try({}, [{}], {:?}))",
            self.end_label, handlers, self.default_handler,
        )
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        0
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        let mut strings = HashSet::new();
        for (ty, _) in &self.handlers {
            strings.extend(ty.get_all_strings());
        }
        strings
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        let mut types = HashSet::new();
        for (ty, _) in &self.handlers {
            types.insert(ty.clone());
        }
        types
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}

/// Label marker. It does not match an dalvik instruction, but it's use as a marker for a
/// jump destination.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct Label {
    #[pyo3(get)]
    pub name: String,
}

#[pymethods]
impl Label {
    pub fn __eq__(&self, other: &Self) -> bool {
        self == other
    }

    pub fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self)?)
    }

    #[staticmethod]
    pub fn from_json(json: &str) -> Result<Self> {
        Ok(serde_json::from_str(json)?)
    }

    #[new]
    pub fn new(name: String) -> Self {
        Self { name }
    }

    pub fn __str__(&self) -> String {
        format!("{}:", self.name)
    }

    pub fn __repr__(&self) -> String {
        format!("Instruction(Label({}))", self.name)
    }
    /// Return the smallest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn min_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the bigest size possible for the associated instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn max_ins_size(&self) -> usize {
        self.ins_size()
    }

    /// Return the actual size of the instruction.
    ///
    /// # Warning
    ///
    /// The returned size is the size in bytes (`u8`) (the same as the value return
    /// by the `Serializable::size()` method), but instructions in the bytecode
    /// count addresses by unit of `u16`.
    pub fn ins_size(&self) -> usize {
        0
    }

    /// Return all strings referenced in the instruction.
    pub fn get_all_strings(&self) -> HashSet<DexString> {
        HashSet::new()
    }

    /// Return all types referenced in the instruction.
    pub fn get_all_types(&self) -> HashSet<IdType> {
        HashSet::new()
    }

    /// Return all prototypes referenced in the instruction.
    pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
        HashSet::new()
    }

    /// Return all field ids referenced in the instruction.
    pub fn get_all_field_ids(&self) -> HashSet<IdField> {
        HashSet::new()
    }

    /// Return all method ids referenced in the instruction.
    pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
        HashSet::new()
    }

    /// Return all method handles referenced in the instruction.
    pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
        HashSet::new()
    }
}
