1#![warn(missing_docs)]
2
3use nargo_types::{NargoValue, Result, Span};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11use crate::types::{MAX_ARRAY_LENGTH, MAX_OBJECT_SIZE, MAX_RECURSION_DEPTH, MAX_STRING_LENGTH, IRError, Trivia};
12
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15pub struct TseAttribute {
16 pub name: String,
18 pub value: Option<JsExpr>,
20 pub is_directive: bool,
22 #[serde(default)]
24 pub span: Span,
25 #[serde(default)]
27 pub trivia: Trivia,
28}
29
30impl TseAttribute {
31 pub fn validate(&self, depth: usize) -> Result<()> {
33 if self.name.len() > MAX_STRING_LENGTH {
34 return Err(IRError::SizeLimitExceeded("TSE attribute name length exceeded".to_string()).into());
35 }
36 if let Some(value) = &self.value {
37 value.validate(depth + 1)?;
38 }
39 Ok(())
40 }
41}
42
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
45pub enum JsExpr {
46 Identifier(String, #[serde(default)] Span, #[serde(default)] Trivia),
48 Literal(NargoValue, #[serde(default)] Span, #[serde(default)] Trivia),
50 Unary {
52 op: String,
54 argument: Box<JsExpr>,
56 #[serde(default)]
58 span: Span,
59 #[serde(default)]
61 trivia: Trivia,
62 },
63 Binary {
65 left: Box<JsExpr>,
67 op: String,
69 right: Box<JsExpr>,
71 #[serde(default)]
73 span: Span,
74 #[serde(default)]
76 trivia: Trivia,
77 },
78 Call {
80 callee: Box<JsExpr>,
82 args: Vec<JsExpr>,
84 #[serde(default)]
86 span: Span,
87 #[serde(default)]
89 trivia: Trivia,
90 },
91 Member {
93 object: Box<JsExpr>,
95 property: Box<JsExpr>,
97 computed: bool,
99 #[serde(default)]
101 span: Span,
102 #[serde(default)]
104 trivia: Trivia,
105 },
106 OptionalMember {
108 object: Box<JsExpr>,
110 property: Box<JsExpr>,
112 computed: bool,
114 #[serde(default)]
116 span: Span,
117 #[serde(default)]
119 trivia: Trivia,
120 },
121 OptionalCall {
123 callee: Box<JsExpr>,
125 args: Vec<JsExpr>,
127 #[serde(default)]
129 span: Span,
130 #[serde(default)]
132 trivia: Trivia,
133 },
134 NullishCoalescing {
136 left: Box<JsExpr>,
138 right: Box<JsExpr>,
140 #[serde(default)]
142 span: Span,
143 #[serde(default)]
145 trivia: Trivia,
146 },
147 LogicalAssignment {
149 op: String,
151 left: Box<JsExpr>,
153 right: Box<JsExpr>,
155 #[serde(default)]
157 span: Span,
158 #[serde(default)]
160 trivia: Trivia,
161 },
162 Array(Vec<JsExpr>, #[serde(default)] Span, #[serde(default)] Trivia),
164 Object(HashMap<String, JsExpr>, #[serde(default)] Span, #[serde(default)] Trivia),
166 ArrowFunction {
168 params: Vec<String>,
170 body: Box<JsExpr>,
172 #[serde(default)]
174 span: Span,
175 #[serde(default)]
177 trivia: Trivia,
178 },
179 TseElement {
181 tag: String,
183 attributes: Vec<TseAttribute>,
185 children: Vec<JsExpr>,
187 #[serde(default)]
189 span: Span,
190 #[serde(default)]
192 trivia: Trivia,
193 },
194 Conditional {
196 test: Box<JsExpr>,
198 consequent: Box<JsExpr>,
200 alternate: Box<JsExpr>,
202 #[serde(default)]
204 span: Span,
205 #[serde(default)]
207 trivia: Trivia,
208 },
209 TemplateLiteral {
211 quasis: Vec<String>,
213 expressions: Vec<JsExpr>,
215 #[serde(default)]
217 span: Span,
218 #[serde(default)]
220 trivia: Trivia,
221 },
222 Spread(Box<JsExpr>, #[serde(default)] Span, #[serde(default)] Trivia),
224 TypeOf(Box<JsExpr>, #[serde(default)] Span, #[serde(default)] Trivia),
226 InstanceOf {
228 left: Box<JsExpr>,
230 right: Box<JsExpr>,
232 #[serde(default)]
234 span: Span,
235 #[serde(default)]
237 trivia: Trivia,
238 },
239 Other(String, #[serde(default)] Span, #[serde(default)] Trivia),
241}
242
243impl JsExpr {
244 pub fn span(&self) -> Span {
246 match self {
247 JsExpr::Identifier(_, span, _) => *span,
248 JsExpr::Literal(_, span, _) => *span,
249 JsExpr::Unary { span, .. } => *span,
250 JsExpr::Binary { span, .. } => *span,
251 JsExpr::Call { span, .. } => *span,
252 JsExpr::Member { span, .. } => *span,
253 JsExpr::OptionalMember { span, .. } => *span,
254 JsExpr::OptionalCall { span, .. } => *span,
255 JsExpr::NullishCoalescing { span, .. } => *span,
256 JsExpr::LogicalAssignment { span, .. } => *span,
257 JsExpr::Array(_, span, _) => *span,
258 JsExpr::Object(_, span, _) => *span,
259 JsExpr::ArrowFunction { span, .. } => *span,
260 JsExpr::TseElement { span, .. } => *span,
261 JsExpr::Conditional { span, .. } => *span,
262 JsExpr::TemplateLiteral { span, .. } => *span,
263 JsExpr::Spread(_, span, _) => *span,
264 JsExpr::TypeOf(_, span, _) => *span,
265 JsExpr::InstanceOf { span, .. } => *span,
266 JsExpr::Other(_, span, _) => *span,
267 }
268 }
269
270 pub fn trivia(&self) -> &Trivia {
272 match self {
273 JsExpr::Identifier(_, _, t) => t,
274 JsExpr::Literal(_, _, t) => t,
275 JsExpr::Unary { trivia, .. } => trivia,
276 JsExpr::Binary { trivia, .. } => trivia,
277 JsExpr::Call { trivia, .. } => trivia,
278 JsExpr::Member { trivia, .. } => trivia,
279 JsExpr::OptionalMember { trivia, .. } => trivia,
280 JsExpr::OptionalCall { trivia, .. } => trivia,
281 JsExpr::NullishCoalescing { trivia, .. } => trivia,
282 JsExpr::LogicalAssignment { trivia, .. } => trivia,
283 JsExpr::Array(_, _, t) => t,
284 JsExpr::Object(_, _, t) => t,
285 JsExpr::ArrowFunction { trivia, .. } => trivia,
286 JsExpr::TseElement { trivia, .. } => trivia,
287 JsExpr::Conditional { trivia, .. } => trivia,
288 JsExpr::TemplateLiteral { trivia, .. } => trivia,
289 JsExpr::Spread(_, _, t) => t,
290 JsExpr::TypeOf(_, _, t) => t,
291 JsExpr::InstanceOf { trivia, .. } => trivia,
292 JsExpr::Other(_, _, t) => t,
293 }
294 }
295
296 pub fn validate(&self, depth: usize) -> Result<()> {
298 if depth > MAX_RECURSION_DEPTH {
299 return Err(IRError::CircularReference("Expression recursion depth exceeded".to_string()).into());
300 }
301
302 match self {
303 JsExpr::Identifier(id, _, _) => {
304 if id.len() > MAX_STRING_LENGTH {
305 return Err(IRError::SizeLimitExceeded("Identifier length exceeded".to_string()).into());
306 }
307 Ok(())
308 }
309 JsExpr::Literal(value, _, _) => value.validate(depth + 1),
310 JsExpr::Unary { argument, .. } => argument.validate(depth + 1),
311 JsExpr::Binary { left, right, .. } => {
312 left.validate(depth + 1)?;
313 right.validate(depth + 1)
314 }
315 JsExpr::Call { callee, args, .. } => {
316 callee.validate(depth + 1)?;
317 if args.len() > MAX_ARRAY_LENGTH {
318 return Err(IRError::SizeLimitExceeded("Call arguments length exceeded".to_string()).into());
319 }
320 for arg in args {
321 arg.validate(depth + 1)?;
322 }
323 Ok(())
324 }
325 JsExpr::Member { object, property, .. } => {
326 object.validate(depth + 1)?;
327 property.validate(depth + 1)
328 }
329 JsExpr::OptionalMember { object, property, .. } => {
330 object.validate(depth + 1)?;
331 property.validate(depth + 1)
332 }
333 JsExpr::OptionalCall { callee, args, .. } => {
334 callee.validate(depth + 1)?;
335 if args.len() > MAX_ARRAY_LENGTH {
336 return Err(IRError::SizeLimitExceeded("Optional call arguments length exceeded".to_string()).into());
337 }
338 for arg in args {
339 arg.validate(depth + 1)?;
340 }
341 Ok(())
342 }
343 JsExpr::NullishCoalescing { left, right, .. } => {
344 left.validate(depth + 1)?;
345 right.validate(depth + 1)
346 }
347 JsExpr::LogicalAssignment { op, left, right, .. } => {
348 if op.len() > MAX_STRING_LENGTH {
349 return Err(IRError::SizeLimitExceeded("Logical assignment operator length exceeded".to_string()).into());
350 }
351 left.validate(depth + 1)?;
352 right.validate(depth + 1)
353 }
354 JsExpr::Array(items, _, _) => {
355 if items.len() > MAX_ARRAY_LENGTH {
356 return Err(IRError::SizeLimitExceeded("Array length exceeded".to_string()).into());
357 }
358 for item in items {
359 item.validate(depth + 1)?;
360 }
361 Ok(())
362 }
363 JsExpr::Object(props, _, _) => {
364 if props.len() > MAX_OBJECT_SIZE {
365 return Err(IRError::SizeLimitExceeded("Object size exceeded".to_string()).into());
366 }
367 for (key, value) in props {
368 if key.len() > MAX_STRING_LENGTH {
369 return Err(IRError::SizeLimitExceeded("Object key length exceeded".to_string()).into());
370 }
371 value.validate(depth + 1)?;
372 }
373 Ok(())
374 }
375 JsExpr::ArrowFunction { params, body, .. } => {
376 if params.len() > MAX_ARRAY_LENGTH {
377 return Err(IRError::SizeLimitExceeded("Arrow function parameters length exceeded".to_string()).into());
378 }
379 for param in params {
380 if param.len() > MAX_STRING_LENGTH {
381 return Err(IRError::SizeLimitExceeded("Parameter name length exceeded".to_string()).into());
382 }
383 }
384 body.validate(depth + 1)
385 }
386 JsExpr::TseElement { tag, attributes, children, .. } => {
387 if tag.len() > MAX_STRING_LENGTH {
388 return Err(IRError::SizeLimitExceeded("TSE element tag length exceeded".to_string()).into());
389 }
390 if attributes.len() > MAX_ARRAY_LENGTH {
391 return Err(IRError::SizeLimitExceeded("TSE element attributes length exceeded".to_string()).into());
392 }
393 for attr in attributes {
394 attr.validate(depth + 1)?;
395 }
396 if children.len() > MAX_ARRAY_LENGTH {
397 return Err(IRError::SizeLimitExceeded("TSE element children length exceeded".to_string()).into());
398 }
399 for child in children {
400 child.validate(depth + 1)?;
401 }
402 Ok(())
403 }
404 JsExpr::Conditional { test, consequent, alternate, .. } => {
405 test.validate(depth + 1)?;
406 consequent.validate(depth + 1)?;
407 alternate.validate(depth + 1)
408 }
409 JsExpr::TemplateLiteral { quasis, expressions, .. } => {
410 if quasis.len() > MAX_ARRAY_LENGTH {
411 return Err(IRError::SizeLimitExceeded("Template literal quasis length exceeded".to_string()).into());
412 }
413 for quasi in quasis {
414 if quasi.len() > MAX_STRING_LENGTH {
415 return Err(IRError::SizeLimitExceeded("Template literal quasi length exceeded".to_string()).into());
416 }
417 }
418 if expressions.len() > MAX_ARRAY_LENGTH {
419 return Err(IRError::SizeLimitExceeded("Template literal expressions length exceeded".to_string()).into());
420 }
421 for expr in expressions {
422 expr.validate(depth + 1)?;
423 }
424 Ok(())
425 }
426 JsExpr::Spread(expr, _, _) => expr.validate(depth + 1),
427 JsExpr::TypeOf(expr, _, _) => expr.validate(depth + 1),
428 JsExpr::InstanceOf { left, right, .. } => {
429 left.validate(depth + 1)?;
430 right.validate(depth + 1)
431 }
432 JsExpr::Other(code, _, _) => {
433 if code.len() > MAX_STRING_LENGTH {
434 return Err(IRError::SizeLimitExceeded("Other expression code length exceeded".to_string()).into());
435 }
436 Ok(())
437 }
438 }
439 }
440
441 pub fn optimize(&mut self) {
443 crate::optimizer::ExprOptimizer::optimize(self);
444 }
445
446 pub fn is_constant(&self) -> bool {
448 match self {
449 JsExpr::Literal(_, _, _) => true,
450 JsExpr::Unary { argument, .. } => argument.is_constant(),
451 JsExpr::Binary { left, right, .. } => left.is_constant() && right.is_constant(),
452 _ => false,
453 }
454 }
455}
456
457pub trait JsExprVisitor<R> {
459 fn visit_identifier(&mut self, id: &String, span: &Span, trivia: &Trivia) -> R;
461 fn visit_literal(&mut self, value: &NargoValue, span: &Span, trivia: &Trivia) -> R;
463 fn visit_unary(&mut self, op: &String, argument: &JsExpr, span: &Span, trivia: &Trivia) -> R;
465 fn visit_binary(&mut self, left: &JsExpr, op: &String, right: &JsExpr, span: &Span, trivia: &Trivia) -> R;
467 fn visit_call(&mut self, callee: &JsExpr, args: &Vec<JsExpr>, span: &Span, trivia: &Trivia) -> R;
469 fn visit_member(&mut self, object: &JsExpr, property: &JsExpr, computed: bool, span: &Span, trivia: &Trivia) -> R;
471 fn visit_optional_member(&mut self, object: &JsExpr, property: &JsExpr, computed: bool, span: &Span, trivia: &Trivia) -> R;
473 fn visit_optional_call(&mut self, callee: &JsExpr, args: &Vec<JsExpr>, span: &Span, trivia: &Trivia) -> R;
475 fn visit_nullish_coalescing(&mut self, left: &JsExpr, right: &JsExpr, span: &Span, trivia: &Trivia) -> R;
477 fn visit_logical_assignment(&mut self, op: &String, left: &JsExpr, right: &JsExpr, span: &Span, trivia: &Trivia) -> R;
479 fn visit_array(&mut self, items: &Vec<JsExpr>, span: &Span, trivia: &Trivia) -> R;
481 fn visit_object(&mut self, props: &HashMap<String, JsExpr>, span: &Span, trivia: &Trivia) -> R;
483 fn visit_arrow_function(&mut self, params: &Vec<String>, body: &JsExpr, span: &Span, trivia: &Trivia) -> R;
485 fn visit_tse_element(&mut self, tag: &String, attributes: &Vec<TseAttribute>, children: &Vec<JsExpr>, span: &Span, trivia: &Trivia) -> R;
487 fn visit_conditional(&mut self, test: &JsExpr, consequent: &JsExpr, alternate: &JsExpr, span: &Span, trivia: &Trivia) -> R;
489 fn visit_template_literal(&mut self, quasis: &Vec<String>, expressions: &Vec<JsExpr>, span: &Span, trivia: &Trivia) -> R;
491 fn visit_spread(&mut self, expr: &JsExpr, span: &Span, trivia: &Trivia) -> R;
493 fn visit_type_of(&mut self, expr: &JsExpr, span: &Span, trivia: &Trivia) -> R;
495 fn visit_instance_of(&mut self, left: &JsExpr, right: &JsExpr, span: &Span, trivia: &Trivia) -> R;
497 fn visit_other(&mut self, code: &String, span: &Span, trivia: &Trivia) -> R;
499}
500
501impl JsExpr {
502 pub fn accept<R>(&self, visitor: &mut dyn JsExprVisitor<R>) -> R {
504 match self {
505 JsExpr::Identifier(id, span, trivia) => visitor.visit_identifier(id, span, trivia),
506 JsExpr::Literal(value, span, trivia) => visitor.visit_literal(value, span, trivia),
507 JsExpr::Unary { op, argument, span, trivia } => visitor.visit_unary(op, argument, span, trivia),
508 JsExpr::Binary { left, op, right, span, trivia } => visitor.visit_binary(left, op, right, span, trivia),
509 JsExpr::Call { callee, args, span, trivia } => visitor.visit_call(callee, args, span, trivia),
510 JsExpr::Member { object, property, computed, span, trivia } => visitor.visit_member(object, property, *computed, span, trivia),
511 JsExpr::OptionalMember { object, property, computed, span, trivia } => visitor.visit_optional_member(object, property, *computed, span, trivia),
512 JsExpr::OptionalCall { callee, args, span, trivia } => visitor.visit_optional_call(callee, args, span, trivia),
513 JsExpr::NullishCoalescing { left, right, span, trivia } => visitor.visit_nullish_coalescing(left, right, span, trivia),
514 JsExpr::LogicalAssignment { op, left, right, span, trivia } => visitor.visit_logical_assignment(op, left, right, span, trivia),
515 JsExpr::Array(items, span, trivia) => visitor.visit_array(items, span, trivia),
516 JsExpr::Object(props, span, trivia) => visitor.visit_object(props, span, trivia),
517 JsExpr::ArrowFunction { params, body, span, trivia } => visitor.visit_arrow_function(params, body, span, trivia),
518 JsExpr::TseElement { tag, attributes, children, span, trivia } => visitor.visit_tse_element(tag, attributes, children, span, trivia),
519 JsExpr::Conditional { test, consequent, alternate, span, trivia } => visitor.visit_conditional(test, consequent, alternate, span, trivia),
520 JsExpr::TemplateLiteral { quasis, expressions, span, trivia } => visitor.visit_template_literal(quasis, expressions, span, trivia),
521 JsExpr::Spread(expr, span, trivia) => visitor.visit_spread(expr, span, trivia),
522 JsExpr::TypeOf(expr, span, trivia) => visitor.visit_type_of(expr, span, trivia),
523 JsExpr::InstanceOf { left, right, span, trivia } => visitor.visit_instance_of(left, right, span, trivia),
524 JsExpr::Other(code, span, trivia) => visitor.visit_other(code, span, trivia),
525 }
526 }
527}