From 05695a1416e1fe84e501bab53faace6d920381a8 Mon Sep 17 00:00:00 2001 From: wtfsck Date: Sat, 17 Jul 2021 20:20:35 +0200 Subject: [PATCH] Manually impl Serialize/Deserialize (Instruction) --- .../Enums/Rust/RustEnumsGenerator.cs | 2 + .../iced-x86-py/tests/instruction_test.py | 20 +- src/rust/iced-x86/Cargo.toml | 4 +- src/rust/iced-x86/src/enums.rs | 1 + src/rust/iced-x86/src/instruction.rs | 295 +++++++++++++++++- 5 files changed, 314 insertions(+), 8 deletions(-) diff --git a/src/csharp/Intel/Generator/Enums/Rust/RustEnumsGenerator.cs b/src/csharp/Intel/Generator/Enums/Rust/RustEnumsGenerator.cs index a81ba68d1..92d594420 100644 --- a/src/csharp/Intel/Generator/Enums/Rust/RustEnumsGenerator.cs +++ b/src/csharp/Intel/Generator/Enums/Rust/RustEnumsGenerator.cs @@ -276,6 +276,8 @@ namespace Generator.Enums.Rust { using (writer.Indent()) { writer.WriteLine($"/// Iterates over all `{enumTypeName}` enum values"); writer.WriteLine("#[inline]"); + if (!enumType.IsPublic) + writer.WriteLine(RustConstants.AttributeAllowDeadCode); writer.WriteLine($"{pub}fn values() -> impl Iterator + DoubleEndedIterator + ExactSizeIterator + FusedIterator {{"); using (writer.Indent()) { if (enumType.Values.Length == 1) { diff --git a/src/rust/iced-x86-py/tests/instruction_test.py b/src/rust/iced-x86-py/tests/instruction_test.py index 128bb6e21..26a970888 100644 --- a/src/rust/iced-x86-py/tests/instruction_test.py +++ b/src/rust/iced-x86-py/tests/instruction_test.py @@ -1208,10 +1208,24 @@ def test_fpu_stack_increment_info(): assert not info.conditional assert info.writes_top -def test_pickle(): - decoder = Decoder(64, b"\xC4\xE3\x49\x48\x10\x41", ip=0x1234_5678_9ABC_DEF1) +@pytest.mark.parametrize("bitness, data", [ + (32, b"\xC4\xE3\x49\x48\x10\x41"), + (64, b"\x62\x92\x7D\x01\xA0\xB4\xF4\x34\x12\x5A\xA5"), + (64, b"\xC4\xE3\x49\x48\xD3\x40"), +]) +def test_pickle(bitness, data): + decoder = Decoder(bitness, data, ip=0x1234_5678_9ABC_DEF1) instr = decoder.decode() + assert not instr.is_invalid + assert not decoder.can_decode + + dump = pickle.dumps(instr) + instr2 = pickle.loads(dump) + assert instr.eq_all_bits(instr2) + +def test_pickle_db(): + instr = Instruction.create_declare_byte(b"\x9E\x75\x1F\x88\xE7\x24\x11\xEB\x96\x4D\x17\x08\x2E\x83\x5B\xA5") + assert instr.declare_data_len == 16 dump = pickle.dumps(instr) instr2 = pickle.loads(dump) - assert instr.eq_all_bits(instr2) diff --git a/src/rust/iced-x86/Cargo.toml b/src/rust/iced-x86/Cargo.toml index 8f06e3aa1..1c7561a18 100644 --- a/src/rust/iced-x86/Cargo.toml +++ b/src/rust/iced-x86/Cargo.toml @@ -25,7 +25,7 @@ intel = [] masm = [] nasm = [] fast_fmt = [] -# Not used anymore +# Not used anymore, but removing it is a breaking change db = [] std = ["lazy_static"] # no_std feature is needed since std and no_std require different deps @@ -52,4 +52,4 @@ lazy_static = { version = "1.4.0", optional = true } hashbrown = { version = ">=0.9.0, <100.0.0", optional = true } # If: __internal_serde -serde = { version = "1.0", optional = true, features = ["derive"] } +serde = { version = "1.0.0", optional = true } diff --git a/src/rust/iced-x86/src/enums.rs b/src/rust/iced-x86/src/enums.rs index 11ef193df..4b89e9fdb 100644 --- a/src/rust/iced-x86/src/enums.rs +++ b/src/rust/iced-x86/src/enums.rs @@ -3867,6 +3867,7 @@ pub(crate) type InstrScaleUnderlyingType = u8; impl InstrScale { /// Iterates over all `InstrScale` enum values #[inline] + #[allow(dead_code)] pub(crate) fn values() -> impl Iterator + DoubleEndedIterator + ExactSizeIterator + FusedIterator { // SAFETY: all values 0-max are valid enum values (0..4).map(|x| unsafe { mem::transmute::(x as u8) }) diff --git a/src/rust/iced-x86/src/instruction.rs b/src/rust/iced-x86/src/instruction.rs index cf49b4d70..17ebe0bb1 100644 --- a/src/rust/iced-x86/src/instruction.rs +++ b/src/rust/iced-x86/src/instruction.rs @@ -12,8 +12,6 @@ use core::fmt; use core::hash::{Hash, Hasher}; use core::iter::{ExactSizeIterator, FusedIterator}; use core::{mem, slice, u16, u32, u64}; -#[cfg(feature = "__internal_serde")] -use serde::{Deserialize, Serialize}; use static_assertions::{const_assert, const_assert_eq}; // GENERATOR-BEGIN: InstrFlags1 @@ -47,7 +45,6 @@ impl InstrFlags1 { /// /// [`Decoder`]: struct.Decoder.html #[derive(Debug, Default, Copy, Clone)] -#[cfg_attr(feature = "__internal_serde", derive(Serialize, Deserialize))] pub struct Instruction { pub(crate) next_rip: u64, pub(crate) flags1: u32, // InstrFlags1 @@ -10542,3 +10539,295 @@ impl Iterator for OpKindIterator { impl ExactSizeIterator for OpKindIterator {} impl FusedIterator for OpKindIterator {} + +// All the enums have generated ser/de implementations without using the proc-macro. This is the only one left +// that is needed to completely remove serde proc-macro. +#[cfg(feature = "__internal_serde")] +const _: () = { + use core::convert::TryFrom; + use core::marker::PhantomData; + #[cfg(not(feature = "std"))] + use hashbrown::HashMap; + use lazy_static::lazy_static; + use serde::de; + use serde::ser::SerializeStruct; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + #[cfg(feature = "std")] + use std::collections::HashMap; + #[allow(non_camel_case_types)] + #[derive(Copy, Clone)] + enum StructField { + next_rip, + flags1, + immediate, + mem_displ, + mem_displ_hi, + code, + mem_base_reg, + mem_index_reg, + regs, + op_kinds, + scale, + displ_size, + len, + db, + } + const NUM_STRUCT_FIELDS: usize = StructField::db as usize + 1; + const FIELDS: &[&str] = &[ + "next_rip", + "flags1", + "immediate", + "mem_displ", + "mem_displ_hi", + "code", + "mem_base_reg", + "mem_index_reg", + "regs", + "op_kinds", + "scale", + "displ_size", + "len", + "db", + ]; + const_assert_eq!(FIELDS.len(), NUM_STRUCT_FIELDS); + impl StructField { + #[inline] + fn values() -> impl Iterator + DoubleEndedIterator + ExactSizeIterator + FusedIterator { + // SAFETY: all values 0-max are valid enum values + (0..NUM_STRUCT_FIELDS).map(|x| unsafe { mem::transmute::(x as u8) }) + } + } + lazy_static! { + static ref NAME_TO_FIELD: HashMap<&'static [u8], StructField> = FIELDS.iter().map(|&s| s.as_bytes()).zip(StructField::values()).collect(); + } + impl TryFrom for StructField { + type Error = &'static str; + #[inline] + fn try_from(value: usize) -> Result { + if value < NUM_STRUCT_FIELDS { + // SAFETY: all values 0-max are valid enum values + Ok(unsafe { mem::transmute(value as u8) }) + } else { + Err("Invalid struct field") + } + } + } + impl Serialize for Instruction { + #[allow(clippy::missing_inline_in_public_items)] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut serde_state = serializer.serialize_struct("Instruction", NUM_STRUCT_FIELDS)?; + let mut fields = 0; + macro_rules! serialize { + ($field:ident) => { + serde_state.serialize_field(stringify!($field), &self.$field)?; + fields += 1; + }; + } + serialize!(next_rip); + serialize!(flags1); + serialize!(immediate); + serialize!(mem_displ); + serialize!(mem_displ_hi); + serialize!(code); + serialize!(mem_base_reg); + serialize!(mem_index_reg); + serialize!(regs); + serialize!(op_kinds); + serialize!(scale); + serialize!(displ_size); + serialize!(len); + serialize!(db); + debug_assert_eq!(fields, NUM_STRUCT_FIELDS); + serde_state.end() + } + } + impl<'de> Deserialize<'de> for Instruction { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct StructFieldVisitor; + impl<'de> de::Visitor<'de> for StructFieldVisitor { + type Value = StructField; + #[inline] + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("field identifier") + } + #[inline] + fn visit_u64(self, v: u64) -> Result + where + E: de::Error, + { + if let Ok(v) = >::try_from(v) { + if let Ok(value) = >::try_from(v) { + return Ok(value); + } + } + Err(de::Error::invalid_value(de::Unexpected::Unsigned(v), &"Invalid Instruction field value")) + } + #[inline] + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + StructFieldVisitor::deserialize_name(v.as_bytes()) + } + #[inline] + fn visit_bytes(self, v: &[u8]) -> Result + where + E: de::Error, + { + StructFieldVisitor::deserialize_name(v) + } + } + impl StructFieldVisitor { + #[inline] + fn deserialize_name(v: &[u8]) -> Result + where + E: de::Error, + { + if let Some(&value) = NAME_TO_FIELD.get(v) { + Ok(value) + } else { + Err(de::Error::unknown_field(&String::from_utf8_lossy(v), &["Instruction fields"][..])) + } + } + } + impl<'de> Deserialize<'de> for StructField { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_identifier(StructFieldVisitor) + } + } + struct Visitor<'de> { + marker: PhantomData, + lifetime: PhantomData<&'de ()>, + } + impl<'de> de::Visitor<'de> for Visitor<'de> { + type Value = Instruction; + #[inline] + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("struct Instruction") + } + #[allow(clippy::missing_inline_in_public_items)] + #[allow(clippy::eval_order_dependence)] + fn visit_seq(self, mut seq: A) -> Result + where + A: de::SeqAccess<'de>, + { + let mut fields = 0; + macro_rules! next_element { + ($field_name:ident : $field_ty:ty) => {{ + fields += 1; + match seq.next_element::<$field_ty>()? { + Some(value) => value, + None => return Err(de::Error::invalid_length(fields, &"struct Instruction with all its fields")), + } + }}; + } + let instruction = Instruction { + next_rip: next_element!(next_rip: u64), + flags1: next_element!(flags1: u32), + immediate: next_element!(immediate: u32), + mem_displ: next_element!(mem_displ: u32), + mem_displ_hi: next_element!(mem_displ_hi: u32), + code: next_element!(code: Code), + mem_base_reg: next_element!(mem_base_reg: Register), + mem_index_reg: next_element!(mem_index_reg: Register), + regs: next_element!(regs: [Register; 4]), + op_kinds: next_element!(op_kinds: [OpKind; 4]), + scale: next_element!(scale: InstrScale), + displ_size: next_element!(displ_size: u8), + len: next_element!(len: u8), + db: next_element!(db: u8), + }; + debug_assert_eq!(fields, NUM_STRUCT_FIELDS); + Ok(instruction) + } + #[allow(clippy::missing_inline_in_public_items)] + #[allow(clippy::eval_order_dependence)] + fn visit_map(self, mut map: A) -> Result + where + A: de::MapAccess<'de>, + { + let mut next_rip: Option = None; + let mut flags1: Option = None; + let mut immediate: Option = None; + let mut mem_displ: Option = None; + let mut mem_displ_hi: Option = None; + let mut code: Option = None; + let mut mem_base_reg: Option = None; + let mut mem_index_reg: Option = None; + let mut regs: Option<[Register; 4]> = None; + let mut op_kinds: Option<[OpKind; 4]> = None; + let mut scale: Option = None; + let mut displ_size: Option = None; + let mut len: Option = None; + let mut db: Option = None; + while let Some(field) = map.next_key::()? { + macro_rules! unpack { + ($field:ident : $field_ty:ty) => {{ + if $field.is_some() { + return Err(::duplicate_field(stringify!($field))); + } + $field = Some(map.next_value::<$field_ty>()?); + }}; + } + match field { + StructField::next_rip => unpack!(next_rip: u64), + StructField::flags1 => unpack!(flags1: u32), + StructField::immediate => unpack!(immediate: u32), + StructField::mem_displ => unpack!(mem_displ: u32), + StructField::mem_displ_hi => unpack!(mem_displ_hi: u32), + StructField::code => unpack!(code: Code), + StructField::mem_base_reg => unpack!(mem_base_reg: Register), + StructField::mem_index_reg => unpack!(mem_index_reg: Register), + StructField::regs => unpack!(regs: [Register; 4]), + StructField::op_kinds => unpack!(op_kinds: [OpKind; 4]), + StructField::scale => unpack!(scale: InstrScale), + StructField::displ_size => unpack!(displ_size: u8), + StructField::len => unpack!(len: u8), + StructField::db => unpack!(db: u8), + } + } + let mut fields = 0; + macro_rules! unpack_field { + ($field:ident) => {{ + fields += 1; + match $field { + Some(value) => value, + None => return Err(::missing_field(stringify!($field))), + } + }}; + } + let instruction = Instruction { + next_rip: unpack_field!(next_rip), + flags1: unpack_field!(flags1), + immediate: unpack_field!(immediate), + mem_displ: unpack_field!(mem_displ), + mem_displ_hi: unpack_field!(mem_displ_hi), + code: unpack_field!(code), + mem_base_reg: unpack_field!(mem_base_reg), + mem_index_reg: unpack_field!(mem_index_reg), + regs: unpack_field!(regs), + op_kinds: unpack_field!(op_kinds), + scale: unpack_field!(scale), + displ_size: unpack_field!(displ_size), + len: unpack_field!(len), + db: unpack_field!(db), + }; + debug_assert_eq!(fields, NUM_STRUCT_FIELDS); + Ok(instruction) + } + } + deserializer.deserialize_struct("Instruction", FIELDS, Visitor { marker: PhantomData::, lifetime: PhantomData }) + } + } +};