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 #[allow(clippy::missing_panics_doc)] pub fn new(bytecode: &[u8], enable_hooks: bool) -> Self {
58 let instructions = decode_program(
59 &bytecode
60 .chunks_exact(8)
61 .map(|chunk| u64::from_be_bytes(chunk.try_into().unwrap()))
62 .collect::<Vec<_>>(),
63 enable_hooks,
64 );
65 let code_page = bytecode
66 .chunks_exact(32)
67 .map(U256::from_big_endian)
68 .collect::<Vec<_>>();
69 Self {
70 instructions: instructions.into(),
71 code_page: code_page.into(),
72 }
73 }
74
75 pub fn from_words(bytecode_words: Vec<U256>, enable_hooks: bool) -> Self {
77 let instructions = decode_program(
78 &bytecode_words
79 .iter()
80 .flat_map(|x| x.0.into_iter().rev())
81 .collect::<Vec<_>>(),
82 enable_hooks,
83 );
84 Self {
85 instructions: instructions.into(),
86 code_page: bytecode_words.into(),
87 }
88 }
89
90 pub(crate) fn new_panicking() -> Self {
91 Self::from_raw(vec![Instruction::from_spontaneous_panic()], vec![])
92 }
93
94 #[doc(hidden)] pub fn from_raw(instructions: Vec<Instruction<T, W>>, code_page: Vec<U256>) -> Self {
96 Self {
97 instructions: instructions.into(),
98 code_page: code_page.into(),
99 }
100 }
101}
102
103impl<T, W> Program<T, W> {
104 pub(crate) fn instruction(&self, n: u16) -> Option<&Instruction<T, W>> {
105 self.instructions.get::<usize>(n.into())
106 }
107
108 pub fn code_page(&self) -> &[U256] {
110 &self.code_page
111 }
112}
113
114impl<T, W> PartialEq for Program<T, W> {
120 fn eq(&self, other: &Self) -> bool {
121 Arc::ptr_eq(&self.code_page, &other.code_page)
122 && Arc::ptr_eq(&self.instructions, &other.instructions)
123 }
124}
125
126fn jump_to_beginning<T, W>() -> Instruction<T, W> {
129 Instruction {
130 handler: jump_to_beginning_handler,
131 arguments: Arguments::new(Predicate::Always, 0, ModeRequirements::none()),
132 }
133}
134
135fn jump_to_beginning_handler<T, W>(
136 vm: &mut VirtualMachine<T, W>,
137 _: &mut W,
138 _: &mut T,
139) -> ExecutionStatus {
140 let first_instruction = vm.state.current_frame.program.instruction(0).unwrap();
141 vm.state.current_frame.pc = first_instruction;
142 ExecutionStatus::Running
143}
144
145fn decode_program<T: Tracer, W: World<T>>(
146 raw: &[u64],
147 is_bootloader: bool,
148) -> Vec<Instruction<T, W>> {
149 raw.iter()
150 .take(1 << 16)
151 .map(|i| decode(*i, is_bootloader))
152 .chain(std::iter::once(if raw.len() >= 1 << 16 {
153 jump_to_beginning()
154 } else {
155 Instruction::from_invalid()
156 }))
157 .collect()
158}