Skip to main content

nargo_ir/
stmt.rs

1#![warn(missing_docs)]
2
3//! 语句模块
4//! 
5//! 提供 JavaScript 语句的 IR 表示和相关功能。
6
7use nargo_types::{Result, Span};
8use serde::{Deserialize, Serialize};
9
10use crate::expr::JsExpr;
11use crate::types::{MAX_ARRAY_LENGTH, MAX_RECURSION_DEPTH, MAX_STRING_LENGTH, IRError, Trivia};
12
13/// JavaScript 语句
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15pub enum JsStmt {
16    /// 表达式语句
17    Expr(JsExpr, #[serde(default)] Span, #[serde(default)] Trivia),
18    /// 变量声明
19    VariableDecl {
20        /// 声明类型(var, let, const)
21        kind: String,
22        /// 变量名
23        id: String,
24        /// 初始值
25        init: Option<JsExpr>,
26        /// 位置信息
27        #[serde(default)]
28        span: Span,
29        /// Trivia 信息
30        #[serde(default)]
31        trivia: Trivia,
32    },
33    /// 导入语句
34    Import {
35        /// 导入源
36        source: String,
37        /// 导入说明符
38        specifiers: Vec<String>,
39        /// 位置信息
40        #[serde(default)]
41        span: Span,
42        /// Trivia 信息
43        #[serde(default)]
44        trivia: Trivia,
45    },
46    /// 导出语句
47    Export {
48        /// 导出声明
49        declaration: Box<JsStmt>,
50        /// 位置信息
51        #[serde(default)]
52        span: Span,
53        /// Trivia 信息
54        #[serde(default)]
55        trivia: Trivia,
56    },
57    /// 导出所有
58    ExportAll {
59        /// 导出源
60        source: String,
61        /// 位置信息
62        #[serde(default)]
63        span: Span,
64        /// Trivia 信息
65        #[serde(default)]
66        trivia: Trivia,
67    },
68    /// 导出命名
69    ExportNamed {
70        /// 导出源
71        source: Option<String>,
72        /// 导出说明符
73        specifiers: Vec<String>,
74        /// 位置信息
75        #[serde(default)]
76        span: Span,
77        /// Trivia 信息
78        #[serde(default)]
79        trivia: Trivia,
80    },
81    /// 函数声明
82    FunctionDecl {
83        /// 函数名
84        id: String,
85        /// 参数列表
86        params: Vec<String>,
87        /// 函数体
88        body: Vec<JsStmt>,
89        /// 是否为异步函数
90        #[serde(default)]
91        is_async: bool,
92        /// 位置信息
93        #[serde(default)]
94        span: Span,
95        /// Trivia 信息
96        #[serde(default)]
97        trivia: Trivia,
98    },
99    /// 返回语句
100    Return(Option<JsExpr>, #[serde(default)] Span, #[serde(default)] Trivia),
101    /// if 语句
102    If {
103        /// 条件
104        test: JsExpr,
105        /// 真分支
106        consequent: Box<JsStmt>,
107        /// 假分支
108        alternate: Option<Box<JsStmt>>,
109        /// 位置信息
110        #[serde(default)]
111        span: Span,
112        /// Trivia 信息
113        #[serde(default)]
114        trivia: Trivia,
115    },
116    /// while 语句
117    While {
118        /// 条件
119        test: JsExpr,
120        /// 循环体
121        body: Box<JsStmt>,
122        /// 位置信息
123        #[serde(default)]
124        span: Span,
125        /// Trivia 信息
126        #[serde(default)]
127        trivia: Trivia,
128    },
129    /// for 语句
130    For {
131        /// 初始化
132        init: Option<Box<JsStmt>>,
133        /// 条件
134        test: Option<JsExpr>,
135        /// 更新
136        update: Option<JsExpr>,
137        /// 循环体
138        body: Box<JsStmt>,
139        /// 位置信息
140        #[serde(default)]
141        span: Span,
142        /// Trivia 信息
143        #[serde(default)]
144        trivia: Trivia,
145    },
146    /// for-in 语句
147    ForIn {
148        /// 变量
149        left: Box<JsStmt>,
150        /// 表达式
151        right: JsExpr,
152        /// 循环体
153        body: Box<JsStmt>,
154        /// 位置信息
155        #[serde(default)]
156        span: Span,
157        /// Trivia 信息
158        #[serde(default)]
159        trivia: Trivia,
160    },
161    /// for-of 语句
162    ForOf {
163        /// 变量
164        left: Box<JsStmt>,
165        /// 表达式
166        right: JsExpr,
167        /// 循环体
168        body: Box<JsStmt>,
169        /// 位置信息
170        #[serde(default)]
171        span: Span,
172        /// Trivia 信息
173        #[serde(default)]
174        trivia: Trivia,
175    },
176    /// try/catch 语句
177    Try {
178        /// try 块
179        block: Box<JsStmt>,
180        /// catch 块
181        handler: Option<(String, Box<JsStmt>)>,
182        /// finally 块
183        finalizer: Option<Box<JsStmt>>,
184        /// 位置信息
185        #[serde(default)]
186        span: Span,
187        /// Trivia 信息
188        #[serde(default)]
189        trivia: Trivia,
190    },
191    /// switch 语句
192    Switch {
193        /// 表达式
194        discriminant: JsExpr,
195        /// 分支
196        cases: Vec<(Option<JsExpr>, Vec<JsStmt>)>,
197        /// 位置信息
198        #[serde(default)]
199        span: Span,
200        /// Trivia 信息
201        #[serde(default)]
202        trivia: Trivia,
203    },
204    /// throw 语句
205    Throw(JsExpr, #[serde(default)] Span, #[serde(default)] Trivia),
206    /// 块语句
207    Block(Vec<JsStmt>, #[serde(default)] Span, #[serde(default)] Trivia),
208    /// break 语句
209    Break(#[serde(default)] Span, #[serde(default)] Trivia),
210    /// continue 语句
211    Continue(#[serde(default)] Span, #[serde(default)] Trivia),
212    /// 其他语句
213    Other(String, #[serde(default)] Span, #[serde(default)] Trivia),
214}
215
216impl JsStmt {
217    /// 获取语句的位置信息
218    pub fn span(&self) -> Span {
219        match self {
220            JsStmt::Expr(_, span, _) => *span,
221            JsStmt::VariableDecl { span, .. } => *span,
222            JsStmt::Import { span, .. } => *span,
223            JsStmt::Export { span, .. } => *span,
224            JsStmt::ExportAll { span, .. } => *span,
225            JsStmt::ExportNamed { span, .. } => *span,
226            JsStmt::FunctionDecl { span, .. } => *span,
227            JsStmt::Return(_, span, _) => *span,
228            JsStmt::If { span, .. } => *span,
229            JsStmt::While { span, .. } => *span,
230            JsStmt::For { span, .. } => *span,
231            JsStmt::ForIn { span, .. } => *span,
232            JsStmt::ForOf { span, .. } => *span,
233            JsStmt::Try { span, .. } => *span,
234            JsStmt::Switch { span, .. } => *span,
235            JsStmt::Throw(_, span, _) => *span,
236            JsStmt::Block(_, span, _) => *span,
237            JsStmt::Break(span, _) => *span,
238            JsStmt::Continue(span, _) => *span,
239            JsStmt::Other(_, span, _) => *span,
240        }
241    }
242
243    /// 获取语句的 trivia 信息
244    pub fn trivia(&self) -> &Trivia {
245        match self {
246            JsStmt::Expr(_, _, t) => t,
247            JsStmt::VariableDecl { trivia, .. } => trivia,
248            JsStmt::Import { trivia, .. } => trivia,
249            JsStmt::Export { trivia, .. } => trivia,
250            JsStmt::ExportAll { trivia, .. } => trivia,
251            JsStmt::ExportNamed { trivia, .. } => trivia,
252            JsStmt::FunctionDecl { trivia, .. } => trivia,
253            JsStmt::Return(_, _, t) => t,
254            JsStmt::If { trivia, .. } => trivia,
255            JsStmt::While { trivia, .. } => trivia,
256            JsStmt::For { trivia, .. } => trivia,
257            JsStmt::ForIn { trivia, .. } => trivia,
258            JsStmt::ForOf { trivia, .. } => trivia,
259            JsStmt::Try { trivia, .. } => trivia,
260            JsStmt::Switch { trivia, .. } => trivia,
261            JsStmt::Throw(_, _, t) => t,
262            JsStmt::Block(_, _, t) => t,
263            JsStmt::Break(_, t) => t,
264            JsStmt::Continue(_, t) => t,
265            JsStmt::Other(_, _, t) => t,
266        }
267    }
268
269    /// 验证语句的有效性
270    pub fn validate(&self, depth: usize) -> Result<()> {
271        if depth > MAX_RECURSION_DEPTH {
272            return Err(IRError::CircularReference("Statement recursion depth exceeded".to_string()).into());
273        }
274
275        match self {
276            JsStmt::Expr(expr, _, _) => expr.validate(depth + 1),
277            JsStmt::VariableDecl { id, init, .. } => {
278                if id.len() > MAX_STRING_LENGTH {
279                    return Err(IRError::SizeLimitExceeded("Variable name length exceeded".to_string()).into());
280                }
281                if let Some(init_expr) = init {
282                    init_expr.validate(depth + 1)?;
283                }
284                Ok(())
285            }
286            JsStmt::Import { source, specifiers, .. } => {
287                if source.len() > MAX_STRING_LENGTH {
288                    return Err(IRError::SizeLimitExceeded("Import source length exceeded".to_string()).into());
289                }
290                if specifiers.len() > MAX_ARRAY_LENGTH {
291                    return Err(IRError::SizeLimitExceeded("Import specifiers length exceeded".to_string()).into());
292                }
293                for specifier in specifiers {
294                    if specifier.len() > MAX_STRING_LENGTH {
295                        return Err(IRError::SizeLimitExceeded("Import specifier length exceeded".to_string()).into());
296                    }
297                }
298                Ok(())
299            }
300            JsStmt::Export { declaration, .. } => declaration.validate(depth + 1),
301            JsStmt::ExportAll { source, .. } => {
302                if source.len() > MAX_STRING_LENGTH {
303                    return Err(IRError::SizeLimitExceeded("Export all source length exceeded".to_string()).into());
304                }
305                Ok(())
306            }
307            JsStmt::ExportNamed { source, specifiers, .. } => {
308                if let Some(source_str) = source {
309                    if source_str.len() > MAX_STRING_LENGTH {
310                        return Err(IRError::SizeLimitExceeded("Export named source length exceeded".to_string()).into());
311                    }
312                }
313                if specifiers.len() > MAX_ARRAY_LENGTH {
314                    return Err(IRError::SizeLimitExceeded("Export named specifiers length exceeded".to_string()).into());
315                }
316                for specifier in specifiers {
317                    if specifier.len() > MAX_STRING_LENGTH {
318                        return Err(IRError::SizeLimitExceeded("Export specifier length exceeded".to_string()).into());
319                    }
320                }
321                Ok(())
322            }
323            JsStmt::FunctionDecl { id, params, body, .. } => {
324                if id.len() > MAX_STRING_LENGTH {
325                    return Err(IRError::SizeLimitExceeded("Function name length exceeded".to_string()).into());
326                }
327                if params.len() > MAX_ARRAY_LENGTH {
328                    return Err(IRError::SizeLimitExceeded("Function parameters length exceeded".to_string()).into());
329                }
330                for param in params {
331                    if param.len() > MAX_STRING_LENGTH {
332                        return Err(IRError::SizeLimitExceeded("Parameter name length exceeded".to_string()).into());
333                    }
334                }
335                if body.len() > MAX_ARRAY_LENGTH {
336                    return Err(IRError::SizeLimitExceeded("Function body length exceeded".to_string()).into());
337                }
338                for stmt in body {
339                    stmt.validate(depth + 1)?;
340                }
341                Ok(())
342            }
343            JsStmt::Return(expr, _, _) => {
344                if let Some(expr) = expr {
345                    expr.validate(depth + 1)?;
346                }
347                Ok(())
348            }
349            JsStmt::If { test, consequent, alternate, .. } => {
350                test.validate(depth + 1)?;
351                consequent.validate(depth + 1)?;
352                if let Some(alt) = alternate {
353                    alt.validate(depth + 1)?;
354                }
355                Ok(())
356            }
357            JsStmt::While { test, body, .. } => {
358                test.validate(depth + 1)?;
359                body.validate(depth + 1)
360            }
361            JsStmt::For { init, test, update, body, .. } => {
362                if let Some(init_stmt) = init {
363                    init_stmt.validate(depth + 1)?;
364                }
365                if let Some(test_expr) = test {
366                    test_expr.validate(depth + 1)?;
367                }
368                if let Some(update_expr) = update {
369                    update_expr.validate(depth + 1)?;
370                }
371                body.validate(depth + 1)
372            }
373            JsStmt::ForIn { left, right, body, .. } => {
374                left.validate(depth + 1)?;
375                right.validate(depth + 1)?;
376                body.validate(depth + 1)
377            }
378            JsStmt::ForOf { left, right, body, .. } => {
379                left.validate(depth + 1)?;
380                right.validate(depth + 1)?;
381                body.validate(depth + 1)
382            }
383            JsStmt::Try { block, handler, finalizer, .. } => {
384                block.validate(depth + 1)?;
385                if let Some((catch_id, catch_body)) = handler {
386                    if catch_id.len() > MAX_STRING_LENGTH {
387                        return Err(IRError::SizeLimitExceeded("Catch parameter name length exceeded".to_string()).into());
388                    }
389                    catch_body.validate(depth + 1)?;
390                }
391                if let Some(finally_body) = finalizer {
392                    finally_body.validate(depth + 1)?;
393                }
394                Ok(())
395            }
396            JsStmt::Switch { discriminant, cases, .. } => {
397                discriminant.validate(depth + 1)?;
398                if cases.len() > MAX_ARRAY_LENGTH {
399                    return Err(IRError::SizeLimitExceeded("Switch cases length exceeded".to_string()).into());
400                }
401                for (test, stmts) in cases {
402                    if let Some(test_expr) = test {
403                        test_expr.validate(depth + 1)?;
404                    }
405                    if stmts.len() > MAX_ARRAY_LENGTH {
406                        return Err(IRError::SizeLimitExceeded("Switch case statements length exceeded".to_string()).into());
407                    }
408                    for stmt in stmts {
409                        stmt.validate(depth + 1)?;
410                    }
411                }
412                Ok(())
413            }
414            JsStmt::Throw(expr, _, _) => expr.validate(depth + 1),
415            JsStmt::Block(stmts, _, _) => {
416                if stmts.len() > MAX_ARRAY_LENGTH {
417                    return Err(IRError::SizeLimitExceeded("Block statements length exceeded".to_string()).into());
418                }
419                for stmt in stmts {
420                    stmt.validate(depth + 1)?;
421                }
422                Ok(())
423            }
424            JsStmt::Break(_, _) => Ok(()),
425            JsStmt::Continue(_, _) => Ok(()),
426            JsStmt::Other(code, _, _) => {
427                if code.len() > MAX_STRING_LENGTH {
428                    return Err(IRError::SizeLimitExceeded("Other statement code length exceeded".to_string()).into());
429                }
430                Ok(())
431            }
432        }
433    }
434
435    /// 优化语句
436    pub fn optimize(&mut self) {
437        crate::optimizer::StmtOptimizer::optimize(self);
438    }
439
440    /// 检查语句是否为空
441    pub fn is_empty(&self) -> bool {
442        match self {
443            JsStmt::Block(stmts, _, _) => stmts.is_empty(),
444            _ => false,
445        }
446    }
447}
448
449/// 语句访问者 trait
450pub trait JsStmtVisitor<R> {
451    /// 访问表达式语句
452    fn visit_expr(&mut self, expr: &JsExpr, span: &Span, trivia: &Trivia) -> R;
453    /// 访问变量声明语句
454    fn visit_variable_decl(&mut self, kind: &String, id: &String, init: &Option<JsExpr>, span: &Span, trivia: &Trivia) -> R;
455    /// 访问导入语句
456    fn visit_import(&mut self, source: &String, specifiers: &Vec<String>, span: &Span, trivia: &Trivia) -> R;
457    /// 访问导出语句
458    fn visit_export(&mut self, declaration: &JsStmt, span: &Span, trivia: &Trivia) -> R;
459    /// 访问导出所有语句
460    fn visit_export_all(&mut self, source: &String, span: &Span, trivia: &Trivia) -> R;
461    /// 访问导出命名语句
462    fn visit_export_named(&mut self, source: &Option<String>, specifiers: &Vec<String>, span: &Span, trivia: &Trivia) -> R;
463    /// 访问函数声明语句
464    fn visit_function_decl(&mut self, id: &String, params: &Vec<String>, body: &Vec<JsStmt>, is_async: bool, span: &Span, trivia: &Trivia) -> R;
465    /// 访问返回语句
466    fn visit_return(&mut self, expr: &Option<JsExpr>, span: &Span, trivia: &Trivia) -> R;
467    /// 访问 if 语句
468    fn visit_if(&mut self, test: &JsExpr, consequent: &JsStmt, alternate: &Option<Box<JsStmt>>, span: &Span, trivia: &Trivia) -> R;
469    /// 访问 while 语句
470    fn visit_while(&mut self, test: &JsExpr, body: &JsStmt, span: &Span, trivia: &Trivia) -> R;
471    /// 访问 for 语句
472    fn visit_for(&mut self, init: &Option<Box<JsStmt>>, test: &Option<JsExpr>, update: &Option<JsExpr>, body: &JsStmt, span: &Span, trivia: &Trivia) -> R;
473    /// 访问 for-in 语句
474    fn visit_for_in(&mut self, left: &JsStmt, right: &JsExpr, body: &JsStmt, span: &Span, trivia: &Trivia) -> R;
475    /// 访问 for-of 语句
476    fn visit_for_of(&mut self, left: &JsStmt, right: &JsExpr, body: &JsStmt, span: &Span, trivia: &Trivia) -> R;
477    /// 访问 try/catch 语句
478    fn visit_try(&mut self, block: &JsStmt, handler: &Option<(String, Box<JsStmt>)>, finalizer: &Option<Box<JsStmt>>, span: &Span, trivia: &Trivia) -> R;
479    /// 访问 switch 语句
480    fn visit_switch(&mut self, discriminant: &JsExpr, cases: &Vec<(Option<JsExpr>, Vec<JsStmt>)>, span: &Span, trivia: &Trivia) -> R;
481    /// 访问 throw 语句
482    fn visit_throw(&mut self, expr: &JsExpr, span: &Span, trivia: &Trivia) -> R;
483    /// 访问块语句
484    fn visit_block(&mut self, stmts: &Vec<JsStmt>, span: &Span, trivia: &Trivia) -> R;
485    /// 访问 break 语句
486    fn visit_break(&mut self, span: &Span, trivia: &Trivia) -> R;
487    /// 访问 continue 语句
488    fn visit_continue(&mut self, span: &Span, trivia: &Trivia) -> R;
489    /// 访问其他语句
490    fn visit_other(&mut self, code: &String, span: &Span, trivia: &Trivia) -> R;
491}
492
493impl JsStmt {
494    /// 接受访问者
495    pub fn accept<R>(&self, visitor: &mut dyn JsStmtVisitor<R>) -> R {
496        match self {
497            JsStmt::Expr(expr, span, trivia) => visitor.visit_expr(expr, span, trivia),
498            JsStmt::VariableDecl { kind, id, init, span, trivia } => visitor.visit_variable_decl(kind, id, init, span, trivia),
499            JsStmt::Import { source, specifiers, span, trivia } => visitor.visit_import(source, specifiers, span, trivia),
500            JsStmt::Export { declaration, span, trivia } => visitor.visit_export(declaration, span, trivia),
501            JsStmt::ExportAll { source, span, trivia } => visitor.visit_export_all(source, span, trivia),
502            JsStmt::ExportNamed { source, specifiers, span, trivia } => visitor.visit_export_named(source, specifiers, span, trivia),
503            JsStmt::FunctionDecl { id, params, body, is_async, span, trivia } => visitor.visit_function_decl(id, params, body, *is_async, span, trivia),
504            JsStmt::Return(expr, span, trivia) => visitor.visit_return(expr, span, trivia),
505            JsStmt::If { test, consequent, alternate, span, trivia } => visitor.visit_if(test, consequent, alternate, span, trivia),
506            JsStmt::While { test, body, span, trivia } => visitor.visit_while(test, body, span, trivia),
507            JsStmt::For { init, test, update, body, span, trivia } => visitor.visit_for(init, test, update, body, span, trivia),
508            JsStmt::ForIn { left, right, body, span, trivia } => visitor.visit_for_in(left, right, body, span, trivia),
509            JsStmt::ForOf { left, right, body, span, trivia } => visitor.visit_for_of(left, right, body, span, trivia),
510            JsStmt::Try { block, handler, finalizer, span, trivia } => visitor.visit_try(block, handler, finalizer, span, trivia),
511            JsStmt::Switch { discriminant, cases, span, trivia } => visitor.visit_switch(discriminant, cases, span, trivia),
512            JsStmt::Throw(expr, span, trivia) => visitor.visit_throw(expr, span, trivia),
513            JsStmt::Block(stmts, span, trivia) => visitor.visit_block(stmts, span, trivia),
514            JsStmt::Break(span, trivia) => visitor.visit_break(span, trivia),
515            JsStmt::Continue(span, trivia) => visitor.visit_continue(span, trivia),
516            JsStmt::Other(code, span, trivia) => visitor.visit_other(code, span, trivia),
517        }
518    }
519}