1use std::{fmt, sync::Arc};
2
3use primitive_types::U256;
4use zksync_vm2_interface::Tracer;
5
6use crate::{
7 addressing_modes::Arguments, decode::decode, hash_for_debugging, instruction::ExecutionStatus,
8 Instruction, ModeRequirements, Predicate, VirtualMachine, World,
9};
10
11pub struct Program<T, W> {
15 code_page: Arc<[U256]>,
19 instructions: Arc<[Instruction<T, W>]>,
20}
21
22impl<T, W> Clone for Program<T, W> {
23 fn clone(&self) -> Self {
24 Self {
25 code_page: self.code_page.clone(),
26 instructions: self.instructions.clone(),
27 }
28 }
29}
30
31impl<T, W> fmt::Debug for Program<T, W> {
32 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
33 const DEBUGGED_ITEMS: usize = 16;
34
35 let mut s = formatter.debug_struct("Program");
36 if self.code_page.len() <= DEBUGGED_ITEMS {
37 s.field("code_page", &self.code_page);
38 } else {
39 s.field("code_page.len", &self.code_page.len())
40 .field("code_page.start", &&self.code_page[..DEBUGGED_ITEMS])
41 .field("code_page.hash", &hash_for_debugging(&self.code_page));
42 }
43
44 if self.instructions.len() <= DEBUGGED_ITEMS {
45 s.field("instructions", &self.instructions);
46 } else {
47 s.field("instructions.len", &self.instructions.len())
48 .field("instructions.start", &&self.instructions[..DEBUGGED_ITEMS]);
49 }
50 s.finish_non_exhaustive()
51 }
52}
53
54impl<T: Tracer, W: World<T>> Program<T, W> {
55 pub fn new(bytecode: &[u8], enable_hooks: bool) -> Self {
57 let (words, _) = bytecode.as_chunks::<8>();
58 let instructions = decode_program(
59 &words
60 .iter()
61 .copied()
62 .map(u64::from_be_bytes)
63 .collect::<Vec<_>>(),
64 enable_hooks,
65 );
66 let (cells, _) = bytecode.as_chunks::<32>();
67 let code_page = cells
68 .iter()
69 .map(|cell| U256::from_big_endian(cell))
70 .collect::<Vec<_>>();
71 Self {
72 instructions: instructions.into(),
73 code_page: code_page.into(),
74 }
75 }
76
77 pub fn from_words(bytecode_words: Vec<U256>, enable_hooks: bool) -> Self {
79 let instructions = decode_program(
80 &bytecode_words
81 .iter()
82 .flat_map(|x| x.0.into_iter().rev())
83 .collect::<Vec<_>>(),
84 enable_hooks,
85 );
86 Self {
87 instructions: instructions.into(),
88 code_page: bytecode_words.into(),
89 }
90 }
91
92 pub(crate) fn new_panicking() -> Self {
93 Self::from_raw(vec![Instruction::from_spontaneous_panic()], vec![])
94 }
95
96 #[doc(hidden)] pub fn from_raw(instructions: Vec<Instruction<T, W>>, code_page: Vec<U256>) -> Self {
98 Self {
99 instructions: instructions.into(),
100 code_page: code_page.into(),
101 }
102 }
103}
104
105impl<T, W> Program<T, W> {
106 pub(crate) fn instruction(&self, n: u16) -> Option<&Instruction<T, W>> {
107 self.instructions.get::<usize>(n.into())
108 }
109
110 pub fn code_page(&self) -> &[U256] {
112 &self.code_page
113 }
114}
115
116impl<T, W> PartialEq for Program<T, W> {
122 fn eq(&self, other: &Self) -> bool {
123 Arc::ptr_eq(&self.code_page, &other.code_page)
124 && Arc::ptr_eq(&self.instructions, &other.instructions)
125 }
126}
127
128fn jump_to_beginning<T, W>() -> Instruction<T, W> {
131 Instruction {
132 handler: jump_to_beginning_handler,
133 arguments: Arguments::new(Predicate::Always, 0, ModeRequirements::none()),
134 }
135}
136
137fn jump_to_beginning_handler<T, W>(
138 vm: &mut VirtualMachine<T, W>,
139 _: &mut W,
140 _: &mut T,
141) -> ExecutionStatus {
142 let first_instruction = vm.state.current_frame.program.instruction(0).unwrap();
143 vm.state.current_frame.pc = first_instruction;
144 ExecutionStatus::Running
145}
146
147fn decode_program<T: Tracer, W: World<T>>(
148 raw: &[u64],
149 is_bootloader: bool,
150) -> Vec<Instruction<T, W>> {
151 raw.iter()
152 .take(1 << 16)
153 .map(|i| decode(*i, is_bootloader))
154 .chain(std::iter::once(if raw.len() >= 1 << 16 {
155 jump_to_beginning()
156 } else {
157 Instruction::from_invalid()
158 }))
159 .collect()
160}