1#![warn(missing_docs)]
2
3use nargo_types::{Error, NargoValue, Result, Span};
15use serde::{Deserialize, Serialize};
16use std::{collections::HashMap, fmt};
17
18#[derive(Debug, Clone, PartialEq)]
22pub enum IRError {
23 InvalidInput(String),
27 InconsistentStructure(String),
31 SizeLimitExceeded(String),
35 CircularReference(String),
39 InvalidExpression(String),
43 Other(String),
47}
48
49impl fmt::Display for IRError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 IRError::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
53 IRError::InconsistentStructure(msg) => write!(f, "Inconsistent structure: {}", msg),
54 IRError::SizeLimitExceeded(msg) => write!(f, "Size limit exceeded: {}", msg),
55 IRError::CircularReference(msg) => write!(f, "Circular reference: {}", msg),
56 IRError::InvalidExpression(msg) => write!(f, "Invalid expression: {}", msg),
57 IRError::Other(msg) => write!(f, "{}", msg),
58 }
59 }
60}
61
62impl From<IRError> for Error {
63 fn from(err: IRError) -> Self {
64 Error::external_error("nargo-ir".to_string(), err.to_string(), Span::unknown())
65 }
66}
67
68const MAX_STRING_LENGTH: usize = 1024 * 1024; const MAX_ARRAY_LENGTH: usize = 10000;
71const MAX_OBJECT_SIZE: usize = 1000;
72const MAX_RECURSION_DEPTH: usize = 100;
73
74#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
76pub struct Trivia {
77 pub leading_whitespace: String,
79 pub leading_comments: Vec<Comment>,
81 pub trailing_comments: Vec<Comment>,
83}
84
85impl Trivia {
86 pub fn new() -> Self {
88 Self::default()
89 }
90
91 pub fn is_empty(&self) -> bool {
93 self.leading_whitespace.is_empty() && self.leading_comments.is_empty() && self.trailing_comments.is_empty()
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
99pub struct Comment {
100 pub content: String,
102 pub is_block: bool,
104 pub span: Span,
106}
107
108impl Comment {
109 pub fn new(content: String, is_block: bool, span: Span) -> Self {
111 Self { content, is_block, span }
112 }
113
114 pub fn is_empty(&self) -> bool {
116 self.content.trim().is_empty()
117 }
118}
119
120#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
122pub enum JsExpr {
123 Identifier(String, #[serde(default)] Span, #[serde(default)] Trivia),
125 Literal(NargoValue, #[serde(default)] Span, #[serde(default)] Trivia),
127 Unary {
129 op: String,
131 argument: Box<JsExpr>,
133 #[serde(default)]
135 span: Span,
136 #[serde(default)]
138 trivia: Trivia,
139 },
140 Binary {
142 left: Box<JsExpr>,
144 op: String,
146 right: Box<JsExpr>,
148 #[serde(default)]
150 span: Span,
151 #[serde(default)]
153 trivia: Trivia,
154 },
155 Call {
157 callee: Box<JsExpr>,
159 args: Vec<JsExpr>,
161 #[serde(default)]
163 span: Span,
164 #[serde(default)]
166 trivia: Trivia,
167 },
168 Member {
170 object: Box<JsExpr>,
172 property: Box<JsExpr>,
174 computed: bool,
176 #[serde(default)]
178 span: Span,
179 #[serde(default)]
181 trivia: Trivia,
182 },
183 OptionalMember {
185 object: Box<JsExpr>,
187 property: Box<JsExpr>,
189 computed: bool,
191 #[serde(default)]
193 span: Span,
194 #[serde(default)]
196 trivia: Trivia,
197 },
198 OptionalCall {
200 callee: Box<JsExpr>,
202 args: Vec<JsExpr>,
204 #[serde(default)]
206 span: Span,
207 #[serde(default)]
209 trivia: Trivia,
210 },
211 NullishCoalescing {
213 left: Box<JsExpr>,
215 right: Box<JsExpr>,
217 #[serde(default)]
219 span: Span,
220 #[serde(default)]
222 trivia: Trivia,
223 },
224 LogicalAssignment {
226 op: String,
228 left: Box<JsExpr>,
230 right: Box<JsExpr>,
232 #[serde(default)]
234 span: Span,
235 #[serde(default)]
237 trivia: Trivia,
238 },
239 Array(Vec<JsExpr>, #[serde(default)] Span, #[serde(default)] Trivia),
241 Object(HashMap<String, JsExpr>, #[serde(default)] Span, #[serde(default)] Trivia),
243 ArrowFunction {
245 params: Vec<String>,
247 body: Box<JsExpr>,
249 #[serde(default)]
251 span: Span,
252 #[serde(default)]
254 trivia: Trivia,
255 },
256 TseElement {
258 tag: String,
260 attributes: Vec<TseAttribute>,
262 children: Vec<JsExpr>,
264 #[serde(default)]
266 span: Span,
267 #[serde(default)]
269 trivia: Trivia,
270 },
271 Conditional {
273 test: Box<JsExpr>,
275 consequent: Box<JsExpr>,
277 alternate: Box<JsExpr>,
279 #[serde(default)]
281 span: Span,
282 #[serde(default)]
284 trivia: Trivia,
285 },
286 TemplateLiteral {
288 quasis: Vec<String>,
290 expressions: Vec<JsExpr>,
292 #[serde(default)]
294 span: Span,
295 #[serde(default)]
297 trivia: Trivia,
298 },
299 Spread(Box<JsExpr>, #[serde(default)] Span, #[serde(default)] Trivia),
301 TypeOf(Box<JsExpr>, #[serde(default)] Span, #[serde(default)] Trivia),
303 InstanceOf {
305 left: Box<JsExpr>,
307 right: Box<JsExpr>,
309 #[serde(default)]
311 span: Span,
312 #[serde(default)]
314 trivia: Trivia,
315 },
316 Other(String, #[serde(default)] Span, #[serde(default)] Trivia),
318}
319
320impl JsExpr {
321 pub fn span(&self) -> Span {
323 match self {
324 JsExpr::Identifier(_, span, _) => *span,
325 JsExpr::Literal(_, span, _) => *span,
326 JsExpr::Unary { span, .. } => *span,
327 JsExpr::Binary { span, .. } => *span,
328 JsExpr::Call { span, .. } => *span,
329 JsExpr::Member { span, .. } => *span,
330 JsExpr::OptionalMember { span, .. } => *span,
331 JsExpr::OptionalCall { span, .. } => *span,
332 JsExpr::NullishCoalescing { span, .. } => *span,
333 JsExpr::LogicalAssignment { span, .. } => *span,
334 JsExpr::Array(_, span, _) => *span,
335 JsExpr::Object(_, span, _) => *span,
336 JsExpr::ArrowFunction { span, .. } => *span,
337 JsExpr::TseElement { span, .. } => *span,
338 JsExpr::Conditional { span, .. } => *span,
339 JsExpr::TemplateLiteral { span, .. } => *span,
340 JsExpr::Spread(_, span, _) => *span,
341 JsExpr::TypeOf(_, span, _) => *span,
342 JsExpr::InstanceOf { span, .. } => *span,
343 JsExpr::Other(_, span, _) => *span,
344 }
345 }
346
347 pub fn trivia(&self) -> &Trivia {
349 match self {
350 JsExpr::Identifier(_, _, t) => t,
351 JsExpr::Literal(_, _, t) => t,
352 JsExpr::Unary { trivia, .. } => trivia,
353 JsExpr::Binary { trivia, .. } => trivia,
354 JsExpr::Call { trivia, .. } => trivia,
355 JsExpr::Member { trivia, .. } => trivia,
356 JsExpr::OptionalMember { trivia, .. } => trivia,
357 JsExpr::OptionalCall { trivia, .. } => trivia,
358 JsExpr::NullishCoalescing { trivia, .. } => trivia,
359 JsExpr::LogicalAssignment { trivia, .. } => trivia,
360 JsExpr::Array(_, _, t) => t,
361 JsExpr::Object(_, _, t) => t,
362 JsExpr::ArrowFunction { trivia, .. } => trivia,
363 JsExpr::TseElement { trivia, .. } => trivia,
364 JsExpr::Conditional { trivia, .. } => trivia,
365 JsExpr::TemplateLiteral { trivia, .. } => trivia,
366 JsExpr::Spread(_, _, t) => t,
367 JsExpr::TypeOf(_, _, t) => t,
368 JsExpr::InstanceOf { trivia, .. } => trivia,
369 JsExpr::Other(_, _, t) => t,
370 }
371 }
372
373 pub fn validate(&self, depth: usize) -> Result<()> {
375 if depth > MAX_RECURSION_DEPTH {
376 return Err(IRError::CircularReference("Expression recursion depth exceeded".to_string()).into());
377 }
378
379 match self {
380 JsExpr::Identifier(id, _, _) => {
381 if id.len() > MAX_STRING_LENGTH {
382 return Err(IRError::SizeLimitExceeded("Identifier length exceeded".to_string()).into());
383 }
384 Ok(())
385 }
386 JsExpr::Literal(value, _, _) => value.validate(depth + 1),
387 JsExpr::Unary { argument, .. } => argument.validate(depth + 1),
388 JsExpr::Binary { left, right, .. } => {
389 left.validate(depth + 1)?;
390 right.validate(depth + 1)
391 }
392 JsExpr::Call { callee, args, .. } => {
393 callee.validate(depth + 1)?;
394 if args.len() > MAX_ARRAY_LENGTH {
395 return Err(IRError::SizeLimitExceeded("Call arguments length exceeded".to_string()).into());
396 }
397 for arg in args {
398 arg.validate(depth + 1)?;
399 }
400 Ok(())
401 }
402 JsExpr::Member { object, property, .. } => {
403 object.validate(depth + 1)?;
404 property.validate(depth + 1)
405 }
406 JsExpr::OptionalMember { object, property, .. } => {
407 object.validate(depth + 1)?;
408 property.validate(depth + 1)
409 }
410 JsExpr::OptionalCall { callee, args, .. } => {
411 callee.validate(depth + 1)?;
412 if args.len() > MAX_ARRAY_LENGTH {
413 return Err(IRError::SizeLimitExceeded("Optional call arguments length exceeded".to_string()).into());
414 }
415 for arg in args {
416 arg.validate(depth + 1)?;
417 }
418 Ok(())
419 }
420 JsExpr::NullishCoalescing { left, right, .. } => {
421 left.validate(depth + 1)?;
422 right.validate(depth + 1)
423 }
424 JsExpr::LogicalAssignment { op, left, right, .. } => {
425 if op.len() > MAX_STRING_LENGTH {
426 return Err(IRError::SizeLimitExceeded("Logical assignment operator length exceeded".to_string()).into());
427 }
428 left.validate(depth + 1)?;
429 right.validate(depth + 1)
430 }
431 JsExpr::Array(items, _, _) => {
432 if items.len() > MAX_ARRAY_LENGTH {
433 return Err(IRError::SizeLimitExceeded("Array length exceeded".to_string()).into());
434 }
435 for item in items {
436 item.validate(depth + 1)?;
437 }
438 Ok(())
439 }
440 JsExpr::Object(props, _, _) => {
441 if props.len() > MAX_OBJECT_SIZE {
442 return Err(IRError::SizeLimitExceeded("Object size exceeded".to_string()).into());
443 }
444 for (key, value) in props {
445 if key.len() > MAX_STRING_LENGTH {
446 return Err(IRError::SizeLimitExceeded("Object key length exceeded".to_string()).into());
447 }
448 value.validate(depth + 1)?;
449 }
450 Ok(())
451 }
452 JsExpr::ArrowFunction { params, body, .. } => {
453 if params.len() > MAX_ARRAY_LENGTH {
454 return Err(IRError::SizeLimitExceeded("Arrow function parameters length exceeded".to_string()).into());
455 }
456 for param in params {
457 if param.len() > MAX_STRING_LENGTH {
458 return Err(IRError::SizeLimitExceeded("Parameter name length exceeded".to_string()).into());
459 }
460 }
461 body.validate(depth + 1)
462 }
463 JsExpr::TseElement { tag, attributes, children, .. } => {
464 if tag.len() > MAX_STRING_LENGTH {
465 return Err(IRError::SizeLimitExceeded("TSE element tag length exceeded".to_string()).into());
466 }
467 if attributes.len() > MAX_ARRAY_LENGTH {
468 return Err(IRError::SizeLimitExceeded("TSE element attributes length exceeded".to_string()).into());
469 }
470 for attr in attributes {
471 attr.validate(depth + 1)?;
472 }
473 if children.len() > MAX_ARRAY_LENGTH {
474 return Err(IRError::SizeLimitExceeded("TSE element children length exceeded".to_string()).into());
475 }
476 for child in children {
477 child.validate(depth + 1)?;
478 }
479 Ok(())
480 }
481 JsExpr::Conditional { test, consequent, alternate, .. } => {
482 test.validate(depth + 1)?;
483 consequent.validate(depth + 1)?;
484 alternate.validate(depth + 1)
485 }
486 JsExpr::TemplateLiteral { quasis, expressions, .. } => {
487 if quasis.len() > MAX_ARRAY_LENGTH {
488 return Err(IRError::SizeLimitExceeded("Template literal quasis length exceeded".to_string()).into());
489 }
490 for quasi in quasis {
491 if quasi.len() > MAX_STRING_LENGTH {
492 return Err(IRError::SizeLimitExceeded("Template literal quasi length exceeded".to_string()).into());
493 }
494 }
495 if expressions.len() > MAX_ARRAY_LENGTH {
496 return Err(IRError::SizeLimitExceeded("Template literal expressions length exceeded".to_string()).into());
497 }
498 for expr in expressions {
499 expr.validate(depth + 1)?;
500 }
501 Ok(())
502 }
503 JsExpr::Spread(expr, _, _) => expr.validate(depth + 1),
504 JsExpr::TypeOf(expr, _, _) => expr.validate(depth + 1),
505 JsExpr::InstanceOf { left, right, .. } => {
506 left.validate(depth + 1)?;
507 right.validate(depth + 1)
508 }
509 JsExpr::Other(code, _, _) => {
510 if code.len() > MAX_STRING_LENGTH {
511 return Err(IRError::SizeLimitExceeded("Other expression code length exceeded".to_string()).into());
512 }
513 Ok(())
514 }
515 }
516 }
517
518 pub fn optimize(&mut self) {
520 optimizer::ExprOptimizer::optimize(self);
521 }
522
523 pub fn is_constant(&self) -> bool {
525 match self {
526 JsExpr::Literal(_, _, _) => true,
527 JsExpr::Unary { argument, .. } => argument.is_constant(),
528 JsExpr::Binary { left, right, .. } => left.is_constant() && right.is_constant(),
529 _ => false,
530 }
531 }
532}
533
534#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
536pub struct TseAttribute {
537 pub name: String,
539 pub value: Option<JsExpr>,
541 pub is_directive: bool,
543 #[serde(default)]
545 pub span: Span,
546 #[serde(default)]
548 pub trivia: Trivia,
549}
550
551impl TseAttribute {
552 pub fn validate(&self, depth: usize) -> Result<()> {
554 if self.name.len() > MAX_STRING_LENGTH {
555 return Err(IRError::SizeLimitExceeded("TSE attribute name length exceeded".to_string()).into());
556 }
557 if let Some(value) = &self.value {
558 value.validate(depth + 1)?;
559 }
560 Ok(())
561 }
562}
563
564#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
566pub enum JsStmt {
567 Expr(JsExpr, #[serde(default)] Span, #[serde(default)] Trivia),
569 VariableDecl {
571 kind: String,
573 id: String,
575 init: Option<JsExpr>,
577 #[serde(default)]
579 span: Span,
580 #[serde(default)]
582 trivia: Trivia,
583 },
584 Import {
586 source: String,
588 specifiers: Vec<String>,
590 #[serde(default)]
592 span: Span,
593 #[serde(default)]
595 trivia: Trivia,
596 },
597 Export {
599 declaration: Box<JsStmt>,
601 #[serde(default)]
603 span: Span,
604 #[serde(default)]
606 trivia: Trivia,
607 },
608 ExportAll {
610 source: String,
612 #[serde(default)]
614 span: Span,
615 #[serde(default)]
617 trivia: Trivia,
618 },
619 ExportNamed {
621 source: Option<String>,
623 specifiers: Vec<String>,
625 #[serde(default)]
627 span: Span,
628 #[serde(default)]
630 trivia: Trivia,
631 },
632 FunctionDecl {
634 id: String,
636 params: Vec<String>,
638 body: Vec<JsStmt>,
640 #[serde(default)]
642 is_async: bool,
643 #[serde(default)]
645 span: Span,
646 #[serde(default)]
648 trivia: Trivia,
649 },
650 Return(Option<JsExpr>, #[serde(default)] Span, #[serde(default)] Trivia),
652 If {
654 test: JsExpr,
656 consequent: Box<JsStmt>,
658 alternate: Option<Box<JsStmt>>,
660 #[serde(default)]
662 span: Span,
663 #[serde(default)]
665 trivia: Trivia,
666 },
667 While {
669 test: JsExpr,
671 body: Box<JsStmt>,
673 #[serde(default)]
675 span: Span,
676 #[serde(default)]
678 trivia: Trivia,
679 },
680 For {
682 init: Option<Box<JsStmt>>,
684 test: Option<JsExpr>,
686 update: Option<JsExpr>,
688 body: Box<JsStmt>,
690 #[serde(default)]
692 span: Span,
693 #[serde(default)]
695 trivia: Trivia,
696 },
697 ForIn {
699 left: Box<JsStmt>,
701 right: JsExpr,
703 body: Box<JsStmt>,
705 #[serde(default)]
707 span: Span,
708 #[serde(default)]
710 trivia: Trivia,
711 },
712 ForOf {
714 left: Box<JsStmt>,
716 right: JsExpr,
718 body: Box<JsStmt>,
720 #[serde(default)]
722 span: Span,
723 #[serde(default)]
725 trivia: Trivia,
726 },
727 Try {
729 block: Box<JsStmt>,
731 handler: Option<(String, Box<JsStmt>)>,
733 finalizer: Option<Box<JsStmt>>,
735 #[serde(default)]
737 span: Span,
738 #[serde(default)]
740 trivia: Trivia,
741 },
742 Switch {
744 discriminant: JsExpr,
746 cases: Vec<(Option<JsExpr>, Vec<JsStmt>)>,
748 #[serde(default)]
750 span: Span,
751 #[serde(default)]
753 trivia: Trivia,
754 },
755 Throw(JsExpr, #[serde(default)] Span, #[serde(default)] Trivia),
757 Block(Vec<JsStmt>, #[serde(default)] Span, #[serde(default)] Trivia),
759 Break(#[serde(default)] Span, #[serde(default)] Trivia),
761 Continue(#[serde(default)] Span, #[serde(default)] Trivia),
763 Other(String, #[serde(default)] Span, #[serde(default)] Trivia),
765}
766
767impl JsStmt {
768 pub fn span(&self) -> Span {
770 match self {
771 JsStmt::Expr(_, span, _) => *span,
772 JsStmt::VariableDecl { span, .. } => *span,
773 JsStmt::Import { span, .. } => *span,
774 JsStmt::Export { span, .. } => *span,
775 JsStmt::ExportAll { span, .. } => *span,
776 JsStmt::ExportNamed { span, .. } => *span,
777 JsStmt::FunctionDecl { span, .. } => *span,
778 JsStmt::Return(_, span, _) => *span,
779 JsStmt::If { span, .. } => *span,
780 JsStmt::While { span, .. } => *span,
781 JsStmt::For { span, .. } => *span,
782 JsStmt::ForIn { span, .. } => *span,
783 JsStmt::ForOf { span, .. } => *span,
784 JsStmt::Try { span, .. } => *span,
785 JsStmt::Switch { span, .. } => *span,
786 JsStmt::Throw(_, span, _) => *span,
787 JsStmt::Block(_, span, _) => *span,
788 JsStmt::Break(span, _) => *span,
789 JsStmt::Continue(span, _) => *span,
790 JsStmt::Other(_, span, _) => *span,
791 }
792 }
793
794 pub fn trivia(&self) -> &Trivia {
796 match self {
797 JsStmt::Expr(_, _, t) => t,
798 JsStmt::VariableDecl { trivia, .. } => trivia,
799 JsStmt::Import { trivia, .. } => trivia,
800 JsStmt::Export { trivia, .. } => trivia,
801 JsStmt::ExportAll { trivia, .. } => trivia,
802 JsStmt::ExportNamed { trivia, .. } => trivia,
803 JsStmt::FunctionDecl { trivia, .. } => trivia,
804 JsStmt::Return(_, _, t) => t,
805 JsStmt::If { trivia, .. } => trivia,
806 JsStmt::While { trivia, .. } => trivia,
807 JsStmt::For { trivia, .. } => trivia,
808 JsStmt::ForIn { trivia, .. } => trivia,
809 JsStmt::ForOf { trivia, .. } => trivia,
810 JsStmt::Try { trivia, .. } => trivia,
811 JsStmt::Switch { trivia, .. } => trivia,
812 JsStmt::Throw(_, _, t) => t,
813 JsStmt::Block(_, _, t) => t,
814 JsStmt::Break(_, t) => t,
815 JsStmt::Continue(_, t) => t,
816 JsStmt::Other(_, _, t) => t,
817 }
818 }
819
820 pub fn validate(&self, depth: usize) -> Result<()> {
822 if depth > MAX_RECURSION_DEPTH {
823 return Err(IRError::CircularReference("Statement recursion depth exceeded".to_string()).into());
824 }
825
826 match self {
827 JsStmt::Expr(expr, _, _) => expr.validate(depth + 1),
828 JsStmt::VariableDecl { id, init, .. } => {
829 if id.len() > MAX_STRING_LENGTH {
830 return Err(IRError::SizeLimitExceeded("Variable name length exceeded".to_string()).into());
831 }
832 if let Some(init_expr) = init {
833 init_expr.validate(depth + 1)?;
834 }
835 Ok(())
836 }
837 JsStmt::Import { source, specifiers, .. } => {
838 if source.len() > MAX_STRING_LENGTH {
839 return Err(IRError::SizeLimitExceeded("Import source length exceeded".to_string()).into());
840 }
841 if specifiers.len() > MAX_ARRAY_LENGTH {
842 return Err(IRError::SizeLimitExceeded("Import specifiers length exceeded".to_string()).into());
843 }
844 for specifier in specifiers {
845 if specifier.len() > MAX_STRING_LENGTH {
846 return Err(IRError::SizeLimitExceeded("Import specifier length exceeded".to_string()).into());
847 }
848 }
849 Ok(())
850 }
851 JsStmt::Export { declaration, .. } => declaration.validate(depth + 1),
852 JsStmt::ExportAll { source, .. } => {
853 if source.len() > MAX_STRING_LENGTH {
854 return Err(IRError::SizeLimitExceeded("Export all source length exceeded".to_string()).into());
855 }
856 Ok(())
857 }
858 JsStmt::ExportNamed { source, specifiers, .. } => {
859 if let Some(source_str) = source {
860 if source_str.len() > MAX_STRING_LENGTH {
861 return Err(IRError::SizeLimitExceeded("Export named source length exceeded".to_string()).into());
862 }
863 }
864 if specifiers.len() > MAX_ARRAY_LENGTH {
865 return Err(IRError::SizeLimitExceeded("Export named specifiers length exceeded".to_string()).into());
866 }
867 for specifier in specifiers {
868 if specifier.len() > MAX_STRING_LENGTH {
869 return Err(IRError::SizeLimitExceeded("Export specifier length exceeded".to_string()).into());
870 }
871 }
872 Ok(())
873 }
874 JsStmt::FunctionDecl { id, params, body, .. } => {
875 if id.len() > MAX_STRING_LENGTH {
876 return Err(IRError::SizeLimitExceeded("Function name length exceeded".to_string()).into());
877 }
878 if params.len() > MAX_ARRAY_LENGTH {
879 return Err(IRError::SizeLimitExceeded("Function parameters length exceeded".to_string()).into());
880 }
881 for param in params {
882 if param.len() > MAX_STRING_LENGTH {
883 return Err(IRError::SizeLimitExceeded("Parameter name length exceeded".to_string()).into());
884 }
885 }
886 if body.len() > MAX_ARRAY_LENGTH {
887 return Err(IRError::SizeLimitExceeded("Function body length exceeded".to_string()).into());
888 }
889 for stmt in body {
890 stmt.validate(depth + 1)?;
891 }
892 Ok(())
893 }
894 JsStmt::Return(expr, _, _) => {
895 if let Some(expr) = expr {
896 expr.validate(depth + 1)?;
897 }
898 Ok(())
899 }
900 JsStmt::If { test, consequent, alternate, .. } => {
901 test.validate(depth + 1)?;
902 consequent.validate(depth + 1)?;
903 if let Some(alt) = alternate {
904 alt.validate(depth + 1)?;
905 }
906 Ok(())
907 }
908 JsStmt::While { test, body, .. } => {
909 test.validate(depth + 1)?;
910 body.validate(depth + 1)
911 }
912 JsStmt::For { init, test, update, body, .. } => {
913 if let Some(init_stmt) = init {
914 init_stmt.validate(depth + 1)?;
915 }
916 if let Some(test_expr) = test {
917 test_expr.validate(depth + 1)?;
918 }
919 if let Some(update_expr) = update {
920 update_expr.validate(depth + 1)?;
921 }
922 body.validate(depth + 1)
923 }
924 JsStmt::ForIn { left, right, body, .. } => {
925 left.validate(depth + 1)?;
926 right.validate(depth + 1)?;
927 body.validate(depth + 1)
928 }
929 JsStmt::ForOf { left, right, body, .. } => {
930 left.validate(depth + 1)?;
931 right.validate(depth + 1)?;
932 body.validate(depth + 1)
933 }
934 JsStmt::Try { block, handler, finalizer, .. } => {
935 block.validate(depth + 1)?;
936 if let Some((catch_id, catch_body)) = handler {
937 if catch_id.len() > MAX_STRING_LENGTH {
938 return Err(IRError::SizeLimitExceeded("Catch parameter name length exceeded".to_string()).into());
939 }
940 catch_body.validate(depth + 1)?;
941 }
942 if let Some(finally_body) = finalizer {
943 finally_body.validate(depth + 1)?;
944 }
945 Ok(())
946 }
947 JsStmt::Switch { discriminant, cases, .. } => {
948 discriminant.validate(depth + 1)?;
949 if cases.len() > MAX_ARRAY_LENGTH {
950 return Err(IRError::SizeLimitExceeded("Switch cases length exceeded".to_string()).into());
951 }
952 for (test, stmts) in cases {
953 if let Some(test_expr) = test {
954 test_expr.validate(depth + 1)?;
955 }
956 if stmts.len() > MAX_ARRAY_LENGTH {
957 return Err(IRError::SizeLimitExceeded("Switch case statements length exceeded".to_string()).into());
958 }
959 for stmt in stmts {
960 stmt.validate(depth + 1)?;
961 }
962 }
963 Ok(())
964 }
965 JsStmt::Throw(expr, _, _) => expr.validate(depth + 1),
966 JsStmt::Block(stmts, _, _) => {
967 if stmts.len() > MAX_ARRAY_LENGTH {
968 return Err(IRError::SizeLimitExceeded("Block statements length exceeded".to_string()).into());
969 }
970 for stmt in stmts {
971 stmt.validate(depth + 1)?;
972 }
973 Ok(())
974 }
975 JsStmt::Break(_, _) => Ok(()),
976 JsStmt::Continue(_, _) => Ok(()),
977 JsStmt::Other(code, _, _) => {
978 if code.len() > MAX_STRING_LENGTH {
979 return Err(IRError::SizeLimitExceeded("Other statement code length exceeded".to_string()).into());
980 }
981 Ok(())
982 }
983 }
984 }
985
986 pub fn optimize(&mut self) {
988 optimizer::StmtOptimizer::optimize(self);
989 }
990
991 pub fn is_empty(&self) -> bool {
993 match self {
994 JsStmt::Block(stmts, _, _) => stmts.is_empty(),
995 _ => false,
996 }
997 }
998}
999
1000#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1004pub struct JsProgram {
1005 pub body: Vec<JsStmt>,
1009 #[serde(default)]
1013 pub span: Span,
1014 #[serde(default)]
1018 pub trivia: Trivia,
1019}
1020
1021impl JsProgram {
1022 pub fn validate(&self) -> Result<()> {
1030 if self.body.len() > MAX_ARRAY_LENGTH {
1031 return Err(IRError::SizeLimitExceeded("Program body length exceeded".to_string()).into());
1032 }
1033 for stmt in &self.body {
1034 stmt.validate(0)?;
1035 }
1036 Ok(())
1037 }
1038
1039 pub fn optimize(&mut self) {
1041 optimizer::ProgramOptimizer::optimize(self);
1042 }
1043
1044 pub fn is_empty(&self) -> bool {
1046 self.body.is_empty()
1047 }
1048}
1049
1050#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1054pub struct IRModule {
1055 pub name: String,
1059 pub metadata: HashMap<String, NargoValue>,
1063 pub script: Option<JsProgram>,
1067 pub script_server: Option<JsProgram>,
1071 pub script_client: Option<JsProgram>,
1075 pub script_meta: Option<NargoValue>,
1079 pub template: Option<TemplateIR>,
1083 pub hoisted_nodes: HashMap<String, TemplateNodeIR>,
1087 pub styles: Vec<StyleIR>,
1091 pub i18n: Option<HashMap<String, HashMap<String, String>>>,
1095 pub wasm: Vec<Vec<u8>>,
1099 pub custom_blocks: Vec<CustomBlockIR>,
1103 pub tests: Vec<TestIR>,
1107 pub span: Span,
1111}
1112
1113impl IRModule {
1114 pub fn validate(&self) -> Result<()> {
1116 if self.name.len() > MAX_STRING_LENGTH {
1117 return Err(IRError::SizeLimitExceeded("Module name length exceeded".to_string()).into());
1118 }
1119
1120 if self.metadata.len() > MAX_OBJECT_SIZE {
1122 return Err(IRError::SizeLimitExceeded("Metadata size exceeded".to_string()).into());
1123 }
1124 for (key, value) in &self.metadata {
1125 if key.len() > MAX_STRING_LENGTH {
1126 return Err(IRError::SizeLimitExceeded("Metadata key length exceeded".to_string()).into());
1127 }
1128 value.validate(0)?;
1129 }
1130
1131 if let Some(script) = &self.script {
1133 script.validate()?;
1134 }
1135 if let Some(script_server) = &self.script_server {
1136 script_server.validate()?;
1137 }
1138 if let Some(script_client) = &self.script_client {
1139 script_client.validate()?;
1140 }
1141
1142 if let Some(meta) = &self.script_meta {
1144 meta.validate(0)?;
1145 }
1146
1147 if let Some(template) = &self.template {
1149 template.validate()?;
1150 }
1151
1152 if self.hoisted_nodes.len() > MAX_OBJECT_SIZE {
1154 return Err(IRError::SizeLimitExceeded("Hoisted nodes size exceeded".to_string()).into());
1155 }
1156 for (key, node) in &self.hoisted_nodes {
1157 if key.len() > MAX_STRING_LENGTH {
1158 return Err(IRError::SizeLimitExceeded("Hoisted node key length exceeded".to_string()).into());
1159 }
1160 node.validate(0)?;
1161 }
1162
1163 if self.styles.len() > MAX_ARRAY_LENGTH {
1165 return Err(IRError::SizeLimitExceeded("Styles length exceeded".to_string()).into());
1166 }
1167 for style in &self.styles {
1168 style.validate()?;
1169 }
1170
1171 if let Some(i18n) = &self.i18n {
1173 if i18n.len() > MAX_OBJECT_SIZE {
1174 return Err(IRError::SizeLimitExceeded("I18n size exceeded".to_string()).into());
1175 }
1176 for (lang, translations) in i18n {
1177 if lang.len() > MAX_STRING_LENGTH {
1178 return Err(IRError::SizeLimitExceeded("I18n language length exceeded".to_string()).into());
1179 }
1180 if translations.len() > MAX_OBJECT_SIZE {
1181 return Err(IRError::SizeLimitExceeded("I18n translations size exceeded".to_string()).into());
1182 }
1183 for (key, value) in translations {
1184 if key.len() > MAX_STRING_LENGTH {
1185 return Err(IRError::SizeLimitExceeded("I18n key length exceeded".to_string()).into());
1186 }
1187 if value.len() > MAX_STRING_LENGTH {
1188 return Err(IRError::SizeLimitExceeded("I18n value length exceeded".to_string()).into());
1189 }
1190 }
1191 }
1192 }
1193
1194 if self.wasm.len() > MAX_ARRAY_LENGTH {
1196 return Err(IRError::SizeLimitExceeded("WASM length exceeded".to_string()).into());
1197 }
1198 for wasm in &self.wasm {
1199 if wasm.len() > MAX_STRING_LENGTH {
1200 return Err(IRError::SizeLimitExceeded("WASM size exceeded".to_string()).into());
1201 }
1202 }
1203
1204 if self.custom_blocks.len() > MAX_ARRAY_LENGTH {
1206 return Err(IRError::SizeLimitExceeded("Custom blocks length exceeded".to_string()).into());
1207 }
1208 for block in &self.custom_blocks {
1209 block.validate()?;
1210 }
1211
1212 if self.tests.len() > MAX_ARRAY_LENGTH {
1214 return Err(IRError::SizeLimitExceeded("Tests length exceeded".to_string()).into());
1215 }
1216 for test in &self.tests {
1217 test.validate()?;
1218 }
1219
1220 Ok(())
1221 }
1222
1223 pub fn cleanup(&mut self) {
1225 if let Some(script) = &mut self.script {
1227 if script.body.is_empty() {
1228 self.script = None;
1229 }
1230 }
1231 if let Some(script_server) = &mut self.script_server {
1232 if script_server.body.is_empty() {
1233 self.script_server = None;
1234 }
1235 }
1236 if let Some(script_client) = &mut self.script_client {
1237 if script_client.body.is_empty() {
1238 self.script_client = None;
1239 }
1240 }
1241
1242 if let Some(meta) = &self.script_meta {
1244 if *meta == NargoValue::Null {
1245 self.script_meta = None;
1246 }
1247 }
1248
1249 if let Some(template) = &mut self.template {
1251 if template.nodes.is_empty() {
1252 self.template = None;
1253 }
1254 }
1255
1256 if let Some(i18n) = &self.i18n {
1258 if i18n.is_empty() {
1259 self.i18n = None;
1260 }
1261 }
1262 }
1263
1264 pub fn optimize(&mut self) {
1266 optimizer::IROptimizer::optimize(self);
1267 }
1268}
1269
1270#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1272pub struct TestIR {
1273 pub name: String,
1275 pub body: JsProgram,
1277 pub span: Span,
1279}
1280
1281impl TestIR {
1282 pub fn validate(&self) -> Result<()> {
1284 if self.name.len() > MAX_STRING_LENGTH {
1285 return Err(IRError::SizeLimitExceeded("Test name length exceeded".to_string()).into());
1286 }
1287 self.body.validate()
1288 }
1289}
1290
1291#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1293pub struct TemplateIR {
1294 pub nodes: Vec<TemplateNodeIR>,
1296 #[serde(default)]
1298 pub span: Span,
1299}
1300
1301impl TemplateIR {
1302 pub fn validate(&self) -> Result<()> {
1304 if self.nodes.len() > MAX_ARRAY_LENGTH {
1305 return Err(IRError::SizeLimitExceeded("Template nodes length exceeded".to_string()).into());
1306 }
1307 for node in &self.nodes {
1308 node.validate(0)?;
1309 }
1310 Ok(())
1311 }
1312}
1313
1314#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1316pub enum TemplateNodeIR {
1317 Element(ElementIR),
1319 If(IfNodeIR),
1321 For(ForNodeIR),
1323 Text(String, #[serde(default)] Span, #[serde(default)] Trivia),
1325 Interpolation(ExpressionIR),
1327 Comment(String, #[serde(default)] Span, #[serde(default)] Trivia),
1329 Hoisted(String),
1331}
1332
1333impl TemplateNodeIR {
1334 pub fn validate(&self, depth: usize) -> Result<()> {
1336 if depth > MAX_RECURSION_DEPTH {
1337 return Err(IRError::CircularReference("Template node recursion depth exceeded".to_string()).into());
1338 }
1339
1340 match self {
1341 TemplateNodeIR::Element(element) => element.validate(depth + 1),
1342 TemplateNodeIR::If(if_node) => if_node.validate(depth + 1),
1343 TemplateNodeIR::For(for_node) => for_node.validate(depth + 1),
1344 TemplateNodeIR::Text(text, _, _) => {
1345 if text.len() > MAX_STRING_LENGTH {
1346 return Err(IRError::SizeLimitExceeded("Text length exceeded".to_string()).into());
1347 }
1348 Ok(())
1349 }
1350 TemplateNodeIR::Interpolation(expr) => expr.validate(depth + 1),
1351 TemplateNodeIR::Comment(comment, _, _) => {
1352 if comment.len() > MAX_STRING_LENGTH {
1353 return Err(IRError::SizeLimitExceeded("Comment length exceeded".to_string()).into());
1354 }
1355 Ok(())
1356 }
1357 TemplateNodeIR::Hoisted(key) => {
1358 if key.len() > MAX_STRING_LENGTH {
1359 return Err(IRError::SizeLimitExceeded("Hoisted key length exceeded".to_string()).into());
1360 }
1361 Ok(())
1362 }
1363 }
1364 }
1365}
1366
1367#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1369pub struct IfNodeIR {
1370 pub condition: ExpressionIR,
1372 pub consequent: Vec<TemplateNodeIR>,
1374 pub alternate: Option<Vec<TemplateNodeIR>>,
1376 pub else_ifs: Vec<(ExpressionIR, Vec<TemplateNodeIR>)>,
1378 #[serde(default)]
1380 pub span: Span,
1381}
1382
1383impl IfNodeIR {
1384 pub fn validate(&self, depth: usize) -> Result<()> {
1386 if depth > MAX_RECURSION_DEPTH {
1387 return Err(IRError::CircularReference("If node recursion depth exceeded".to_string()).into());
1388 }
1389
1390 self.condition.validate(depth + 1)?;
1391
1392 if self.consequent.len() > MAX_ARRAY_LENGTH {
1393 return Err(IRError::SizeLimitExceeded("If node consequent length exceeded".to_string()).into());
1394 }
1395 for node in &self.consequent {
1396 node.validate(depth + 1)?;
1397 }
1398
1399 if let Some(alternate) = &self.alternate {
1400 if alternate.len() > MAX_ARRAY_LENGTH {
1401 return Err(IRError::SizeLimitExceeded("If node alternate length exceeded".to_string()).into());
1402 }
1403 for node in alternate {
1404 node.validate(depth + 1)?;
1405 }
1406 }
1407
1408 if self.else_ifs.len() > MAX_ARRAY_LENGTH {
1409 return Err(IRError::SizeLimitExceeded("If node else ifs length exceeded".to_string()).into());
1410 }
1411 for (condition, body) in &self.else_ifs {
1412 condition.validate(depth + 1)?;
1413 if body.len() > MAX_ARRAY_LENGTH {
1414 return Err(IRError::SizeLimitExceeded("If node else if body length exceeded".to_string()).into());
1415 }
1416 for node in body {
1417 node.validate(depth + 1)?;
1418 }
1419 }
1420
1421 Ok(())
1422 }
1423}
1424
1425#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1427pub struct ForNodeIR {
1428 pub iterator: ForIteratorIR,
1430 pub body: Vec<TemplateNodeIR>,
1432 #[serde(default)]
1434 pub span: Span,
1435}
1436
1437impl ForNodeIR {
1438 pub fn validate(&self, depth: usize) -> Result<()> {
1440 if depth > MAX_RECURSION_DEPTH {
1441 return Err(IRError::CircularReference("For node recursion depth exceeded".to_string()).into());
1442 }
1443
1444 self.iterator.validate(depth + 1)?;
1445
1446 if self.body.len() > MAX_ARRAY_LENGTH {
1447 return Err(IRError::SizeLimitExceeded("For node body length exceeded".to_string()).into());
1448 }
1449 for node in &self.body {
1450 node.validate(depth + 1)?;
1451 }
1452
1453 Ok(())
1454 }
1455}
1456
1457#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1459pub struct ForIteratorIR {
1460 pub item: String,
1462 pub index: Option<String>,
1464 pub collection: ExpressionIR,
1466}
1467
1468impl ForIteratorIR {
1469 pub fn validate(&self, depth: usize) -> Result<()> {
1471 if depth > MAX_RECURSION_DEPTH {
1472 return Err(IRError::CircularReference("For iterator recursion depth exceeded".to_string()).into());
1473 }
1474
1475 if self.item.len() > MAX_STRING_LENGTH {
1476 return Err(IRError::SizeLimitExceeded("For iterator item length exceeded".to_string()).into());
1477 }
1478
1479 if let Some(index) = &self.index {
1480 if index.len() > MAX_STRING_LENGTH {
1481 return Err(IRError::SizeLimitExceeded("For iterator index length exceeded".to_string()).into());
1482 }
1483 }
1484
1485 self.collection.validate(depth + 1)
1486 }
1487}
1488
1489#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1491pub struct ElementIR {
1492 pub tag: String,
1494 pub attributes: Vec<AttributeIR>,
1496 pub children: Vec<TemplateNodeIR>,
1498 pub is_static: bool,
1500 #[serde(default)]
1502 pub span: Span,
1503 #[serde(default)]
1505 pub trivia: Trivia,
1506}
1507
1508impl ElementIR {
1509 pub fn validate(&self, depth: usize) -> Result<()> {
1511 if depth > MAX_RECURSION_DEPTH {
1512 return Err(IRError::CircularReference("Element recursion depth exceeded".to_string()).into());
1513 }
1514
1515 if self.tag.len() > MAX_STRING_LENGTH {
1516 return Err(IRError::SizeLimitExceeded("Element tag length exceeded".to_string()).into());
1517 }
1518
1519 if self.attributes.len() > MAX_ARRAY_LENGTH {
1520 return Err(IRError::SizeLimitExceeded("Element attributes length exceeded".to_string()).into());
1521 }
1522 for attr in &self.attributes {
1523 attr.validate(depth + 1)?;
1524 }
1525
1526 if self.children.len() > MAX_ARRAY_LENGTH {
1527 return Err(IRError::SizeLimitExceeded("Element children length exceeded".to_string()).into());
1528 }
1529 for child in &self.children {
1530 child.validate(depth + 1)?;
1531 }
1532
1533 Ok(())
1534 }
1535}
1536
1537#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1539pub struct AttributeIR {
1540 pub name: String,
1542 pub value: Option<String>,
1544 pub value_ast: Option<JsExpr>,
1546 pub argument: Option<String>,
1548 pub modifiers: Vec<String>,
1550 pub is_directive: bool,
1552 pub is_dynamic: bool,
1554 #[serde(default)]
1556 pub span: Span,
1557 #[serde(default)]
1559 pub trivia: Trivia,
1560}
1561
1562impl AttributeIR {
1563 pub fn validate(&self, depth: usize) -> Result<()> {
1565 if depth > MAX_RECURSION_DEPTH {
1566 return Err(IRError::CircularReference("Attribute recursion depth exceeded".to_string()).into());
1567 }
1568
1569 if self.name.len() > MAX_STRING_LENGTH {
1570 return Err(IRError::SizeLimitExceeded("Attribute name length exceeded".to_string()).into());
1571 }
1572
1573 if let Some(value) = &self.value {
1574 if value.len() > MAX_STRING_LENGTH {
1575 return Err(IRError::SizeLimitExceeded("Attribute value length exceeded".to_string()).into());
1576 }
1577 }
1578
1579 if let Some(value_ast) = &self.value_ast {
1580 value_ast.validate(depth + 1)?;
1581 }
1582
1583 if let Some(argument) = &self.argument {
1584 if argument.len() > MAX_STRING_LENGTH {
1585 return Err(IRError::SizeLimitExceeded("Attribute argument length exceeded".to_string()).into());
1586 }
1587 }
1588
1589 if self.modifiers.len() > MAX_ARRAY_LENGTH {
1590 return Err(IRError::SizeLimitExceeded("Attribute modifiers length exceeded".to_string()).into());
1591 }
1592 for modifier in &self.modifiers {
1593 if modifier.len() > MAX_STRING_LENGTH {
1594 return Err(IRError::SizeLimitExceeded("Attribute modifier length exceeded".to_string()).into());
1595 }
1596 }
1597
1598 Ok(())
1599 }
1600}
1601
1602#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1604pub struct ExpressionIR {
1605 pub code: String,
1607 pub ast: Option<JsExpr>,
1609 pub is_static: bool,
1611 #[serde(default)]
1613 pub span: Span,
1614 #[serde(default)]
1616 pub trivia: Trivia,
1617}
1618
1619impl ExpressionIR {
1620 pub fn validate(&self, depth: usize) -> Result<()> {
1622 if depth > MAX_RECURSION_DEPTH {
1623 return Err(IRError::CircularReference("Expression IR recursion depth exceeded".to_string()).into());
1624 }
1625
1626 if self.code.len() > MAX_STRING_LENGTH {
1627 return Err(IRError::SizeLimitExceeded("Expression code length exceeded".to_string()).into());
1628 }
1629
1630 if let Some(ast) = &self.ast {
1631 ast.validate(depth + 1)?;
1632 }
1633
1634 Ok(())
1635 }
1636
1637 pub fn is_empty(&self) -> bool {
1639 self.code.is_empty() && self.ast.is_none() && self.trivia.is_empty()
1640 }
1641}
1642
1643#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1645pub struct CustomBlockIR {
1646 pub name: String,
1648 pub content: String,
1650 pub attributes: HashMap<String, String>,
1652 #[serde(default)]
1654 pub span: Span,
1655 #[serde(default)]
1657 pub trivia: Trivia,
1658}
1659
1660impl CustomBlockIR {
1661 pub fn validate(&self) -> Result<()> {
1663 if self.name.len() > MAX_STRING_LENGTH {
1664 return Err(IRError::SizeLimitExceeded("Custom block name length exceeded".to_string()).into());
1665 }
1666
1667 if self.content.len() > MAX_STRING_LENGTH {
1668 return Err(IRError::SizeLimitExceeded("Custom block content length exceeded".to_string()).into());
1669 }
1670
1671 if self.attributes.len() > MAX_OBJECT_SIZE {
1672 return Err(IRError::SizeLimitExceeded("Custom block attributes size exceeded".to_string()).into());
1673 }
1674 for (key, value) in &self.attributes {
1675 if key.len() > MAX_STRING_LENGTH {
1676 return Err(IRError::SizeLimitExceeded("Custom block attribute key length exceeded".to_string()).into());
1677 }
1678 if value.len() > MAX_STRING_LENGTH {
1679 return Err(IRError::SizeLimitExceeded("Custom block attribute value length exceeded".to_string()).into());
1680 }
1681 }
1682
1683 Ok(())
1684 }
1685
1686 pub fn is_empty(&self) -> bool {
1688 self.name.is_empty() && self.content.is_empty() && self.attributes.is_empty() && self.trivia.is_empty()
1689 }
1690}
1691
1692#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1694pub struct StyleIR {
1695 pub code: String,
1697 pub lang: String,
1699 pub scoped: bool,
1701 #[serde(default)]
1703 pub span: Span,
1704 #[serde(default)]
1706 pub trivia: Trivia,
1707}
1708
1709impl StyleIR {
1710 pub fn validate(&self) -> Result<()> {
1712 if self.code.len() > MAX_STRING_LENGTH {
1713 return Err(IRError::SizeLimitExceeded("Style code length exceeded".to_string()).into());
1714 }
1715
1716 if self.lang.len() > MAX_STRING_LENGTH {
1717 return Err(IRError::SizeLimitExceeded("Style lang length exceeded".to_string()).into());
1718 }
1719
1720 Ok(())
1721 }
1722
1723 pub fn is_empty(&self) -> bool {
1725 self.code.is_empty() && self.lang.is_empty() && self.trivia.is_empty()
1726 }
1727}
1728
1729pub trait JsExprVisitor<R> {
1731 fn visit_identifier(&mut self, id: &String, span: &Span, trivia: &Trivia) -> R;
1733 fn visit_literal(&mut self, value: &NargoValue, span: &Span, trivia: &Trivia) -> R;
1735 fn visit_unary(&mut self, op: &String, argument: &JsExpr, span: &Span, trivia: &Trivia) -> R;
1737 fn visit_binary(&mut self, left: &JsExpr, op: &String, right: &JsExpr, span: &Span, trivia: &Trivia) -> R;
1739 fn visit_call(&mut self, callee: &JsExpr, args: &Vec<JsExpr>, span: &Span, trivia: &Trivia) -> R;
1741 fn visit_member(&mut self, object: &JsExpr, property: &JsExpr, computed: bool, span: &Span, trivia: &Trivia) -> R;
1743 fn visit_optional_member(&mut self, object: &JsExpr, property: &JsExpr, computed: bool, span: &Span, trivia: &Trivia) -> R;
1745 fn visit_optional_call(&mut self, callee: &JsExpr, args: &Vec<JsExpr>, span: &Span, trivia: &Trivia) -> R;
1747 fn visit_nullish_coalescing(&mut self, left: &JsExpr, right: &JsExpr, span: &Span, trivia: &Trivia) -> R;
1749 fn visit_logical_assignment(&mut self, op: &String, left: &JsExpr, right: &JsExpr, span: &Span, trivia: &Trivia) -> R;
1751 fn visit_array(&mut self, items: &Vec<JsExpr>, span: &Span, trivia: &Trivia) -> R;
1753 fn visit_object(&mut self, props: &HashMap<String, JsExpr>, span: &Span, trivia: &Trivia) -> R;
1755 fn visit_arrow_function(&mut self, params: &Vec<String>, body: &JsExpr, span: &Span, trivia: &Trivia) -> R;
1757 fn visit_tse_element(&mut self, tag: &String, attributes: &Vec<TseAttribute>, children: &Vec<JsExpr>, span: &Span, trivia: &Trivia) -> R;
1759 fn visit_conditional(&mut self, test: &JsExpr, consequent: &JsExpr, alternate: &JsExpr, span: &Span, trivia: &Trivia) -> R;
1761 fn visit_template_literal(&mut self, quasis: &Vec<String>, expressions: &Vec<JsExpr>, span: &Span, trivia: &Trivia) -> R;
1763 fn visit_spread(&mut self, expr: &JsExpr, span: &Span, trivia: &Trivia) -> R;
1765 fn visit_type_of(&mut self, expr: &JsExpr, span: &Span, trivia: &Trivia) -> R;
1767 fn visit_instance_of(&mut self, left: &JsExpr, right: &JsExpr, span: &Span, trivia: &Trivia) -> R;
1769 fn visit_other(&mut self, code: &String, span: &Span, trivia: &Trivia) -> R;
1771}
1772
1773impl JsExpr {
1774 pub fn accept<R>(&self, visitor: &mut dyn JsExprVisitor<R>) -> R {
1776 match self {
1777 JsExpr::Identifier(id, span, trivia) => visitor.visit_identifier(id, span, trivia),
1778 JsExpr::Literal(value, span, trivia) => visitor.visit_literal(value, span, trivia),
1779 JsExpr::Unary { op, argument, span, trivia } => visitor.visit_unary(op, argument, span, trivia),
1780 JsExpr::Binary { left, op, right, span, trivia } => visitor.visit_binary(left, op, right, span, trivia),
1781 JsExpr::Call { callee, args, span, trivia } => visitor.visit_call(callee, args, span, trivia),
1782 JsExpr::Member { object, property, computed, span, trivia } => visitor.visit_member(object, property, *computed, span, trivia),
1783 JsExpr::OptionalMember { object, property, computed, span, trivia } => visitor.visit_optional_member(object, property, *computed, span, trivia),
1784 JsExpr::OptionalCall { callee, args, span, trivia } => visitor.visit_optional_call(callee, args, span, trivia),
1785 JsExpr::NullishCoalescing { left, right, span, trivia } => visitor.visit_nullish_coalescing(left, right, span, trivia),
1786 JsExpr::LogicalAssignment { op, left, right, span, trivia } => visitor.visit_logical_assignment(op, left, right, span, trivia),
1787 JsExpr::Array(items, span, trivia) => visitor.visit_array(items, span, trivia),
1788 JsExpr::Object(props, span, trivia) => visitor.visit_object(props, span, trivia),
1789 JsExpr::ArrowFunction { params, body, span, trivia } => visitor.visit_arrow_function(params, body, span, trivia),
1790 JsExpr::TseElement { tag, attributes, children, span, trivia } => visitor.visit_tse_element(tag, attributes, children, span, trivia),
1791 JsExpr::Conditional { test, consequent, alternate, span, trivia } => visitor.visit_conditional(test, consequent, alternate, span, trivia),
1792 JsExpr::TemplateLiteral { quasis, expressions, span, trivia } => visitor.visit_template_literal(quasis, expressions, span, trivia),
1793 JsExpr::Spread(expr, span, trivia) => visitor.visit_spread(expr, span, trivia),
1794 JsExpr::TypeOf(expr, span, trivia) => visitor.visit_type_of(expr, span, trivia),
1795 JsExpr::InstanceOf { left, right, span, trivia } => visitor.visit_instance_of(left, right, span, trivia),
1796 JsExpr::Other(code, span, trivia) => visitor.visit_other(code, span, trivia),
1797 }
1798 }
1799}
1800
1801pub trait JsStmtVisitor<R> {
1803 fn visit_expr(&mut self, expr: &JsExpr, span: &Span, trivia: &Trivia) -> R;
1805 fn visit_variable_decl(&mut self, kind: &String, id: &String, init: &Option<JsExpr>, span: &Span, trivia: &Trivia) -> R;
1807 fn visit_import(&mut self, source: &String, specifiers: &Vec<String>, span: &Span, trivia: &Trivia) -> R;
1809 fn visit_export(&mut self, declaration: &JsStmt, span: &Span, trivia: &Trivia) -> R;
1811 fn visit_export_all(&mut self, source: &String, span: &Span, trivia: &Trivia) -> R;
1813 fn visit_export_named(&mut self, source: &Option<String>, specifiers: &Vec<String>, span: &Span, trivia: &Trivia) -> R;
1815 fn visit_function_decl(&mut self, id: &String, params: &Vec<String>, body: &Vec<JsStmt>, is_async: bool, span: &Span, trivia: &Trivia) -> R;
1817 fn visit_return(&mut self, expr: &Option<JsExpr>, span: &Span, trivia: &Trivia) -> R;
1819 fn visit_if(&mut self, test: &JsExpr, consequent: &JsStmt, alternate: &Option<Box<JsStmt>>, span: &Span, trivia: &Trivia) -> R;
1821 fn visit_while(&mut self, test: &JsExpr, body: &JsStmt, span: &Span, trivia: &Trivia) -> R;
1823 fn visit_for(&mut self, init: &Option<Box<JsStmt>>, test: &Option<JsExpr>, update: &Option<JsExpr>, body: &JsStmt, span: &Span, trivia: &Trivia) -> R;
1825 fn visit_for_in(&mut self, left: &JsStmt, right: &JsExpr, body: &JsStmt, span: &Span, trivia: &Trivia) -> R;
1827 fn visit_for_of(&mut self, left: &JsStmt, right: &JsExpr, body: &JsStmt, span: &Span, trivia: &Trivia) -> R;
1829 fn visit_try(&mut self, block: &JsStmt, handler: &Option<(String, Box<JsStmt>)>, finalizer: &Option<Box<JsStmt>>, span: &Span, trivia: &Trivia) -> R;
1831 fn visit_switch(&mut self, discriminant: &JsExpr, cases: &Vec<(Option<JsExpr>, Vec<JsStmt>)>, span: &Span, trivia: &Trivia) -> R;
1833 fn visit_throw(&mut self, expr: &JsExpr, span: &Span, trivia: &Trivia) -> R;
1835 fn visit_block(&mut self, stmts: &Vec<JsStmt>, span: &Span, trivia: &Trivia) -> R;
1837 fn visit_break(&mut self, span: &Span, trivia: &Trivia) -> R;
1839 fn visit_continue(&mut self, span: &Span, trivia: &Trivia) -> R;
1841 fn visit_other(&mut self, code: &String, span: &Span, trivia: &Trivia) -> R;
1843}
1844
1845impl JsStmt {
1846 pub fn accept<R>(&self, visitor: &mut dyn JsStmtVisitor<R>) -> R {
1848 match self {
1849 JsStmt::Expr(expr, span, trivia) => visitor.visit_expr(expr, span, trivia),
1850 JsStmt::VariableDecl { kind, id, init, span, trivia } => visitor.visit_variable_decl(kind, id, init, span, trivia),
1851 JsStmt::Import { source, specifiers, span, trivia } => visitor.visit_import(source, specifiers, span, trivia),
1852 JsStmt::Export { declaration, span, trivia } => visitor.visit_export(declaration, span, trivia),
1853 JsStmt::ExportAll { source, span, trivia } => visitor.visit_export_all(source, span, trivia),
1854 JsStmt::ExportNamed { source, specifiers, span, trivia } => visitor.visit_export_named(source, specifiers, span, trivia),
1855 JsStmt::FunctionDecl { id, params, body, is_async, span, trivia } => visitor.visit_function_decl(id, params, body, *is_async, span, trivia),
1856 JsStmt::Return(expr, span, trivia) => visitor.visit_return(expr, span, trivia),
1857 JsStmt::If { test, consequent, alternate, span, trivia } => visitor.visit_if(test, consequent, alternate, span, trivia),
1858 JsStmt::While { test, body, span, trivia } => visitor.visit_while(test, body, span, trivia),
1859 JsStmt::For { init, test, update, body, span, trivia } => visitor.visit_for(init, test, update, body, span, trivia),
1860 JsStmt::ForIn { left, right, body, span, trivia } => visitor.visit_for_in(left, right, body, span, trivia),
1861 JsStmt::ForOf { left, right, body, span, trivia } => visitor.visit_for_of(left, right, body, span, trivia),
1862 JsStmt::Try { block, handler, finalizer, span, trivia } => visitor.visit_try(block, handler, finalizer, span, trivia),
1863 JsStmt::Switch { discriminant, cases, span, trivia } => visitor.visit_switch(discriminant, cases, span, trivia),
1864 JsStmt::Throw(expr, span, trivia) => visitor.visit_throw(expr, span, trivia),
1865 JsStmt::Block(stmts, span, trivia) => visitor.visit_block(stmts, span, trivia),
1866 JsStmt::Break(span, trivia) => visitor.visit_break(span, trivia),
1867 JsStmt::Continue(span, trivia) => visitor.visit_continue(span, trivia),
1868 JsStmt::Other(code, span, trivia) => visitor.visit_other(code, span, trivia),
1869 }
1870 }
1871}
1872
1873pub trait TemplateNodeVisitor<R> {
1875 fn visit_element(&mut self, element: &ElementIR, depth: usize) -> R;
1877 fn visit_if(&mut self, if_node: &IfNodeIR, depth: usize) -> R;
1879 fn visit_for(&mut self, for_node: &ForNodeIR, depth: usize) -> R;
1881 fn visit_text(&mut self, text: &String, span: &Span, trivia: &Trivia, depth: usize) -> R;
1883 fn visit_interpolation(&mut self, expr: &ExpressionIR, depth: usize) -> R;
1885 fn visit_comment(&mut self, comment: &String, span: &Span, trivia: &Trivia, depth: usize) -> R;
1887 fn visit_hoisted(&mut self, key: &String, depth: usize) -> R;
1889}
1890
1891impl TemplateNodeIR {
1892 pub fn accept<R>(&self, visitor: &mut dyn TemplateNodeVisitor<R>, depth: usize) -> R {
1894 match self {
1895 TemplateNodeIR::Element(element) => visitor.visit_element(element, depth),
1896 TemplateNodeIR::If(if_node) => visitor.visit_if(if_node, depth),
1897 TemplateNodeIR::For(for_node) => visitor.visit_for(for_node, depth),
1898 TemplateNodeIR::Text(text, span, trivia) => visitor.visit_text(text, span, trivia, depth),
1899 TemplateNodeIR::Interpolation(expr) => visitor.visit_interpolation(expr, depth),
1900 TemplateNodeIR::Comment(comment, span, trivia) => visitor.visit_comment(comment, span, trivia, depth),
1901 TemplateNodeIR::Hoisted(key) => visitor.visit_hoisted(key, depth),
1902 }
1903 }
1904}
1905
1906pub struct DefaultVisitor;
1908
1909impl JsExprVisitor<()> for DefaultVisitor {
1910 fn visit_identifier(&mut self, _id: &String, _span: &Span, _trivia: &Trivia) -> () {}
1911 fn visit_literal(&mut self, _value: &NargoValue, _span: &Span, _trivia: &Trivia) -> () {}
1912 fn visit_unary(&mut self, _op: &String, argument: &JsExpr, _span: &Span, _trivia: &Trivia) -> () {
1913 argument.accept(self);
1914 }
1915 fn visit_binary(&mut self, left: &JsExpr, _op: &String, right: &JsExpr, _span: &Span, _trivia: &Trivia) -> () {
1916 left.accept(self);
1917 right.accept(self);
1918 }
1919 fn visit_call(&mut self, callee: &JsExpr, args: &Vec<JsExpr>, _span: &Span, _trivia: &Trivia) -> () {
1920 callee.accept(self);
1921 for arg in args {
1922 arg.accept(self);
1923 }
1924 }
1925 fn visit_member(&mut self, object: &JsExpr, property: &JsExpr, _computed: bool, _span: &Span, _trivia: &Trivia) -> () {
1926 object.accept(self);
1927 property.accept(self);
1928 }
1929 fn visit_optional_member(&mut self, object: &JsExpr, property: &JsExpr, _computed: bool, _span: &Span, _trivia: &Trivia) -> () {
1930 object.accept(self);
1931 property.accept(self);
1932 }
1933 fn visit_optional_call(&mut self, callee: &JsExpr, args: &Vec<JsExpr>, _span: &Span, _trivia: &Trivia) -> () {
1934 callee.accept(self);
1935 for arg in args {
1936 arg.accept(self);
1937 }
1938 }
1939 fn visit_nullish_coalescing(&mut self, left: &JsExpr, right: &JsExpr, _span: &Span, _trivia: &Trivia) -> () {
1940 left.accept(self);
1941 right.accept(self);
1942 }
1943 fn visit_logical_assignment(&mut self, _op: &String, left: &JsExpr, right: &JsExpr, _span: &Span, _trivia: &Trivia) -> () {
1944 left.accept(self);
1945 right.accept(self);
1946 }
1947 fn visit_array(&mut self, items: &Vec<JsExpr>, _span: &Span, _trivia: &Trivia) -> () {
1948 for item in items {
1949 item.accept(self);
1950 }
1951 }
1952 fn visit_object(&mut self, props: &HashMap<String, JsExpr>, _span: &Span, _trivia: &Trivia) -> () {
1953 for (_key, value) in props {
1954 value.accept(self);
1955 }
1956 }
1957 fn visit_arrow_function(&mut self, _params: &Vec<String>, body: &JsExpr, _span: &Span, _trivia: &Trivia) -> () {
1958 body.accept(self);
1959 }
1960 fn visit_tse_element(&mut self, _tag: &String, _attributes: &Vec<TseAttribute>, children: &Vec<JsExpr>, _span: &Span, _trivia: &Trivia) -> () {
1961 for child in children {
1962 child.accept(self);
1963 }
1964 }
1965 fn visit_conditional(&mut self, test: &JsExpr, consequent: &JsExpr, alternate: &JsExpr, _span: &Span, _trivia: &Trivia) -> () {
1966 test.accept(self);
1967 consequent.accept(self);
1968 alternate.accept(self);
1969 }
1970 fn visit_template_literal(&mut self, _quasis: &Vec<String>, expressions: &Vec<JsExpr>, _span: &Span, _trivia: &Trivia) -> () {
1971 for expr in expressions {
1972 expr.accept(self);
1973 }
1974 }
1975 fn visit_spread(&mut self, expr: &JsExpr, _span: &Span, _trivia: &Trivia) -> () {
1976 expr.accept(self);
1977 }
1978 fn visit_type_of(&mut self, expr: &JsExpr, _span: &Span, _trivia: &Trivia) -> () {
1979 expr.accept(self);
1980 }
1981 fn visit_instance_of(&mut self, left: &JsExpr, right: &JsExpr, _span: &Span, _trivia: &Trivia) -> () {
1982 left.accept(self);
1983 right.accept(self);
1984 }
1985 fn visit_other(&mut self, _code: &String, _span: &Span, _trivia: &Trivia) -> () {}
1986}
1987
1988impl JsStmtVisitor<()> for DefaultVisitor {
1989 fn visit_expr(&mut self, expr: &JsExpr, _span: &Span, _trivia: &Trivia) -> () {
1990 expr.accept(self);
1991 }
1992 fn visit_variable_decl(&mut self, _kind: &String, _id: &String, init: &Option<JsExpr>, _span: &Span, _trivia: &Trivia) -> () {
1993 if let Some(expr) = init {
1994 expr.accept(self);
1995 }
1996 }
1997 fn visit_import(&mut self, _source: &String, _specifiers: &Vec<String>, _span: &Span, _trivia: &Trivia) -> () {}
1998 fn visit_export(&mut self, declaration: &JsStmt, _span: &Span, _trivia: &Trivia) -> () {
1999 declaration.accept(self);
2000 }
2001 fn visit_export_all(&mut self, _source: &String, _span: &Span, _trivia: &Trivia) -> () {}
2002 fn visit_export_named(&mut self, _source: &Option<String>, _specifiers: &Vec<String>, _span: &Span, _trivia: &Trivia) -> () {}
2003 fn visit_function_decl(&mut self, _id: &String, _params: &Vec<String>, body: &Vec<JsStmt>, _is_async: bool, _span: &Span, _trivia: &Trivia) -> () {
2004 for stmt in body {
2005 stmt.accept(self);
2006 }
2007 }
2008 fn visit_return(&mut self, expr: &Option<JsExpr>, _span: &Span, _trivia: &Trivia) -> () {
2009 if let Some(expr) = expr {
2010 expr.accept(self);
2011 }
2012 }
2013 fn visit_if(&mut self, test: &JsExpr, consequent: &JsStmt, alternate: &Option<Box<JsStmt>>, _span: &Span, _trivia: &Trivia) -> () {
2014 test.accept(self);
2015 consequent.accept(self);
2016 if let Some(alt) = alternate {
2017 alt.accept(self);
2018 }
2019 }
2020 fn visit_while(&mut self, test: &JsExpr, body: &JsStmt, _span: &Span, _trivia: &Trivia) -> () {
2021 test.accept(self);
2022 body.accept(self);
2023 }
2024 fn visit_for(&mut self, init: &Option<Box<JsStmt>>, test: &Option<JsExpr>, update: &Option<JsExpr>, body: &JsStmt, _span: &Span, _trivia: &Trivia) -> () {
2025 if let Some(init_stmt) = init {
2026 init_stmt.accept(self);
2027 }
2028 if let Some(test_expr) = test {
2029 test_expr.accept(self);
2030 }
2031 if let Some(update_expr) = update {
2032 update_expr.accept(self);
2033 }
2034 body.accept(self);
2035 }
2036 fn visit_for_in(&mut self, left: &JsStmt, right: &JsExpr, body: &JsStmt, _span: &Span, _trivia: &Trivia) -> () {
2037 left.accept(self);
2038 right.accept(self);
2039 body.accept(self);
2040 }
2041 fn visit_for_of(&mut self, left: &JsStmt, right: &JsExpr, body: &JsStmt, _span: &Span, _trivia: &Trivia) -> () {
2042 left.accept(self);
2043 right.accept(self);
2044 body.accept(self);
2045 }
2046 fn visit_try(&mut self, block: &JsStmt, handler: &Option<(String, Box<JsStmt>)>, finalizer: &Option<Box<JsStmt>>, _span: &Span, _trivia: &Trivia) -> () {
2047 block.accept(self);
2048 if let Some((_id, body)) = handler {
2049 body.accept(self);
2050 }
2051 if let Some(body) = finalizer {
2052 body.accept(self);
2053 }
2054 }
2055 fn visit_switch(&mut self, discriminant: &JsExpr, cases: &Vec<(Option<JsExpr>, Vec<JsStmt>)>, _span: &Span, _trivia: &Trivia) -> () {
2056 discriminant.accept(self);
2057 for (test, stmts) in cases {
2058 if let Some(test_expr) = test {
2059 test_expr.accept(self);
2060 }
2061 for stmt in stmts {
2062 stmt.accept(self);
2063 }
2064 }
2065 }
2066 fn visit_throw(&mut self, expr: &JsExpr, _span: &Span, _trivia: &Trivia) -> () {
2067 expr.accept(self);
2068 }
2069 fn visit_block(&mut self, stmts: &Vec<JsStmt>, _span: &Span, _trivia: &Trivia) -> () {
2070 for stmt in stmts {
2071 stmt.accept(self);
2072 }
2073 }
2074 fn visit_break(&mut self, _span: &Span, _trivia: &Trivia) -> () {}
2075 fn visit_continue(&mut self, _span: &Span, _trivia: &Trivia) -> () {}
2076 fn visit_other(&mut self, _code: &String, _span: &Span, _trivia: &Trivia) -> () {}
2077}
2078
2079impl TemplateNodeVisitor<()> for DefaultVisitor {
2080 fn visit_element(&mut self, element: &ElementIR, depth: usize) -> () {
2081 for child in &element.children {
2082 child.accept(self, depth + 1);
2083 }
2084 }
2085 fn visit_if(&mut self, if_node: &IfNodeIR, depth: usize) -> () {
2086 for node in &if_node.consequent {
2087 node.accept(self, depth + 1);
2088 }
2089 if let Some(alternate) = &if_node.alternate {
2090 for node in alternate {
2091 node.accept(self, depth + 1);
2092 }
2093 }
2094 for (_condition, body) in &if_node.else_ifs {
2095 for node in body {
2096 node.accept(self, depth + 1);
2097 }
2098 }
2099 }
2100 fn visit_for(&mut self, for_node: &ForNodeIR, depth: usize) -> () {
2101 for node in &for_node.body {
2102 node.accept(self, depth + 1);
2103 }
2104 }
2105 fn visit_text(&mut self, _text: &String, _span: &Span, _trivia: &Trivia, _depth: usize) -> () {}
2106 fn visit_interpolation(&mut self, _expr: &ExpressionIR, _depth: usize) -> () {}
2107 fn visit_comment(&mut self, _comment: &String, _span: &Span, _trivia: &Trivia, _depth: usize) -> () {}
2108 fn visit_hoisted(&mut self, _key: &String, _depth: usize) -> () {}
2109}
2110
2111pub mod optimizer {
2115 use super::*;
2116
2117 pub struct ExprOptimizer;
2121
2122 impl ExprOptimizer {
2123 pub fn optimize(expr: &mut JsExpr) {
2128 match expr {
2129 JsExpr::Unary { op, argument, .. } => {
2130 Self::optimize(argument);
2131 if let JsExpr::Literal(value, span, trivia) = &**argument {
2133 match op.as_str() {
2134 "!" => {
2135 if let Some(b) = value.as_bool() {
2136 *expr = JsExpr::Literal(NargoValue::Bool(!b), *span, trivia.clone());
2137 }
2138 }
2139 "-" => {
2140 if let Some(n) = value.as_number() {
2141 *expr = JsExpr::Literal(NargoValue::Number(-n), *span, trivia.clone());
2142 }
2143 }
2144 "+" => {
2145 if let Some(n) = value.as_number() {
2146 *expr = JsExpr::Literal(NargoValue::Number(n), *span, trivia.clone());
2147 }
2148 else if let Some(s) = value.as_str() {
2149 if let Ok(n) = s.parse::<f64>() {
2150 *expr = JsExpr::Literal(NargoValue::Number(n), *span, trivia.clone());
2151 }
2152 }
2153 }
2154 "~" => {
2155 if let Some(n) = value.as_number() {
2156 let int_val = n as i64;
2157 *expr = JsExpr::Literal(NargoValue::Number((!int_val) as f64), *span, trivia.clone());
2158 }
2159 }
2160 _ => return,
2161 };
2162 }
2163 }
2164 JsExpr::Binary { left, op, right, span, trivia } => {
2165 Self::optimize(left);
2166 Self::optimize(right);
2167 if let (JsExpr::Literal(left_val, _, _), JsExpr::Literal(right_val, _, _)) = (&**left, &**right) {
2169 match op.as_str() {
2170 "+" => {
2171 if let (Some(l), Some(r)) = (left_val.as_number(), right_val.as_number()) {
2172 *expr = JsExpr::Literal(NargoValue::Number(l + r), *span, trivia.clone());
2173 }
2174 else if let (Some(l), Some(r)) = (left_val.as_str(), right_val.as_str()) {
2175 *expr = JsExpr::Literal(NargoValue::String(format!("{}{}", l, r)), *span, trivia.clone());
2176 }
2177 else if let (Some(l), Some(r)) = (left_val.as_str(), right_val.as_number()) {
2178 *expr = JsExpr::Literal(NargoValue::String(format!("{}{}", l, r)), *span, trivia.clone());
2179 }
2180 else if let (Some(l), Some(r)) = (left_val.as_number(), right_val.as_str()) {
2181 *expr = JsExpr::Literal(NargoValue::String(format!("{}{}", l, r)), *span, trivia.clone());
2182 }
2183 }
2184 "-" => {
2185 if let (Some(l), Some(r)) = (left_val.as_number(), right_val.as_number()) {
2186 *expr = JsExpr::Literal(NargoValue::Number(l - r), *span, trivia.clone());
2187 }
2188 }
2189 "*" => {
2190 if let (Some(l), Some(r)) = (left_val.as_number(), right_val.as_number()) {
2191 *expr = JsExpr::Literal(NargoValue::Number(l * r), *span, trivia.clone());
2192 }
2193 }
2194 "/" => {
2195 if let (Some(l), Some(r)) = (left_val.as_number(), right_val.as_number()) {
2196 *expr = JsExpr::Literal(NargoValue::Number(l / r), *span, trivia.clone());
2197 }
2198 }
2199 "%" => {
2200 if let (Some(l), Some(r)) = (left_val.as_number(), right_val.as_number()) {
2201 *expr = JsExpr::Literal(NargoValue::Number(l % r), *span, trivia.clone());
2202 }
2203 }
2204 "==" | "!=" | "===" | "!==" => {
2205 let result = left_val == right_val;
2206 let final_result = if op == "!=" || op == "!==" { !result } else { result };
2207 *expr = JsExpr::Literal(NargoValue::Bool(final_result), *span, trivia.clone());
2208 }
2209 "<" => {
2210 if let (Some(l), Some(r)) = (left_val.as_number(), right_val.as_number()) {
2211 *expr = JsExpr::Literal(NargoValue::Bool(l < r), *span, trivia.clone());
2212 }
2213 }
2214 "<=" => {
2215 if let (Some(l), Some(r)) = (left_val.as_number(), right_val.as_number()) {
2216 *expr = JsExpr::Literal(NargoValue::Bool(l <= r), *span, trivia.clone());
2217 }
2218 }
2219 ">" => {
2220 if let (Some(l), Some(r)) = (left_val.as_number(), right_val.as_number()) {
2221 *expr = JsExpr::Literal(NargoValue::Bool(l > r), *span, trivia.clone());
2222 }
2223 }
2224 ">=" => {
2225 if let (Some(l), Some(r)) = (left_val.as_number(), right_val.as_number()) {
2226 *expr = JsExpr::Literal(NargoValue::Bool(l >= r), *span, trivia.clone());
2227 }
2228 }
2229 "&&" => {
2230 if let (Some(l), Some(r)) = (left_val.as_bool(), right_val.as_bool()) {
2231 *expr = JsExpr::Literal(NargoValue::Bool(l && r), *span, trivia.clone());
2232 }
2233 }
2234 "||" => {
2235 if let (Some(l), Some(r)) = (left_val.as_bool(), right_val.as_bool()) {
2236 *expr = JsExpr::Literal(NargoValue::Bool(l || r), *span, trivia.clone());
2237 }
2238 }
2239 _ => return,
2240 };
2241 }
2242 }
2243 JsExpr::Conditional { test, consequent, alternate, span: _, trivia: _ } => {
2244 Self::optimize(test);
2245 Self::optimize(consequent);
2246 Self::optimize(alternate);
2247 if let JsExpr::Literal(test_val, _, _) = &**test {
2249 if let Some(b) = test_val.as_bool() {
2250 let optimized = if b { consequent.clone() } else { alternate.clone() };
2251 *expr = *optimized;
2252 }
2253 }
2254 }
2255 JsExpr::Array(items, ..) => {
2256 for item in items {
2257 Self::optimize(item);
2258 }
2259 }
2260 JsExpr::Object(props, ..) => {
2261 for (_, value) in props {
2262 Self::optimize(value);
2263 }
2264 }
2265 JsExpr::Call { callee, args, .. } => {
2266 Self::optimize(callee);
2267 for arg in args {
2268 Self::optimize(arg);
2269 }
2270 }
2271 JsExpr::Member { object, property, .. } => {
2272 Self::optimize(object);
2273 Self::optimize(property);
2274 }
2275 JsExpr::OptionalMember { object, property, .. } => {
2276 Self::optimize(object);
2277 Self::optimize(property);
2278 }
2279 JsExpr::OptionalCall { callee, args, .. } => {
2280 Self::optimize(callee);
2281 for arg in args {
2282 Self::optimize(arg);
2283 }
2284 }
2285 JsExpr::NullishCoalescing { left, right, .. } => {
2286 Self::optimize(left);
2287 Self::optimize(right);
2288 }
2289 JsExpr::LogicalAssignment { left, right, .. } => {
2290 Self::optimize(left);
2291 Self::optimize(right);
2292 }
2293 JsExpr::ArrowFunction { body, .. } => {
2294 Self::optimize(body);
2295 }
2296 JsExpr::TseElement { children, .. } => {
2297 for child in children {
2298 Self::optimize(child);
2299 }
2300 }
2301 JsExpr::TemplateLiteral { expressions, .. } => {
2302 for expr in expressions {
2303 Self::optimize(expr);
2304 }
2305 }
2306 JsExpr::Spread(expr, ..) => {
2307 Self::optimize(expr);
2308 }
2309 JsExpr::TypeOf(expr, ..) => {
2310 Self::optimize(expr);
2311 }
2312 JsExpr::InstanceOf { left, right, .. } => {
2313 Self::optimize(left);
2314 Self::optimize(right);
2315 }
2316 _ => {}
2317 }
2318 }
2319 }
2320
2321 pub struct StmtOptimizer;
2325
2326 impl StmtOptimizer {
2327 pub fn optimize(stmt: &mut JsStmt) {
2332 match stmt {
2333 JsStmt::Expr(expr, ..) => {
2334 ExprOptimizer::optimize(expr);
2335 }
2336 JsStmt::VariableDecl { init, .. } => {
2337 if let Some(expr) = init {
2338 ExprOptimizer::optimize(expr);
2339 }
2340 }
2341 JsStmt::FunctionDecl { body, .. } => {
2342 for stmt in body {
2343 Self::optimize(stmt);
2344 }
2345 }
2346 JsStmt::Return(expr, ..) => {
2347 if let Some(expr) = expr {
2348 ExprOptimizer::optimize(expr);
2349 }
2350 }
2351 JsStmt::If { test, consequent, alternate, .. } => {
2352 ExprOptimizer::optimize(test);
2353 Self::optimize(consequent);
2354 if let Some(alt) = alternate {
2355 Self::optimize(alt);
2356 }
2357 if let JsExpr::Literal(test_val, _, _) = test {
2359 if let Some(b) = test_val.as_bool() {
2360 if b {
2361 *stmt = *consequent.clone();
2362 }
2363 else if let Some(alt) = alternate {
2364 *stmt = *alt.clone();
2365 }
2366 else {
2367 *stmt = JsStmt::Block(vec![], Span::unknown(), Trivia::new());
2368 }
2369 }
2370 }
2371 }
2372 JsStmt::While { test, body, .. } => {
2373 ExprOptimizer::optimize(test);
2374 Self::optimize(body);
2375 if let JsExpr::Literal(test_val, _, _) = test {
2377 if let Some(b) = test_val.as_bool() {
2378 if !b {
2379 *stmt = JsStmt::Block(vec![], Span::unknown(), Trivia::new());
2380 }
2381 }
2382 }
2383 }
2384 JsStmt::For { init, test, update, body, .. } => {
2385 if let Some(init_stmt) = init {
2386 Self::optimize(init_stmt);
2387 }
2388 if let Some(test_expr) = test {
2389 ExprOptimizer::optimize(test_expr);
2390 }
2391 if let Some(update_expr) = update {
2392 ExprOptimizer::optimize(update_expr);
2393 }
2394 Self::optimize(body);
2395 }
2396 JsStmt::ForIn { left, right, body, .. } => {
2397 Self::optimize(left);
2398 ExprOptimizer::optimize(right);
2399 Self::optimize(body);
2400 }
2401 JsStmt::ForOf { left, right, body, .. } => {
2402 Self::optimize(left);
2403 ExprOptimizer::optimize(right);
2404 Self::optimize(body);
2405 }
2406 JsStmt::Try { block, handler, finalizer, .. } => {
2407 Self::optimize(block);
2408 if let Some((_, body)) = handler {
2409 Self::optimize(body);
2410 }
2411 if let Some(body) = finalizer {
2412 Self::optimize(body);
2413 }
2414 }
2415 JsStmt::Switch { discriminant, cases, .. } => {
2416 ExprOptimizer::optimize(discriminant);
2417 for (test, stmts) in cases {
2418 if let Some(test_expr) = test {
2419 ExprOptimizer::optimize(test_expr);
2420 }
2421 for stmt in stmts {
2422 Self::optimize(stmt);
2423 }
2424 }
2425 }
2426 JsStmt::Throw(expr, ..) => {
2427 ExprOptimizer::optimize(expr);
2428 }
2429 JsStmt::Block(stmts, ..) => {
2430 let mut optimized_stmts = vec![];
2432 for stmt in &mut *stmts {
2433 let mut optimized_stmt = stmt.clone();
2434 Self::optimize(&mut optimized_stmt);
2435 if let JsStmt::Block(inner_stmts, _, _) = optimized_stmt {
2437 if !inner_stmts.is_empty() {
2438 optimized_stmts.extend(inner_stmts);
2439 }
2440 }
2441 else if let JsStmt::Expr(expr, _, _) = &optimized_stmt {
2442 if !matches!(expr, JsExpr::Identifier(_, _, _)) {
2444 optimized_stmts.push(optimized_stmt);
2445 }
2446 }
2447 else {
2448 optimized_stmts.push(optimized_stmt);
2449 }
2450 }
2451 *stmts = optimized_stmts;
2452 }
2453 _ => {}
2454 }
2455 }
2456 }
2457
2458 pub struct ProgramOptimizer;
2462
2463 impl ProgramOptimizer {
2464 pub fn optimize(program: &mut JsProgram) {
2469 let mut optimized_body = vec![];
2470 for stmt in &program.body {
2471 let mut optimized_stmt = stmt.clone();
2472 StmtOptimizer::optimize(&mut optimized_stmt);
2473 if let JsStmt::Block(inner_stmts, _, _) = optimized_stmt {
2475 if !inner_stmts.is_empty() {
2476 optimized_body.extend(inner_stmts);
2477 }
2478 }
2479 else {
2480 optimized_body.push(optimized_stmt);
2481 }
2482 }
2483 program.body = optimized_body;
2484 }
2485 }
2486
2487 pub struct IROptimizer;
2491
2492 impl IROptimizer {
2493 pub fn optimize(module: &mut IRModule) {
2498 if let Some(script) = &mut module.script {
2499 ProgramOptimizer::optimize(script);
2500 }
2501 if let Some(script_server) = &mut module.script_server {
2502 ProgramOptimizer::optimize(script_server);
2503 }
2504 if let Some(script_client) = &mut module.script_client {
2505 ProgramOptimizer::optimize(script_client);
2506 }
2507 }
2508 }
2509}
2510
2511pub mod validator {
2515 use super::*;
2516
2517 #[derive(Debug, Clone, PartialEq)]
2521 pub enum TypeInfo {
2522 Number,
2524 String,
2526 Boolean,
2528 Object,
2530 Array,
2532 Function,
2534 Undefined,
2536 Null,
2538 Any,
2540 }
2541
2542 pub struct TypeEnvironment {
2544 variables: HashMap<String, TypeInfo>,
2546 }
2547
2548 impl TypeEnvironment {
2549 pub fn new() -> Self {
2551 Self { variables: HashMap::new() }
2552 }
2553
2554 pub fn add_variable(&mut self, name: String, type_info: TypeInfo) {
2556 self.variables.insert(name, type_info);
2557 }
2558
2559 pub fn get_variable(&self, name: &String) -> Option<&TypeInfo> {
2561 self.variables.get(name)
2562 }
2563 }
2564
2565 pub struct ExprValidator {
2567 env: TypeEnvironment,
2569 }
2570
2571 impl ExprValidator {
2572 pub fn new() -> Self {
2574 Self { env: TypeEnvironment::new() }
2575 }
2576
2577 pub fn validate(&mut self, expr: &JsExpr) -> Result<TypeInfo> {
2579 match expr {
2580 JsExpr::Identifier(id, _span, _) => {
2581 if let Some(type_info) = self.env.get_variable(id) {
2582 Ok(type_info.clone())
2583 }
2584 else {
2585 Err(IRError::InvalidExpression(format!("Undefined variable: {}", id)).into())
2586 }
2587 }
2588 JsExpr::Literal(value, _, _) => Ok(match value {
2589 NargoValue::Number(_) => TypeInfo::Number,
2590 NargoValue::String(_) => TypeInfo::String,
2591 NargoValue::Bool(_) => TypeInfo::Boolean,
2592 NargoValue::Object(_) => TypeInfo::Object,
2593 NargoValue::Array(_) => TypeInfo::Array,
2594 NargoValue::Null => TypeInfo::Null,
2595 _ => TypeInfo::Any,
2596 }),
2597 JsExpr::Unary { op, argument, span: _, .. } => {
2598 let arg_type = self.validate(argument)?;
2599 match op.as_str() {
2600 "!" => Ok(TypeInfo::Boolean),
2601 "-" | "+" | "~" => {
2602 if matches!(arg_type, TypeInfo::Number) {
2603 Ok(TypeInfo::Number)
2604 }
2605 else {
2606 Err(IRError::InvalidExpression(format!("Unary operator {} requires number type", op)).into())
2607 }
2608 }
2609 _ => Ok(TypeInfo::Any),
2610 }
2611 }
2612 JsExpr::Binary { left, op, right, span: _, .. } => {
2613 let left_type = self.validate(left)?;
2614 let right_type = self.validate(right)?;
2615 match op.as_str() {
2616 "+" => {
2617 if matches!(left_type, TypeInfo::Number) && matches!(right_type, TypeInfo::Number) {
2618 Ok(TypeInfo::Number)
2619 }
2620 else if matches!(left_type, TypeInfo::String) || matches!(right_type, TypeInfo::String) {
2621 Ok(TypeInfo::String)
2622 }
2623 else {
2624 Err(IRError::InvalidExpression(format!("Addition operator requires number or string types").to_string()).into())
2625 }
2626 }
2627 "-" | "*" | "/" | "%" => {
2628 if matches!(left_type, TypeInfo::Number) && matches!(right_type, TypeInfo::Number) {
2629 Ok(TypeInfo::Number)
2630 }
2631 else {
2632 Err(IRError::InvalidExpression(format!("Arithmetic operator {} requires number types", op)).into())
2633 }
2634 }
2635 "==" | "!=" | "===" | "!==" => Ok(TypeInfo::Boolean),
2636 "<" | "<=" | ">" | ">=" => {
2637 if matches!(left_type, TypeInfo::Number) && matches!(right_type, TypeInfo::Number) {
2638 Ok(TypeInfo::Boolean)
2639 }
2640 else if matches!(left_type, TypeInfo::String) && matches!(right_type, TypeInfo::String) {
2641 Ok(TypeInfo::Boolean)
2642 }
2643 else {
2644 Err(IRError::InvalidExpression(format!("Comparison operator {} requires number or string types", op)).into())
2645 }
2646 }
2647 "&&" | "||" => Ok(TypeInfo::Boolean),
2648 "&" | "|" | "^" | "<<" | ">>" | ">>>" => {
2649 if matches!(left_type, TypeInfo::Number) && matches!(right_type, TypeInfo::Number) {
2650 Ok(TypeInfo::Number)
2651 }
2652 else {
2653 Err(IRError::InvalidExpression(format!("Bitwise operator {} requires number types", op)).into())
2654 }
2655 }
2656 "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | ">>>=" => Ok(left_type),
2657 _ => Ok(TypeInfo::Any),
2658 }
2659 }
2660 JsExpr::Call { callee, args, span: _, .. } => {
2661 let callee_type = self.validate(callee)?;
2662 if matches!(callee_type, TypeInfo::Function) {
2663 for arg in args {
2665 self.validate(arg)?;
2666 }
2667 Ok(TypeInfo::Any)
2668 }
2669 else {
2670 Err(IRError::InvalidExpression("Callee must be a function".to_string()).into())
2671 }
2672 }
2673 JsExpr::Member { object, property, .. } => {
2674 self.validate(object)?;
2675 self.validate(property)?;
2676 Ok(TypeInfo::Any)
2677 }
2678 JsExpr::OptionalMember { object, property, .. } => {
2679 self.validate(object)?;
2680 self.validate(property)?;
2681 Ok(TypeInfo::Any)
2682 }
2683 JsExpr::OptionalCall { callee, args, .. } => {
2684 self.validate(callee)?;
2685 for arg in args {
2686 self.validate(arg)?;
2687 }
2688 Ok(TypeInfo::Any)
2689 }
2690 JsExpr::NullishCoalescing { left, right, .. } => {
2691 self.validate(left)?;
2692 self.validate(right)?;
2693 Ok(TypeInfo::Any)
2694 }
2695 JsExpr::LogicalAssignment { left, right, .. } => {
2696 self.validate(left)?;
2697 self.validate(right)?;
2698 Ok(TypeInfo::Any)
2699 }
2700 JsExpr::Array(items, ..) => {
2701 for item in items {
2702 self.validate(item)?;
2703 }
2704 Ok(TypeInfo::Array)
2705 }
2706 JsExpr::Object(props, ..) => {
2707 for (_, value) in props {
2708 self.validate(value)?;
2709 }
2710 Ok(TypeInfo::Object)
2711 }
2712 JsExpr::ArrowFunction { params, body, .. } => {
2713 for param in params {
2715 self.env.add_variable(param.clone(), TypeInfo::Any);
2716 }
2717 self.validate(body)?;
2718 Ok(TypeInfo::Function)
2719 }
2720 JsExpr::TseElement { children, .. } => {
2721 for child in children {
2722 self.validate(child)?;
2723 }
2724 Ok(TypeInfo::Any)
2725 }
2726 JsExpr::Conditional { test, consequent, alternate, .. } => {
2727 let test_type = self.validate(test)?;
2728 if !matches!(test_type, TypeInfo::Boolean) {
2729 return Err(IRError::InvalidExpression("Condition must be a boolean".to_string()).into());
2730 }
2731 let _consequent_type = self.validate(consequent)?;
2732 let _alternate_type = self.validate(alternate)?;
2733 Ok(TypeInfo::Any)
2735 }
2736 JsExpr::TemplateLiteral { expressions, .. } => {
2737 for expr in expressions {
2738 self.validate(expr)?;
2739 }
2740 Ok(TypeInfo::String)
2741 }
2742 JsExpr::Spread(expr, ..) => {
2743 self.validate(expr)?;
2744 Ok(TypeInfo::Any)
2745 }
2746 JsExpr::TypeOf(expr, ..) => {
2747 self.validate(expr)?;
2748 Ok(TypeInfo::String)
2749 }
2750 JsExpr::InstanceOf { left, right, .. } => {
2751 self.validate(left)?;
2752 self.validate(right)?;
2753 Ok(TypeInfo::Boolean)
2754 }
2755 JsExpr::Other(_, _, _) => Ok(TypeInfo::Any),
2756 }
2757 }
2758 }
2759
2760 pub struct StmtValidator {
2762 expr_validator: ExprValidator,
2764 }
2765
2766 impl StmtValidator {
2767 pub fn new() -> Self {
2769 Self { expr_validator: ExprValidator::new() }
2770 }
2771
2772 pub fn validate(&mut self, stmt: &JsStmt) -> Result<()> {
2774 match stmt {
2775 JsStmt::Expr(expr, ..) => {
2776 self.expr_validator.validate(expr)?;
2777 Ok(())
2778 }
2779 JsStmt::VariableDecl { kind: _, id, init, .. } => {
2780 if let Some(expr) = init {
2781 let type_info = self.expr_validator.validate(expr)?;
2782 self.expr_validator.env.add_variable(id.clone(), type_info);
2783 }
2784 else {
2785 self.expr_validator.env.add_variable(id.clone(), TypeInfo::Any);
2787 }
2788 Ok(())
2789 }
2790 JsStmt::Import { .. } => Ok(()),
2791 JsStmt::Export { declaration, .. } => self.validate(declaration),
2792 JsStmt::ExportAll { .. } => Ok(()),
2793 JsStmt::ExportNamed { .. } => Ok(()),
2794 JsStmt::FunctionDecl { id, params, body, .. } => {
2795 self.expr_validator.env.add_variable(id.clone(), TypeInfo::Function);
2797 for param in params {
2799 self.expr_validator.env.add_variable(param.clone(), TypeInfo::Any);
2800 }
2801 for stmt in body {
2803 self.validate(stmt)?;
2804 }
2805 Ok(())
2806 }
2807 JsStmt::Return(expr, ..) => {
2808 if let Some(expr) = expr {
2809 self.expr_validator.validate(expr)?;
2810 }
2811 Ok(())
2812 }
2813 JsStmt::If { test, consequent, alternate, .. } => {
2814 let test_type = self.expr_validator.validate(test)?;
2815 if !matches!(test_type, TypeInfo::Boolean) {
2816 return Err(IRError::InvalidExpression("Condition must be a boolean".to_string()).into());
2817 }
2818 self.validate(consequent)?;
2819 if let Some(alt) = alternate {
2820 self.validate(alt)?;
2821 }
2822 Ok(())
2823 }
2824 JsStmt::While { test, body, .. } => {
2825 let test_type = self.expr_validator.validate(test)?;
2826 if !matches!(test_type, TypeInfo::Boolean) {
2827 return Err(IRError::InvalidExpression("Condition must be a boolean".to_string()).into());
2828 }
2829 self.validate(body)?;
2830 Ok(())
2831 }
2832 JsStmt::For { init, test, update, body, .. } => {
2833 if let Some(init_stmt) = init {
2834 self.validate(init_stmt)?;
2835 }
2836 if let Some(test_expr) = test {
2837 let test_type = self.expr_validator.validate(test_expr)?;
2838 if !matches!(test_type, TypeInfo::Boolean) {
2839 return Err(IRError::InvalidExpression("Condition must be a boolean".to_string()).into());
2840 }
2841 }
2842 if let Some(update_expr) = update {
2843 self.expr_validator.validate(update_expr)?;
2844 }
2845 self.validate(body)?;
2846 Ok(())
2847 }
2848 JsStmt::ForIn { left, right, body, .. } => {
2849 self.validate(left)?;
2850 self.expr_validator.validate(right)?;
2851 self.validate(body)?;
2852 Ok(())
2853 }
2854 JsStmt::ForOf { left, right, body, .. } => {
2855 self.validate(left)?;
2856 self.expr_validator.validate(right)?;
2857 self.validate(body)?;
2858 Ok(())
2859 }
2860 JsStmt::Try { block, handler, finalizer, .. } => {
2861 self.validate(block)?;
2862 if let Some((id, body)) = handler {
2863 self.expr_validator.env.add_variable(id.clone(), TypeInfo::Any);
2865 self.validate(body)?;
2866 }
2867 if let Some(body) = finalizer {
2868 self.validate(body)?;
2869 }
2870 Ok(())
2871 }
2872 JsStmt::Switch { discriminant, cases, .. } => {
2873 self.expr_validator.validate(discriminant)?;
2874 for (test, stmts) in cases {
2875 if let Some(test_expr) = test {
2876 self.expr_validator.validate(test_expr)?;
2877 }
2878 for stmt in stmts {
2879 self.validate(stmt)?;
2880 }
2881 }
2882 Ok(())
2883 }
2884 JsStmt::Throw(expr, ..) => {
2885 self.expr_validator.validate(expr)?;
2886 Ok(())
2887 }
2888 JsStmt::Block(stmts, ..) => {
2889 for stmt in stmts {
2890 self.validate(stmt)?;
2891 }
2892 Ok(())
2893 }
2894 JsStmt::Break(_, _) => Ok(()),
2895 JsStmt::Continue(_, _) => Ok(()),
2896 JsStmt::Other(_, _, _) => Ok(()),
2897 }
2898 }
2899 }
2900
2901 pub struct ProgramValidator {
2903 stmt_validator: StmtValidator,
2905 }
2906
2907 impl ProgramValidator {
2908 pub fn new() -> Self {
2910 Self { stmt_validator: StmtValidator::new() }
2911 }
2912
2913 pub fn validate(&mut self, program: &JsProgram) -> Result<()> {
2915 for stmt in &program.body {
2916 self.stmt_validator.validate(stmt)?;
2917 }
2918 Ok(())
2919 }
2920 }
2921
2922 pub struct IRValidator {
2924 program_validator: ProgramValidator,
2926 }
2927
2928 impl IRValidator {
2929 pub fn new() -> Self {
2931 Self { program_validator: ProgramValidator::new() }
2932 }
2933
2934 pub fn validate(&mut self, module: &IRModule) -> Result<()> {
2936 module.validate()?;
2938
2939 if let Some(script) = &module.script {
2941 self.program_validator.validate(script)?;
2942 }
2943 if let Some(script_server) = &module.script_server {
2944 self.program_validator.validate(script_server)?;
2945 }
2946 if let Some(script_client) = &module.script_client {
2947 self.program_validator.validate(script_client)?;
2948 }
2949
2950 Ok(())
2951 }
2952 }
2953}