Skip to main content

revm_bytecode/bytecode/
mod.rs

1//! Module that contains the bytecode struct with all variants supported by Ethereum mainnet.
2//!
3//! Those are:
4//! - Legacy bytecode with jump table analysis
5//! - EIP-7702 bytecode, introduced in Prague and contains address to delegated account
6
7#[cfg(feature = "serde")]
8mod serde_impl;
9
10use crate::{
11    eip7702::{Eip7702DecodeError, EIP7702_MAGIC_BYTES, EIP7702_VERSION},
12    legacy::{analyze_legacy, pad_legacy},
13    opcode, BytecodeDecodeError, JumpTable,
14};
15use core::fmt;
16use primitives::{
17    alloy_primitives::Sealable, keccak256, Address, Bytes, OnceLock, B256, KECCAK_EMPTY,
18};
19use std::sync::Arc;
20
21/// Ethereum EVM bytecode.
22#[derive(Clone)]
23pub struct Bytecode(Option<Arc<BytecodeInner>>);
24
25/// Inner bytecode representation.
26///
27/// This struct is flattened to avoid nested allocations. The `kind` field determines
28/// how the bytecode should be interpreted.
29#[derive(Debug)]
30struct BytecodeInner {
31    /// The kind of bytecode (Legacy or EIP-7702).
32    kind: BytecodeKind,
33    /// Bytecode padded for execution. Exactly 23 unpadded bytes for EIP-7702.
34    bytecode: Bytes,
35    /// Length of the original bytecode before padding.
36    original_len: usize,
37    /// Cached jump table for legacy bytecode. Uninitialized for EIP-7702.
38    jump_table: OnceLock<JumpTable>,
39    /// Cached hash of the original bytecode.
40    hash: OnceLock<B256>,
41}
42
43/// The kind of bytecode.
44#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Default)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46pub enum BytecodeKind {
47    /// Legacy bytecode with padding and a lazily initialized jump table.
48    #[default]
49    LegacyAnalyzed,
50    /// EIP-7702 delegated bytecode.
51    Eip7702,
52}
53
54impl Default for Bytecode {
55    #[inline]
56    fn default() -> Self {
57        Self::new()
58    }
59}
60
61impl PartialEq for Bytecode {
62    #[inline]
63    fn eq(&self, other: &Self) -> bool {
64        self.original_byte_slice() == other.original_byte_slice()
65    }
66}
67
68impl Eq for Bytecode {}
69
70impl core::hash::Hash for Bytecode {
71    #[inline]
72    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
73        self.original_byte_slice().hash(state);
74    }
75}
76
77impl PartialOrd for Bytecode {
78    #[inline]
79    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
80        Some(self.cmp(other))
81    }
82}
83
84impl Ord for Bytecode {
85    #[inline]
86    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
87        self.original_byte_slice().cmp(other.original_byte_slice())
88    }
89}
90
91impl Sealable for Bytecode {
92    #[inline]
93    fn hash_slow(&self) -> B256 {
94        self.hash_slow()
95    }
96}
97
98impl Bytecode {
99    /// Creates empty legacy [`Bytecode`] that executes as a single STOP opcode.
100    #[inline]
101    pub const fn new() -> Self {
102        Self(None)
103    }
104
105    #[inline]
106    fn empty_inner() -> &'static BytecodeInner {
107        static EMPTY: OnceLock<BytecodeInner> = OnceLock::new();
108        EMPTY.get_or_init(|| BytecodeInner {
109            kind: BytecodeKind::LegacyAnalyzed,
110            bytecode: Bytes::from_static(&[opcode::STOP]),
111            original_len: 0,
112            jump_table: OnceLock::new(),
113            hash: {
114                let hash = OnceLock::new();
115                let _ = hash.set(KECCAK_EMPTY);
116                hash
117            },
118        })
119    }
120
121    #[inline]
122    fn inner(&self) -> &BytecodeInner {
123        match &self.0 {
124            Some(inner) => inner,
125            None => Self::empty_inner(),
126        }
127    }
128
129    /// Creates a new legacy [`Bytecode`] from raw bytes without analyzing them.
130    ///
131    /// Appends 33 zero bytes unless the last 33 bytes are already zero.
132    /// Empty code uses a single STOP instead. The jump table is initialized
133    /// on the first call to [`Self::legacy_jump_table`].
134    #[inline]
135    pub fn new_legacy(raw: Bytes) -> Self {
136        if raw.is_empty() {
137            return Self::new();
138        }
139
140        Self(Some(Arc::new(BytecodeInner {
141            kind: BytecodeKind::LegacyAnalyzed,
142            original_len: raw.len(),
143            bytecode: pad_legacy(raw),
144            jump_table: OnceLock::new(),
145            hash: OnceLock::new(),
146        })))
147    }
148
149    /// Creates a new raw [`Bytecode`].
150    ///
151    /// # Panics
152    ///
153    /// Panics if bytecode is in incorrect format. If you want to handle errors use [`Self::new_raw_checked`].
154    #[inline]
155    pub fn new_raw(bytecode: Bytes) -> Self {
156        Self::new_raw_checked(bytecode).expect("Expect correct bytecode")
157    }
158
159    /// Creates a new EIP-7702 [`Bytecode`] from [`Address`].
160    #[inline]
161    pub fn new_eip7702(address: Address) -> Self {
162        let raw: Bytes = [EIP7702_MAGIC_BYTES, &[EIP7702_VERSION], &address[..]]
163            .concat()
164            .into();
165        Self(Some(Arc::new(BytecodeInner {
166            kind: BytecodeKind::Eip7702,
167            original_len: raw.len(),
168            bytecode: raw,
169            jump_table: OnceLock::new(),
170            hash: OnceLock::new(),
171        })))
172    }
173
174    /// Creates a new raw [`Bytecode`].
175    ///
176    /// Returns an error on incorrect bytecode format.
177    #[inline]
178    pub fn new_raw_checked(bytes: Bytes) -> Result<Self, BytecodeDecodeError> {
179        if bytes.starts_with(EIP7702_MAGIC_BYTES) {
180            Self::new_eip7702_raw(bytes).map_err(Into::into)
181        } else {
182            Ok(Self::new_legacy(bytes))
183        }
184    }
185
186    /// Creates a new EIP-7702 [`Bytecode`] from raw bytes.
187    ///
188    /// Returns an error if the bytes are not valid EIP-7702 bytecode.
189    #[inline]
190    pub fn new_eip7702_raw(bytes: Bytes) -> Result<Self, Eip7702DecodeError> {
191        if bytes.len() != 23 {
192            return Err(Eip7702DecodeError::InvalidLength);
193        }
194        if !bytes.starts_with(EIP7702_MAGIC_BYTES) {
195            return Err(Eip7702DecodeError::InvalidMagic);
196        }
197        if bytes[2] != EIP7702_VERSION {
198            return Err(Eip7702DecodeError::UnsupportedVersion);
199        }
200        Ok(Self(Some(Arc::new(BytecodeInner {
201            kind: BytecodeKind::Eip7702,
202            original_len: bytes.len(),
203            bytecode: bytes,
204            jump_table: OnceLock::new(),
205            hash: OnceLock::new(),
206        }))))
207    }
208
209    /// Create new checked bytecode from pre-analyzed components.
210    ///
211    /// # Safety
212    ///
213    /// `bytecode` must satisfy the same padding invariants as the result of
214    /// [`Bytecode::bytecode`]. In particular, execution must never cause the
215    /// interpreter to read past the backing allocation when decoding opcode
216    /// immediates (`PUSH1`–`PUSH32` via `read_slice`, and `DUPN`/`SWAPN`/
217    /// `EXCHANGE` via `read_u8`).
218    ///
219    /// [`Bytecode::new_legacy`] handles this automatically.
220    /// This constructor is only for restoring trusted, previously analyzed
221    /// bytecode (e.g., from database storage) where the padding was already
222    /// applied.
223    ///
224    /// Violating this causes undefined behavior during execution due to
225    /// out-of-bounds reads from raw pointers.
226    ///
227    /// # Panics
228    ///
229    /// * If `original_len` is greater than `bytecode.len()`
230    /// * If jump table length is less than `original_len`
231    /// * If bytecode is empty
232    #[inline]
233    pub unsafe fn new_analyzed(
234        bytecode: Bytes,
235        original_len: usize,
236        jump_table: JumpTable,
237    ) -> Self {
238        assert!(
239            original_len <= bytecode.len(),
240            "original_len is greater than bytecode length"
241        );
242        assert!(
243            original_len <= jump_table.len(),
244            "jump table length is less than original length"
245        );
246        assert!(!bytecode.is_empty(), "bytecode cannot be empty");
247        let cached_jump_table = OnceLock::new();
248        let _ = cached_jump_table.set(jump_table);
249        Self(Some(Arc::new(BytecodeInner {
250            kind: BytecodeKind::LegacyAnalyzed,
251            bytecode,
252            original_len,
253            jump_table: cached_jump_table,
254            hash: OnceLock::new(),
255        })))
256    }
257
258    /// Returns the kind of bytecode.
259    #[inline]
260    pub fn kind(&self) -> BytecodeKind {
261        self.inner().kind
262    }
263
264    /// Returns `true` if bytecode is legacy.
265    #[inline]
266    pub fn is_legacy(&self) -> bool {
267        self.kind() == BytecodeKind::LegacyAnalyzed
268    }
269
270    /// Returns `true` if bytecode is EIP-7702.
271    #[inline]
272    pub fn is_eip7702(&self) -> bool {
273        self.kind() == BytecodeKind::Eip7702
274    }
275
276    /// Returns the EIP-7702 delegated address if this is EIP-7702 bytecode.
277    #[inline]
278    pub fn eip7702_address(&self) -> Option<Address> {
279        if self.is_eip7702() {
280            Some(Address::from_slice(&self.inner().bytecode[3..23]))
281        } else {
282            None
283        }
284    }
285
286    /// Returns the jump table for legacy bytecode, initializing it if necessary.
287    #[inline]
288    pub fn legacy_jump_table(&self) -> Option<&JumpTable> {
289        if self.is_legacy() {
290            Some(
291                self.inner()
292                    .jump_table
293                    .get_or_init(|| analyze_legacy(self.original_byte_slice())),
294            )
295        } else {
296            None
297        }
298    }
299
300    /// Calculates or returns cached hash of the bytecode.
301    #[inline]
302    pub fn hash_slow(&self) -> B256 {
303        *self
304            .inner()
305            .hash
306            .get_or_init(|| keccak256(self.original_byte_slice()))
307    }
308
309    /// Returns a reference to the bytecode bytes.
310    ///
311    /// For legacy bytecode, this includes padding. For EIP-7702, this is the raw bytes.
312    #[inline]
313    pub fn bytecode(&self) -> &Bytes {
314        &self.inner().bytecode
315    }
316
317    /// Pointer to the bytecode bytes.
318    #[inline]
319    pub fn bytecode_ptr(&self) -> *const u8 {
320        self.bytecode().as_ptr()
321    }
322
323    /// Returns a clone of the bytecode bytes.
324    #[inline]
325    pub fn bytes(&self) -> Bytes {
326        self.bytecode().clone()
327    }
328
329    /// Returns a reference to the bytecode bytes.
330    #[inline]
331    pub fn bytes_ref(&self) -> &Bytes {
332        self.bytecode()
333    }
334
335    /// Returns the bytecode as a slice.
336    #[inline]
337    pub fn bytes_slice(&self) -> &[u8] {
338        self.bytecode()
339    }
340
341    /// Returns the original bytecode without padding.
342    #[inline]
343    pub fn original_bytes(&self) -> Bytes {
344        let inner = self.inner();
345        inner.bytecode.slice(..inner.original_len)
346    }
347
348    /// Returns the original bytecode as a byte slice without padding.
349    #[inline]
350    pub fn original_byte_slice(&self) -> &[u8] {
351        let inner = self.inner();
352        &inner.bytecode[..inner.original_len]
353    }
354
355    /// Returns the length of the original bytes (without padding).
356    #[inline]
357    pub fn len(&self) -> usize {
358        self.inner().original_len
359    }
360
361    /// Returns whether the bytecode is empty.
362    #[inline]
363    pub fn is_empty(&self) -> bool {
364        self.inner().original_len == 0
365    }
366
367    /// Returns `true` if the bytecode is empty and has the default bytecode hash.
368    #[inline]
369    pub const fn is_default(&self) -> bool {
370        self.0.is_none()
371    }
372
373    /// Returns an iterator over the opcodes in this bytecode, skipping immediates.
374    #[inline]
375    pub fn iter_opcodes(&self) -> crate::BytecodeIterator<'_> {
376        crate::BytecodeIterator::new(self)
377    }
378}
379
380impl fmt::Debug for Bytecode {
381    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
382        formatter
383            .debug_tuple("Bytecode")
384            .field(self.inner())
385            .finish()
386    }
387}
388
389#[cfg(test)]
390mod tests {
391    extern crate std as test_std;
392
393    use super::*;
394    use crate::{eip7702::Eip7702DecodeError, opcode};
395    use bitvec::{bitvec, order::Lsb0};
396    use primitives::bytes;
397    use std::format;
398    use test_std::hash::{BuildHasher, RandomState};
399
400    #[test]
401    fn test_new_empty() {
402        let analyzed = unsafe {
403            Bytecode::new_analyzed(Bytes::from_static(&[opcode::STOP]), 0, JumpTable::default())
404        };
405        assert!(!analyzed.is_default());
406        assert!(analyzed.0.is_some());
407        let hasher = RandomState::new();
408        for bytecode in [
409            Bytecode::default(),
410            Bytecode::new(),
411            Bytecode::new().clone(),
412            Bytecode::new_legacy(Bytes::new()),
413            Bytecode::new_raw(Bytes::new()),
414            Bytecode::new_raw_checked(Bytes::new()).unwrap(),
415        ] {
416            assert!(bytecode.0.is_none());
417            assert!(bytecode.is_default());
418            assert_eq!(bytecode.kind(), BytecodeKind::LegacyAnalyzed);
419            assert!(bytecode.is_default());
420            assert_eq!(bytecode.len(), 0);
421            assert_eq!(bytecode.bytes_slice(), [opcode::STOP]);
422            assert!(bytecode.is_empty());
423            assert!(bytecode.is_legacy());
424            assert!(!bytecode.is_eip7702());
425            assert_eq!(bytecode.eip7702_address(), None);
426            assert_eq!(bytecode.bytecode(), &Bytes::from_static(&[opcode::STOP]));
427            assert_eq!(bytecode.original_bytes(), Bytes::new());
428            assert!(bytecode.original_byte_slice().is_empty());
429            assert_eq!(bytecode.hash_slow(), KECCAK_EMPTY);
430            assert!(bytecode.legacy_jump_table().unwrap().is_empty());
431            assert!(bytecode.is_default());
432            assert!(bytecode.clone().is_default());
433            assert_eq!(bytecode, analyzed);
434            assert_eq!(bytecode.cmp(&analyzed), core::cmp::Ordering::Equal);
435            assert_eq!(hasher.hash_one(&bytecode), hasher.hash_one(&analyzed));
436            assert!(bytecode < Bytecode::new_legacy(Bytes::from_static(&[opcode::STOP])));
437        }
438    }
439
440    #[test]
441    fn test_new_analyzed() {
442        let raw = Bytes::from_static(&[opcode::PUSH1, 0x01]);
443        let bytecode = Bytecode::new_legacy(raw);
444        // SAFETY: bytecode was produced by `new_legacy` which pads correctly.
445        let _ = unsafe {
446            Bytecode::new_analyzed(
447                bytecode.bytecode().clone(),
448                bytecode.len(),
449                bytecode.legacy_jump_table().unwrap().clone(),
450            )
451        };
452    }
453
454    #[test]
455    #[should_panic(expected = "original_len is greater than bytecode length")]
456    fn test_panic_on_large_original_len() {
457        let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
458        // SAFETY: testing the panic, not execution safety.
459        let _ = unsafe {
460            Bytecode::new_analyzed(
461                bytecode.bytecode().clone(),
462                100,
463                bytecode.legacy_jump_table().unwrap().clone(),
464            )
465        };
466    }
467
468    #[test]
469    #[should_panic(expected = "jump table length is less than original length")]
470    fn test_panic_on_short_jump_table() {
471        let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
472        let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 1]);
473        // SAFETY: testing the panic, not execution safety.
474        let _ = unsafe {
475            Bytecode::new_analyzed(bytecode.bytecode().clone(), bytecode.len(), jump_table)
476        };
477    }
478
479    #[test]
480    #[should_panic(expected = "bytecode cannot be empty")]
481    fn test_panic_on_empty_bytecode() {
482        let bytecode = Bytes::from_static(&[]);
483        let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 0]);
484        // SAFETY: testing the panic, not execution safety.
485        let _ = unsafe { Bytecode::new_analyzed(bytecode, 0, jump_table) };
486    }
487
488    #[test]
489    fn eip7702_sanity_decode() {
490        let raw = bytes!("ef01deadbeef");
491        assert_eq!(
492            Bytecode::new_eip7702_raw(raw),
493            Err(Eip7702DecodeError::InvalidLength)
494        );
495
496        let raw = bytes!("ef0101deadbeef00000000000000000000000000000000");
497        assert_eq!(
498            Bytecode::new_eip7702_raw(raw),
499            Err(Eip7702DecodeError::UnsupportedVersion)
500        );
501
502        let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
503        let bytecode = Bytecode::new_eip7702_raw(raw.clone()).unwrap();
504        assert!(bytecode.is_eip7702());
505        assert_eq!(
506            bytecode.eip7702_address(),
507            Some(Address::from_slice(&raw[3..]))
508        );
509        assert_eq!(bytecode.original_bytes(), raw);
510    }
511
512    #[test]
513    fn eip7702_from_address() {
514        let address = Address::new([0x01; 20]);
515        let bytecode = Bytecode::new_eip7702(address);
516        assert_eq!(bytecode.eip7702_address(), Some(address));
517        assert_eq!(
518            bytecode.original_bytes(),
519            bytes!("ef01000101010101010101010101010101010101010101")
520        );
521    }
522
523    #[test]
524    fn eip7702_invalid_magic() {
525        let raw1 = bytes!("ee0101deadbeef00000000000000000000000000000000");
526        assert_eq!(
527            Bytecode::new_eip7702_raw(raw1),
528            Err(Eip7702DecodeError::InvalidMagic)
529        );
530
531        let raw2 = bytes!("ef0001deadbeef00000000000000000000000000000000");
532        assert_eq!(
533            Bytecode::new_eip7702_raw(raw2),
534            Err(Eip7702DecodeError::InvalidMagic)
535        );
536    }
537
538    #[test]
539    fn new_raw_checked_dispatch() {
540        let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
541        let bytecode = Bytecode::new_raw_checked(raw).unwrap();
542        assert!(bytecode.is_eip7702());
543
544        assert_eq!(
545            Bytecode::new_raw_checked(bytes!("ef01")),
546            Err(BytecodeDecodeError::Eip7702(
547                Eip7702DecodeError::InvalidLength
548            ))
549        );
550
551        let bytecode = Bytecode::new_raw_checked(bytes!("ef00")).unwrap();
552        assert!(bytecode.is_legacy());
553        assert_eq!(bytecode.eip7702_address(), None);
554    }
555
556    #[test]
557    fn is_default() {
558        assert!(Bytecode::default().is_default());
559    }
560
561    #[test]
562    #[cfg(feature = "serde")]
563    fn is_default_after_serde() {
564        let bc = Bytecode::default();
565        let json = serde_json::to_string(&bc).unwrap();
566        let deser: Bytecode = serde_json::from_str(&json).unwrap();
567        assert!(deser.is_default());
568    }
569
570    #[test]
571    fn analysis_is_lazy_and_shared() {
572        let raw = bytes!("5b605b7f");
573        for bytecode in [
574            Bytecode::new_legacy(raw.clone()),
575            Bytecode::new_raw(raw.clone()),
576            Bytecode::new_raw_checked(raw.clone()).unwrap(),
577        ] {
578            let cloned = bytecode.clone();
579            assert_eq!(bytecode.original_byte_slice(), raw.as_ref());
580            assert_eq!(bytecode.original_bytes().as_ptr(), bytecode.bytecode_ptr());
581            assert_eq!(bytecode.len(), raw.len());
582            assert!(!bytecode.is_empty());
583            assert_eq!(bytecode, cloned);
584            assert_eq!(bytecode.cmp(&cloned), core::cmp::Ordering::Equal);
585            assert_eq!(bytecode.iter_opcodes().count(), 3);
586            assert_eq!(bytecode.hash_slow(), keccak256(&raw));
587            assert_eq!(bytecode.bytecode().len(), raw.len() + 33);
588            assert!(bytecode.inner().jump_table.get().is_none());
589
590            let table = bytecode.legacy_jump_table().unwrap();
591            assert_eq!(bytecode.original_bytes().as_ptr(), bytecode.bytecode_ptr());
592            assert_eq!(table.len(), raw.len());
593            assert!(table.is_valid(0));
594            assert!(!table.is_valid(2)); // JUMPDEST inside PUSH1 immediate data.
595            assert!(!table.is_valid(raw.len())); // Padding is not a jump destination.
596            assert!(core::ptr::eq(
597                table,
598                bytecode.inner().jump_table.get().unwrap()
599            ));
600            assert!(core::ptr::eq(table, cloned.legacy_jump_table().unwrap()));
601            assert_eq!(bytecode.bytecode_ptr(), cloned.bytecode_ptr());
602        }
603    }
604
605    #[test]
606    fn supplied_analysis_is_preserved() {
607        static TABLE: &[u8] = &[1];
608        let padded = bytes!("5b600000");
609        let ptr = padded.as_ptr();
610        // SAFETY: The trailing PUSH1 has a zero immediate followed by STOP.
611        let bytecode =
612            unsafe { Bytecode::new_analyzed(padded, 2, JumpTable::from_static_slice(TABLE, 2)) };
613        assert!(bytecode.inner().jump_table.get().is_some());
614        assert_eq!(bytecode.original_bytes(), bytes!("5b60"));
615        assert_eq!(bytecode.bytecode_ptr(), ptr);
616        let table = bytecode.legacy_jump_table().unwrap();
617        assert!(core::ptr::eq(table.as_slice(), TABLE));
618        assert!(table.is_valid(0));
619        assert!(!table.is_valid(1));
620    }
621
622    #[test]
623    fn empty_and_delegated_jump_tables() {
624        assert!(Bytecode::new().legacy_jump_table().unwrap().is_empty());
625        let delegated = Bytecode::new_eip7702(Address::ZERO);
626        let from_raw = Bytecode::new_eip7702_raw(delegated.original_bytes()).unwrap();
627        for bytecode in [delegated, from_raw] {
628            assert!(bytecode.legacy_jump_table().is_none());
629            assert_eq!(bytecode.bytes_slice(), bytecode.original_byte_slice());
630            assert_eq!(bytecode.bytecode_ptr(), bytecode.original_bytes().as_ptr());
631            assert!(bytecode.inner().jump_table.get().is_none());
632        }
633    }
634
635    #[test]
636    #[cfg(feature = "serde")]
637    fn lazy_analysis_serde_compatibility() {
638        let bytecode = Bytecode::new_legacy(bytes!("5b60"));
639        assert!(bytecode.inner().jump_table.get().is_none());
640        let mut serialized = serde_json::to_value(&bytecode).unwrap();
641        assert_eq!(
642            serialized,
643            serde_json::json!({
644                "LegacyAnalyzed": {
645                    "bytecode": std::format!("0x5b60{}", "00".repeat(33)),
646                    "original_len": 2,
647                    "jump_table": {
648                        "order": "bitvec::order::Lsb0",
649                        "head": { "width": 8, "index": 0 },
650                        "bits": 2,
651                        "data": [1]
652                    }
653                }
654            })
655        );
656        assert!(bytecode.inner().jump_table.get().is_some());
657
658        // Deserialization must not trust supplied padding or analysis from untrusted input.
659        serialized["LegacyAnalyzed"]["bytecode"] = serde_json::json!("0x5b60");
660        serialized["LegacyAnalyzed"]["jump_table"]["data"] = serde_json::json!([2]);
661        let restored: Bytecode = serde_json::from_value(serialized).unwrap();
662        assert!(restored.inner().jump_table.get().is_none());
663        assert_eq!(restored, bytecode);
664        assert_eq!(restored.bytecode(), bytecode.bytecode());
665        assert_eq!(restored.legacy_jump_table(), bytecode.legacy_jump_table());
666    }
667
668    #[test]
669    fn bytecode_access_does_not_initialize_jump_table() {
670        let bytecode = Bytecode::new_raw(bytes!("7f"));
671        let cloned = bytecode.clone();
672        assert!(bytecode.inner().jump_table.get().is_none());
673
674        // The interpreter acquires this pointer before reading instructions or immediates.
675        let ptr = bytecode.bytecode_ptr();
676        assert_eq!(bytecode.bytecode().len(), 34);
677        assert_eq!(bytecode.bytecode()[0], opcode::PUSH32);
678        assert_eq!(&bytecode.bytecode()[1..], &[opcode::STOP; 33]);
679        assert_eq!(bytecode.bytecode().as_ptr(), ptr);
680        assert_eq!(bytecode.bytes().as_ptr(), ptr);
681        assert_eq!(bytecode.bytes_ref().as_ptr(), ptr);
682        assert_eq!(bytecode.bytes_slice().as_ptr(), ptr);
683        assert_eq!(cloned.bytecode_ptr(), ptr);
684        assert_eq!(bytecode.original_byte_slice(), &[opcode::PUSH32]);
685        assert!(bytecode.inner().jump_table.get().is_none());
686        let table = bytecode.legacy_jump_table().unwrap();
687        assert_eq!(table.len(), 1);
688        assert!(!table.is_valid(0));
689    }
690
691    #[test]
692    fn existing_zero_suffix_is_original_code() {
693        let mut raw = [0; 34];
694        raw[0] = opcode::PUSH32;
695        let raw = Bytes::copy_from_slice(&raw);
696        let bytecode = Bytecode::new_legacy(raw.clone());
697        assert_eq!(bytecode.bytecode_ptr(), raw.as_ptr());
698        assert_eq!(bytecode.len(), raw.len());
699        assert_eq!(bytecode.original_bytes(), raw);
700        assert_eq!(bytecode.hash_slow(), keccak256(&raw));
701        assert!(bytecode.inner().jump_table.get().is_none());
702        assert_eq!(bytecode.legacy_jump_table().unwrap().len(), raw.len());
703    }
704
705    #[test]
706    fn empty_debug_matches_shared_form() {
707        let empty = Bytecode::new();
708        let shared = Arc::new(empty.inner());
709        let _ = empty.hash_slow();
710        let _ = empty.legacy_jump_table();
711        assert_eq!(format!("{empty:?}"), format!("Bytecode({shared:?})"));
712    }
713
714    #[test]
715    #[cfg(feature = "serde")]
716    fn empty_serde_roundtrip() {
717        let empty = Bytecode::new();
718        let analyzed = unsafe {
719            Bytecode::new_analyzed(Bytes::from_static(&[opcode::STOP]), 0, JumpTable::default())
720        };
721        let serialized = serde_json::to_value(&empty).unwrap();
722        assert_eq!(serialized, serde_json::to_value(&analyzed).unwrap());
723        let restored: Bytecode = serde_json::from_value(serialized).unwrap();
724        assert_eq!(restored, empty);
725        assert!(restored.is_default());
726        assert!(restored.0.is_none());
727        assert_eq!(restored.bytes_slice(), [opcode::STOP]);
728    }
729}