1#![warn(missing_docs)]
2
3use nargo_types::{NargoValue, Result, Span};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11use crate::stmt::JsStmt;
12use crate::template::{CustomBlockIR, StyleIR, TemplateIR, TestIR};
13use crate::types::{MAX_ARRAY_LENGTH, MAX_OBJECT_SIZE, MAX_STRING_LENGTH, IRError, Trivia};
14
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
19pub struct JsProgram {
20 pub body: Vec<JsStmt>,
24 #[serde(default)]
28 pub span: Span,
29 #[serde(default)]
33 pub trivia: Trivia,
34}
35
36impl JsProgram {
37 pub fn validate(&self) -> Result<()> {
45 if self.body.len() > MAX_ARRAY_LENGTH {
46 return Err(IRError::SizeLimitExceeded("Program body length exceeded".to_string()).into());
47 }
48 for stmt in &self.body {
49 stmt.validate(0)?;
50 }
51 Ok(())
52 }
53
54 pub fn optimize(&mut self) {
56 crate::optimizer::ProgramOptimizer::optimize(self);
57 }
58
59 pub fn is_empty(&self) -> bool {
61 self.body.is_empty()
62 }
63}
64
65#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
69pub struct IRModule {
70 pub name: String,
74 pub metadata: HashMap<String, NargoValue>,
78 pub script: Option<JsProgram>,
82 pub script_server: Option<JsProgram>,
86 pub script_client: Option<JsProgram>,
90 pub script_meta: Option<NargoValue>,
94 pub template: Option<TemplateIR>,
98 pub hoisted_nodes: HashMap<String, crate::template::TemplateNodeIR>,
102 pub styles: Vec<StyleIR>,
106 pub i18n: Option<HashMap<String, HashMap<String, String>>>,
110 pub wasm: Vec<Vec<u8>>,
114 pub custom_blocks: Vec<CustomBlockIR>,
118 pub tests: Vec<TestIR>,
122 pub span: Span,
126}
127
128impl IRModule {
129 pub fn validate(&self) -> Result<()> {
131 if self.name.len() > MAX_STRING_LENGTH {
132 return Err(IRError::SizeLimitExceeded("Module name length exceeded".to_string()).into());
133 }
134
135 if self.metadata.len() > MAX_OBJECT_SIZE {
137 return Err(IRError::SizeLimitExceeded("Metadata size exceeded".to_string()).into());
138 }
139 for (key, value) in &self.metadata {
140 if key.len() > MAX_STRING_LENGTH {
141 return Err(IRError::SizeLimitExceeded("Metadata key length exceeded".to_string()).into());
142 }
143 value.validate(0)?;
144 }
145
146 if let Some(script) = &self.script {
148 script.validate()?;
149 }
150 if let Some(script_server) = &self.script_server {
151 script_server.validate()?;
152 }
153 if let Some(script_client) = &self.script_client {
154 script_client.validate()?;
155 }
156
157 if let Some(meta) = &self.script_meta {
159 meta.validate(0)?;
160 }
161
162 if let Some(template) = &self.template {
164 template.validate()?;
165 }
166
167 if self.hoisted_nodes.len() > MAX_OBJECT_SIZE {
169 return Err(IRError::SizeLimitExceeded("Hoisted nodes size exceeded".to_string()).into());
170 }
171 for (key, node) in &self.hoisted_nodes {
172 if key.len() > MAX_STRING_LENGTH {
173 return Err(IRError::SizeLimitExceeded("Hoisted node key length exceeded".to_string()).into());
174 }
175 node.validate(0)?;
176 }
177
178 if self.styles.len() > MAX_ARRAY_LENGTH {
180 return Err(IRError::SizeLimitExceeded("Styles length exceeded".to_string()).into());
181 }
182 for style in &self.styles {
183 style.validate()?;
184 }
185
186 if let Some(i18n) = &self.i18n {
188 if i18n.len() > MAX_OBJECT_SIZE {
189 return Err(IRError::SizeLimitExceeded("I18n size exceeded".to_string()).into());
190 }
191 for (lang, translations) in i18n {
192 if lang.len() > MAX_STRING_LENGTH {
193 return Err(IRError::SizeLimitExceeded("I18n language length exceeded".to_string()).into());
194 }
195 if translations.len() > MAX_OBJECT_SIZE {
196 return Err(IRError::SizeLimitExceeded("I18n translations size exceeded".to_string()).into());
197 }
198 for (key, value) in translations {
199 if key.len() > MAX_STRING_LENGTH {
200 return Err(IRError::SizeLimitExceeded("I18n key length exceeded".to_string()).into());
201 }
202 if value.len() > MAX_STRING_LENGTH {
203 return Err(IRError::SizeLimitExceeded("I18n value length exceeded".to_string()).into());
204 }
205 }
206 }
207 }
208
209 if self.wasm.len() > MAX_ARRAY_LENGTH {
211 return Err(IRError::SizeLimitExceeded("WASM length exceeded".to_string()).into());
212 }
213 for wasm in &self.wasm {
214 if wasm.len() > MAX_STRING_LENGTH {
215 return Err(IRError::SizeLimitExceeded("WASM size exceeded".to_string()).into());
216 }
217 }
218
219 if self.custom_blocks.len() > MAX_ARRAY_LENGTH {
221 return Err(IRError::SizeLimitExceeded("Custom blocks length exceeded".to_string()).into());
222 }
223 for block in &self.custom_blocks {
224 block.validate()?;
225 }
226
227 if self.tests.len() > MAX_ARRAY_LENGTH {
229 return Err(IRError::SizeLimitExceeded("Tests length exceeded".to_string()).into());
230 }
231 for test in &self.tests {
232 test.validate()?;
233 }
234
235 Ok(())
236 }
237
238 pub fn cleanup(&mut self) {
240 if let Some(script) = &mut self.script {
242 if script.body.is_empty() {
243 self.script = None;
244 }
245 }
246 if let Some(script_server) = &mut self.script_server {
247 if script_server.body.is_empty() {
248 self.script_server = None;
249 }
250 }
251 if let Some(script_client) = &mut self.script_client {
252 if script_client.body.is_empty() {
253 self.script_client = None;
254 }
255 }
256
257 if let Some(meta) = &self.script_meta {
259 if *meta == NargoValue::Null {
260 self.script_meta = None;
261 }
262 }
263
264 if let Some(template) = &mut self.template {
266 if template.nodes.is_empty() {
267 self.template = None;
268 }
269 }
270
271 if let Some(i18n) = &self.i18n {
273 if i18n.is_empty() {
274 self.i18n = None;
275 }
276 }
277 }
278
279 pub fn optimize(&mut self) {
281 crate::optimizer::IROptimizer::optimize(self);
282 }
283}