fuel_asm/
panic_instruction.rs1use core::fmt;
2
3use crate::{
4 Instruction,
5 PanicReason,
6 RawInstruction,
7 Word,
8};
9
10#[derive(Clone, Copy, PartialEq, Eq, Hash)]
11#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[derive(fuel_types::canonical::Deserialize, fuel_types::canonical::Serialize)]
14pub struct PanicInstruction {
16 #[canonical(skip)]
17 reason: PanicReason,
18 instruction: RawInstruction,
19}
20
21impl PanicInstruction {
22 pub const fn error(reason: PanicReason, instruction: RawInstruction) -> Self {
24 Self {
25 reason,
26 instruction,
27 }
28 }
29
30 pub const fn reason(&self) -> &PanicReason {
32 &self.reason
33 }
34
35 pub const fn instruction(&self) -> &RawInstruction {
37 &self.instruction
38 }
39}
40
41struct InstructionDbg(RawInstruction);
43impl fmt::Debug for InstructionDbg {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 match Instruction::try_from(self.0) {
47 Ok(instr) => write!(f, "{:?}", instr)?,
48 Err(_) => write!(f, "Unknown")?,
49 };
50 write!(f, " (bytes: ")?;
51 for (i, byte) in self.0.to_be_bytes().iter().enumerate() {
52 if i != 0 {
53 write!(f, " ")?;
54 }
55 write!(f, "{:02x}", byte)?;
56 }
57 write!(f, ")")
58 }
59}
60
61impl fmt::Debug for PanicInstruction {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 f.debug_struct("PanicInstruction")
64 .field("reason", &self.reason)
65 .field("instruction", &InstructionDbg(self.instruction))
66 .finish()
67 }
68}
69
70#[cfg(feature = "typescript")]
71#[wasm_bindgen::prelude::wasm_bindgen]
72impl PanicInstruction {
73 #[wasm_bindgen(constructor)]
75 pub fn error_typescript(reason: PanicReason, instruction: RawInstruction) -> Self {
76 Self::error(reason, instruction)
77 }
78
79 #[wasm_bindgen(js_name = reason)]
81 pub fn reason_typescript(&self) -> PanicReason {
82 *self.reason()
83 }
84
85 #[wasm_bindgen(js_name = instruction)]
87 pub fn instruction_typescript(&self) -> RawInstruction {
88 *self.instruction()
89 }
90}
91
92const WORD_SIZE: usize = core::mem::size_of::<Word>();
93const REASON_OFFSET: Word = (WORD_SIZE * 8 - 8) as Word;
94const INSTR_OFFSET: Word = REASON_OFFSET - (Instruction::SIZE * 8) as Word;
95
96impl From<PanicInstruction> for Word {
97 fn from(r: PanicInstruction) -> Word {
98 let reason = Word::from(r.reason as u8);
99 let instruction = Word::from(r.instruction);
100 (reason << REASON_OFFSET) | (instruction << INSTR_OFFSET)
101 }
102}
103
104impl From<Word> for PanicInstruction {
105 #[allow(clippy::cast_possible_truncation)]
106 fn from(val: Word) -> Self {
107 let reason_u8 = (val >> REASON_OFFSET) as u8;
109 let instruction = (val >> INSTR_OFFSET) as u32;
111 let reason = PanicReason::from(reason_u8);
112 Self {
113 reason,
114 instruction,
115 }
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::op;
123 use fuel_types::canonical::Serialize;
124
125 #[test]
126 fn canonical_serialization_ignores_panic_reason() {
127 let revert_panic_instruction =
128 PanicInstruction::error(PanicReason::Revert, op::noop().into());
129 let out_of_gas_panic_instruction =
130 PanicInstruction::error(PanicReason::OutOfGas, op::noop().into());
131 assert_eq!(
132 revert_panic_instruction.to_bytes(),
133 out_of_gas_panic_instruction.to_bytes()
134 );
135 }
136}