snarkvm_synthesizer_program/constructor/
mod.rs1mod bytes;
17mod parse;
18
19use crate::Command;
20
21use console::{
22 network::prelude::*,
23 program::{Identifier, Register},
24};
25
26use std::collections::HashMap;
27
28#[derive(Clone, PartialEq, Eq)]
29pub struct ConstructorCore<N: Network> {
30 commands: Vec<Command<N>>,
32 num_writes: u16,
34 positions: HashMap<Identifier<N>, usize>,
36}
37
38impl<N: Network> ConstructorCore<N> {
39 pub fn commands(&self) -> &[Command<N>] {
41 &self.commands
42 }
43
44 pub const fn num_writes(&self) -> u16 {
46 self.num_writes
47 }
48
49 pub const fn positions(&self) -> &HashMap<Identifier<N>, usize> {
51 &self.positions
52 }
53
54 pub fn contains_external_struct(&self) -> bool {
56 self.commands.iter().any(|command| command.contains_external_struct())
57 }
58
59 pub fn contains_string_type(&self) -> bool {
61 self.commands.iter().any(|command| command.contains_string_type())
62 }
63
64 pub fn contains_identifier_type(&self) -> Result<bool> {
66 for command in &self.commands {
67 if command.contains_identifier_type()? {
68 return Ok(true);
69 }
70 }
71 Ok(false)
72 }
73
74 pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
76 self.commands.iter().any(|command| command.exceeds_max_array_size(max_array_size))
77 }
78}
79
80impl<N: Network> ConstructorCore<N> {
81 #[inline]
86 pub fn add_command(&mut self, command: Command<N>) -> Result<()> {
87 ensure!(self.commands.len() < N::MAX_COMMANDS, "Cannot add more than {} commands", N::MAX_COMMANDS);
89 if command.is_write() {
91 ensure!(
92 self.num_writes < N::LATEST_MAX_WRITES(),
93 "Cannot add more than {} 'set' & 'remove' commands",
94 N::LATEST_MAX_WRITES()
95 );
96 }
97
98 ensure!(!command.is_async(), "Forbidden operation: Constructor cannot invoke an 'async' instruction");
100 ensure!(!command.is_call(), "Forbidden operation: Constructor cannot invoke a 'call'");
102 ensure!(!command.is_cast_to_record(), "Forbidden operation: Constructor cannot cast to a record");
104 ensure!(!command.is_await(), "Forbidden operation: Constructor cannot 'await'");
106
107 for register in command.destinations() {
109 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
111 }
112
113 if let Some(position) = command.branch_to() {
115 ensure!(!self.positions.contains_key(position), "Cannot branch to an earlier position '{position}'");
117 }
118
119 if let Some(position) = command.position() {
121 ensure!(!self.positions.contains_key(position), "Cannot redefine position '{position}'");
123 ensure!(self.positions.len() < N::MAX_POSITIONS, "Cannot add more than {} positions", N::MAX_POSITIONS);
125 self.positions.insert(*position, self.commands.len());
127 }
128
129 if command.is_write() {
131 self.num_writes += 1;
133 }
134
135 self.commands.push(command);
137 Ok(())
138 }
139}
140
141impl<N: Network> TypeName for ConstructorCore<N> {
142 #[inline]
144 fn type_name() -> &'static str {
145 "constructor"
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 use crate::{Command, Constructor};
154
155 type CurrentNetwork = console::network::MainnetV0;
156
157 #[test]
158 fn test_add_command() {
159 let mut constructor = Constructor::<CurrentNetwork> {
161 commands: Default::default(),
162 num_writes: 0,
163 positions: Default::default(),
164 };
165
166 let command = Command::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
168 assert!(constructor.add_command(command).is_ok());
169
170 for i in 3..CurrentNetwork::MAX_COMMANDS * 2 {
172 let command = Command::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
173
174 match constructor.commands.len() < CurrentNetwork::MAX_COMMANDS {
175 true => assert!(constructor.add_command(command).is_ok()),
176 false => assert!(constructor.add_command(command).is_err()),
177 }
178 }
179
180 let mut constructor = Constructor::<CurrentNetwork> {
184 commands: Default::default(),
185 num_writes: 0,
186 positions: Default::default(),
187 };
188
189 for _ in 0..CurrentNetwork::LATEST_MAX_WRITES() * 2 {
190 let command = Command::<CurrentNetwork>::from_str("remove object[r0];").unwrap();
191
192 match constructor.commands.len() < CurrentNetwork::LATEST_MAX_WRITES() as usize {
193 true => assert!(constructor.add_command(command).is_ok()),
194 false => assert!(constructor.add_command(command).is_err()),
195 }
196 }
197 }
198
199 #[test]
200 fn test_add_command_duplicate_positions() {
201 let mut constructor =
203 Constructor { commands: Default::default(), num_writes: 0, positions: Default::default() };
204
205 let command = Command::<CurrentNetwork>::from_str("position start;").unwrap();
207 assert!(constructor.add_command(command.clone()).is_ok());
208
209 assert!(constructor.add_command(command).is_err());
211
212 #[allow(clippy::cast_possible_truncation)]
214 fn to_unique_string(mut n: usize) -> String {
215 let mut s = String::new();
216 while n > 0 {
217 s.push((b'A' + (n % 26) as u8) as char);
218 n /= 26;
219 }
220 s.chars().rev().collect::<String>()
221 }
222
223 for i in 1..u8::MAX as usize * 2 {
225 let position = to_unique_string(i);
226 println!("position: {position}");
227 let command = Command::<CurrentNetwork>::from_str(&format!("position {position};")).unwrap();
228
229 match constructor.commands.len() < u8::MAX as usize {
230 true => assert!(constructor.add_command(command).is_ok()),
231 false => assert!(constructor.add_command(command).is_err()),
232 }
233 }
234 }
235}