summaryrefslogtreecommitdiffstats
path: root/third_party/rust/jsparagus-scope/src/pass.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/jsparagus-scope/src/pass.rs')
-rw-r--r--third_party/rust/jsparagus-scope/src/pass.rs373
1 files changed, 373 insertions, 0 deletions
diff --git a/third_party/rust/jsparagus-scope/src/pass.rs b/third_party/rust/jsparagus-scope/src/pass.rs
new file mode 100644
index 0000000000..34bede940e
--- /dev/null
+++ b/third_party/rust/jsparagus-scope/src/pass.rs
@@ -0,0 +1,373 @@
+//! Code to traverse the AST and drive the rest of scope analysis.
+//!
+//! This module is responsible for walking the AST. Other modules do the actual
+//! analysis.
+//!
+//! Scope analysis is currently a second pass, after parsing, using the AST,
+//! but the goal is to do this analysis as part of the parse phase, even when
+//! no AST is built. So we try to keep AST use separate from the analysis code.
+
+use crate::builder::{ScopeBuildError, ScopeDataMapAndScriptStencilList, ScopeDataMapBuilder};
+use crate::data::FunctionDeclarationPropertyMap;
+use ast::arena;
+use ast::associated_data::AssociatedData;
+use ast::source_atom_set::CommonSourceAtomSetIndices;
+use ast::{types::*, visit::Pass};
+use std::collections::HashMap;
+use stencil::scope::ScopeDataMap;
+use stencil::script::{ScriptStencilIndex, ScriptStencilList};
+
+/// The result of scope analysis.
+pub struct ScopePassResult<'alloc> {
+ pub scope_data_map: ScopeDataMap,
+ pub function_declarations: HashMap<ScriptStencilIndex, &'alloc Function<'alloc>>,
+ pub function_stencil_indices: AssociatedData<ScriptStencilIndex>,
+ pub function_declaration_properties: FunctionDeclarationPropertyMap,
+ pub scripts: ScriptStencilList,
+ pub error: Option<ScopeBuildError>,
+}
+
+/// The top-level struct responsible for extracting the necessary information
+/// from the AST. The analysis itself is done mainly by the `ScopeDataMapBuilder`,
+/// which has very limited interaction with the AST.
+///
+/// FIXME: This should be rewritten as a grammar extension.
+#[derive(Debug)]
+pub struct ScopePass<'alloc> {
+ builder: ScopeDataMapBuilder,
+ function_declarations: HashMap<ScriptStencilIndex, &'alloc Function<'alloc>>,
+}
+
+impl<'alloc> ScopePass<'alloc> {
+ pub fn new() -> Self {
+ Self {
+ builder: ScopeDataMapBuilder::new(),
+ function_declarations: HashMap::new(),
+ }
+ }
+}
+
+impl<'alloc> From<ScopePass<'alloc>> for ScopePassResult<'alloc> {
+ fn from(pass: ScopePass<'alloc>) -> ScopePassResult<'alloc> {
+ let ScopeDataMapAndScriptStencilList {
+ scope_data_map,
+ function_stencil_indices,
+ function_declaration_properties,
+ scripts,
+ error,
+ } = pass.builder.into();
+ ScopePassResult {
+ scope_data_map,
+ function_declarations: pass.function_declarations,
+ function_stencil_indices,
+ function_declaration_properties,
+ scripts,
+ error,
+ }
+ }
+}
+
+impl<'alloc> Pass<'alloc> for ScopePass<'alloc> {
+ fn enter_script(&mut self, _ast: &'alloc Script<'alloc>) {
+ self.builder.before_script();
+ }
+
+ fn leave_script(&mut self, _ast: &'alloc Script<'alloc>) {
+ self.builder.after_script();
+ }
+
+ fn enter_enum_statement_variant_block_statement(&mut self, block: &'alloc Block<'alloc>) {
+ self.builder.before_block_statement(block);
+ }
+
+ fn leave_enum_statement_variant_block_statement(&mut self, _block: &'alloc Block<'alloc>) {
+ self.builder.after_block_statement();
+ }
+
+ fn enter_variable_declaration(&mut self, ast: &'alloc VariableDeclaration<'alloc>) {
+ match ast.kind {
+ VariableDeclarationKind::Var { .. } => {
+ self.builder.before_var_declaration();
+ }
+ VariableDeclarationKind::Let { .. } => {
+ self.builder.before_let_declaration();
+ }
+ VariableDeclarationKind::Const { .. } => {
+ self.builder.before_const_declaration();
+ }
+ }
+ }
+
+ fn leave_variable_declaration(&mut self, ast: &'alloc VariableDeclaration<'alloc>) {
+ match ast.kind {
+ VariableDeclarationKind::Var { .. } => {
+ self.builder.after_var_declaration();
+ }
+ VariableDeclarationKind::Let { .. } => {
+ self.builder.after_let_declaration();
+ }
+ VariableDeclarationKind::Const { .. } => {
+ self.builder.after_const_declaration();
+ }
+ }
+ }
+
+ fn visit_binding_identifier(&mut self, ast: &'alloc BindingIdentifier) {
+ self.builder.on_binding_identifier(ast.name.value);
+ }
+
+ // Given we override `visit_binding_identifier` above,
+ // visit_identifier is not called for Identifier inside BindingIdentifier,
+ // and this is called only for references, either
+ // IdentifierExpression or AssignmentTargetIdentifier.
+ fn visit_identifier(&mut self, ast: &'alloc Identifier) {
+ self.builder.on_non_binding_identifier(ast.value);
+ }
+
+ fn visit_enum_expression_variant_this_expression(&mut self) {
+ self.builder
+ .on_non_binding_identifier(CommonSourceAtomSetIndices::this());
+ }
+
+ fn enter_enum_statement_variant_function_declaration(&mut self, ast: &'alloc Function<'alloc>) {
+ if !self.builder.is_syntax_only_mode() {
+ self.builder.enter_syntax_only_mode();
+ }
+
+ let name = if let Some(ref name) = ast.name {
+ name.name.value
+ } else {
+ panic!("FunctionDeclaration should have name");
+ };
+ let fun_index =
+ self.builder
+ .before_function_declaration(name, ast, ast.is_generator, ast.is_async);
+ self.function_declarations.insert(fun_index, ast);
+ }
+
+ fn leave_enum_statement_variant_function_declaration(&mut self, ast: &'alloc Function<'alloc>) {
+ self.builder.after_function_declaration(ast);
+ }
+
+ fn enter_enum_expression_variant_function_expression(&mut self, ast: &'alloc Function<'alloc>) {
+ if !self.builder.is_syntax_only_mode() {
+ self.builder.enter_syntax_only_mode();
+ }
+
+ self.builder
+ .before_function_expression(ast, ast.is_generator, ast.is_async);
+ }
+
+ fn leave_enum_expression_variant_function_expression(&mut self, ast: &'alloc Function<'alloc>) {
+ self.builder.after_function_expression(ast);
+ }
+
+ fn visit_formal_parameters(&mut self, ast: &'alloc FormalParameters<'alloc>) {
+ self.builder.before_function_parameters(ast);
+
+ self.enter_formal_parameters(ast);
+ for item in &ast.items {
+ self.builder.before_parameter();
+ self.visit_parameter(item);
+ }
+ if let Some(item) = &ast.rest {
+ self.builder.before_rest_parameter();
+ self.visit_binding(item);
+ }
+ self.leave_formal_parameters(ast);
+
+ self.builder.after_function_parameters();
+ }
+
+ fn enter_enum_method_definition_variant_method(&mut self, ast: &'alloc Method<'alloc>) {
+ self.builder
+ .before_method(ast, ast.is_generator, ast.is_async);
+ // FIXME: Call self.builder.on_function_name
+ }
+
+ fn leave_enum_method_definition_variant_method(&mut self, ast: &'alloc Method<'alloc>) {
+ self.builder.after_method(ast);
+ }
+
+ /// Getter doesn't have FormalParameters.
+ /// Call builder methods just before body.
+ fn visit_getter(&mut self, ast: &'alloc Getter<'alloc>) {
+ self.builder.before_getter(ast);
+ // FIXME: Call self.builder.on_function_name
+
+ self.enter_getter(ast);
+ self.visit_class_element_name(&ast.property_name);
+
+ // FIXME: Pass something that points `()` part of getter.
+ self.builder.on_getter_parameter(ast);
+
+ self.visit_function_body(&ast.body);
+ self.leave_getter(ast);
+
+ self.builder.after_getter(ast);
+ }
+
+ /// Setter doesn't have FormalParameters, but single Parameter.
+ /// Call builder methods around it.
+ fn visit_setter(&mut self, ast: &'alloc Setter<'alloc>) {
+ self.builder.before_setter(ast);
+ // FIXME: Call self.builder.on_function_name
+
+ self.enter_setter(ast);
+ self.visit_class_element_name(&ast.property_name);
+
+ // FIXME: Pass something that points `(param)` part of setter,
+ // including `(` and `)`.
+ self.builder.before_setter_parameter(&ast.param);
+ self.visit_parameter(&ast.param);
+ self.builder.after_setter_parameter();
+
+ self.visit_function_body(&ast.body);
+ self.leave_setter(ast);
+
+ self.builder.after_setter(ast);
+ }
+
+ fn leave_binding_with_default(&mut self, _ast: &'alloc BindingWithDefault<'alloc>) {
+ self.builder.after_initializer();
+ }
+
+ fn enter_enum_property_name_variant_computed_property_name(
+ &mut self,
+ _ast: &'alloc ComputedPropertyName<'alloc>,
+ ) {
+ self.builder.before_computed_property_name();
+ }
+
+ fn enter_binding_pattern(&mut self, _ast: &'alloc BindingPattern<'alloc>) {
+ self.builder.before_binding_pattern();
+ }
+
+ fn enter_function_body(&mut self, ast: &'alloc FunctionBody<'alloc>) {
+ self.builder.before_function_body(ast);
+ }
+
+ fn leave_function_body(&mut self, _ast: &'alloc FunctionBody<'alloc>) {
+ self.builder.after_function_body();
+ }
+
+ fn enter_enum_expression_variant_arrow_expression(
+ &mut self,
+ is_async: &'alloc bool,
+ params: &'alloc FormalParameters<'alloc>,
+ _body: &'alloc ArrowExpressionBody<'alloc>,
+ ) {
+ self.builder.before_arrow_function(*is_async, params);
+ }
+
+ fn leave_enum_expression_variant_arrow_expression(
+ &mut self,
+ _is_async: &'alloc bool,
+ _params: &'alloc FormalParameters<'alloc>,
+ body: &'alloc ArrowExpressionBody<'alloc>,
+ ) {
+ self.builder.after_arrow_function(body);
+ }
+
+ /// Arrow function with expression body.
+ /// Use the expression as the node for function body.
+ fn enter_enum_arrow_expression_body_variant_expression(
+ &mut self,
+ ast: &'alloc arena::Box<'alloc, Expression<'alloc>>,
+ ) {
+ let expr: &Expression = ast;
+ self.builder.before_function_body(expr);
+ }
+
+ fn leave_enum_arrow_expression_body_variant_expression(
+ &mut self,
+ _ast: &'alloc arena::Box<'alloc, Expression<'alloc>>,
+ ) {
+ self.builder.after_function_body();
+ }
+
+ fn enter_catch_clause(&mut self, _ast: &'alloc CatchClause<'alloc>) {
+ self.builder.before_catch_clause();
+ }
+
+ fn enter_call_expression(&mut self, ast: &'alloc CallExpression<'alloc>) {
+ match &ast.callee {
+ ExpressionOrSuper::Expression(expr) => match &**expr {
+ Expression::IdentifierExpression(IdentifierExpression { name, .. }) => {
+ if name.value == CommonSourceAtomSetIndices::eval() {
+ self.builder.on_direct_eval();
+ }
+ }
+ _ => {}
+ },
+ _ => {}
+ }
+ }
+
+ fn enter_class_declaration(&mut self, _ast: &'alloc ClassDeclaration<'alloc>) {
+ self.builder.on_class();
+ }
+
+ fn enter_class_expression(&mut self, _ast: &'alloc ClassExpression<'alloc>) {
+ self.builder.on_class();
+ }
+
+ fn enter_enum_statement_variant_with_statement(
+ &mut self,
+ _object: &'alloc arena::Box<'alloc, Expression<'alloc>>,
+ _body: &'alloc arena::Box<'alloc, Statement<'alloc>>,
+ ) {
+ self.builder.on_with();
+ }
+
+ fn visit_enum_unary_operator_variant_delete(&mut self) {
+ self.builder.on_delete();
+ }
+
+ fn enter_enum_statement_variant_for_statement(
+ &mut self,
+ init: &'alloc Option<VariableDeclarationOrExpression<'alloc>>,
+ _test: &'alloc Option<arena::Box<'alloc, Expression<'alloc>>>,
+ _update: &'alloc Option<arena::Box<'alloc, Expression<'alloc>>>,
+ _block: &'alloc arena::Box<'alloc, Statement<'alloc>>,
+ ) {
+ match init {
+ Some(VariableDeclarationOrExpression::VariableDeclaration(decl)) => match decl.kind {
+ VariableDeclarationKind::Let { .. } | VariableDeclarationKind::Const { .. } => {
+ self.builder.on_lexical_for();
+ }
+ _ => {}
+ },
+ _ => {}
+ }
+ }
+
+ fn enter_enum_statement_variant_for_in_statement(
+ &mut self,
+ left: &'alloc VariableDeclarationOrAssignmentTarget<'alloc>,
+ _right: &'alloc arena::Box<'alloc, Expression<'alloc>>,
+ _block: &'alloc arena::Box<'alloc, Statement<'alloc>>,
+ ) {
+ match left {
+ VariableDeclarationOrAssignmentTarget::VariableDeclaration(decl) => match decl.kind {
+ VariableDeclarationKind::Let { .. } | VariableDeclarationKind::Const { .. } => {
+ self.builder.on_lexical_for();
+ }
+ _ => {}
+ },
+ _ => {}
+ }
+ }
+
+ fn enter_enum_statement_variant_switch_statement(
+ &mut self,
+ _discriminant: &'alloc arena::Box<'alloc, Expression<'alloc>>,
+ _cases: &'alloc arena::Vec<'alloc, SwitchCase<'alloc>>,
+ ) {
+ self.builder.on_switch();
+ }
+
+ fn visit_enum_expression_variant_new_target_expression(&mut self) {
+ self.builder.on_new_target();
+ }
+}