fn call()

in unsupported/juno/crates/juno/src/sema/resolver.rs [1142:1388]


    fn call(&mut self, lock: &'gc GCLock, node: &'gc Node<'gc>, path: Option<Path<'gc>>) {
        match node {
            Node::Program(_) => {
                panic!("visited unexpected {}", node.name())
            }

            Node::FunctionDeclaration(..) => {
                // Collect hoisted function.
                self.sem
                    .scope_mut(self.current_scope.unwrap())
                    .hoisted_functions
                    .push(NodeRc::from_node(lock, node));
                self.visit_function_like(lock, node);
            }

            Node::FunctionExpression(fe) => self.visit_function_expression(lock, fe, node),

            Node::Identifier(ident) => self.visit_identifier(lock, ident, node, path.unwrap()),

            Node::BlockStatement(_) => self.visit_block_statement(lock, node, path.unwrap()),

            Node::SwitchStatement(switch) => {
                // Visit the discriminant before creating a new scope.
                switch.discriminant.visit(
                    lock,
                    self,
                    Some(Path::new(node, NodeField::discriminant)),
                );
                self.with_new_label(lock, None, node, |pself| {
                    pself.in_new_scope(lock, node, |pself| {
                        if let Some(decls) =
                            pself.function_context().decls.scope_decls_for_node(node)
                        {
                            pself.process_declarations(lock, &decls);
                        }
                        for case in &switch.cases {
                            case.visit(lock, pself, Some(Path::new(node, NodeField::cases)));
                        }
                    });
                });
            }

            Node::ForStatement(_) | Node::ForInStatement(_) | Node::ForOfStatement(_) => {
                self.with_new_label(lock, None, node, |pself| {
                    // Only create a lexical scope if there are declarations in it.
                    if let Some(decls) = pself.function_context().decls.scope_decls_for_node(node) {
                        pself.in_new_scope(lock, node, |pself| {
                            pself.process_declarations(lock, decls.as_slice());
                            node.visit_children(lock, pself);
                        });
                    } else {
                        node.visit_children(lock, pself);
                    }
                });
            }

            Node::WhileStatement(_) | Node::DoWhileStatement(_) => {
                self.with_new_label(lock, None, node, |pself| {
                    node.visit_children(lock, pself);
                });
            }

            Node::LabeledStatement(labeled) => {
                self.with_new_label(lock, Some(labeled.label), node, |pself| {
                    node.visit_children(lock, pself);
                });
            }

            Node::BreakStatement(ast::BreakStatement {
                label: Some(label), ..
            }) => {
                let name = node_cast!(Node::Identifier, label).name;
                if self.function_context().label_table.get(&name).is_none() {
                    lock.sm().error(
                        *label.range(),
                        format!("label '{}' is not defined", lock.str(name)),
                    );
                }
            }
            Node::BreakStatement(ast::BreakStatement { label: None, .. }) => {
                if self.current_loop_or_switch.is_none() {
                    lock.sm()
                        .error(*node.range(), "'break' not within a loop or switch");
                }
            }

            Node::ContinueStatement(ast::ContinueStatement {
                label: Some(label_node),
                ..
            }) => {
                let name = node_cast!(Node::Identifier, label_node).name;
                match self.function_context().label_table.get(&name) {
                    Some(label) => {
                        if matches!(label.target_statement, Node::LabeledStatement(_)) {
                            lock.sm().error(
                                *label_node.range(),
                                format!(
                                    "'continue' label '{}' is not a loop label",
                                    lock.str(name)
                                ),
                            );
                        }
                    }
                    None => {
                        lock.sm().error(
                            *label_node.range(),
                            format!("label '{}' is not defined", lock.str(name)),
                        );
                    }
                }
            }
            Node::ContinueStatement(ast::ContinueStatement { label: None, .. }) => {
                if self.current_loop.is_none() {
                    lock.sm()
                        .error(*node.range(), "'continue' not within a loop or switch");
                }
            }

            Node::CatchClause(ast::CatchClause { param, body, .. }) => {
                self.in_new_scope(lock, node, |pself| {
                    if let Some(id_node @ Node::Identifier(_)) = param {
                        // For compatibility with ES5, we need to treat a single catch variable
                        // specially, see: B.3.5 VariableStatements in Catch Blocks
                        // https://www.ecma-international.org/ecma-262/10.0/index.html#sec-variablestatements-in-catch-blocks
                        pself.validate_and_declare_identifier(lock, DeclKind::ES5Catch, id_node);
                    } else {
                        let mut idents = SmallVec::<[&Node; 4]>::new();
                        Self::extract_declared_idents_from_id(lock, *param, &mut idents);
                        for id_node in idents {
                            pself.validate_and_declare_identifier(lock, DeclKind::Let, id_node);
                        }
                    }
                    // Process body's declarations, skip visiting it, visit its children.
                    pself.process_collected_declarations(lock, body);
                    body.visit_children(lock, pself);
                });
            }

            Node::MetaProperty(ast::MetaProperty {
                meta: Node::Identifier(meta),
                property: Node::Identifier(prop),
                ..
            }) => {
                // Validate "new.target"
                if meta.name == self.kw.ident_new
                    && prop.name == self.kw.ident_target
                    && self.function_context().func_id.is_global()
                {
                    // ES9.0 15.1.1:
                    // It is a Syntax Error if StatementList Contains NewTarget unless the
                    // source code containing NewTarget is eval code that is being processed
                    // by a direct eval.
                    lock.sm()
                        .error(*node.range(), "'new.target' outside of a function");
                }
            }

            Node::ImportDeclaration(ast::ImportDeclaration {
                source: Node::StringLiteral(ast::StringLiteral { value, .. }),
                ..
            }) => {
                node.visit_children(lock, self);
                if let ResolverMode::Module {
                    dependency_resolver,
                } = self.mode
                {
                    // Resolve `import`.
                    let target = String::from_utf16_lossy(&value.str);
                    match dependency_resolver.resolve_dependency(
                        lock,
                        self.file_id,
                        &target,
                        DependencyKind::Import,
                    ) {
                        Some(file_id) => {
                            self.sem.add_require(NodeRc::from_node(lock, node), file_id);
                        }
                        None => {
                            lock.sm().warning(
                                *node.range(),
                                format!("Unable to resolve import for {}", target),
                            );
                        }
                    }
                }
            }

            Node::ClassDeclaration(_) | Node::ClassExpression(_) => {
                let old_strict = self.function_strict_mode();
                *self.function_strict_mode_mut() = true;
                node.visit_children(lock, self);
                *self.function_strict_mode_mut() = old_strict;
            }

            Node::CallExpression(call @ ast::CallExpression { arguments, .. }) => {
                // Check for a direct call to local `eval()`.
                if let Node::Identifier(identifier) = call.callee {
                    if identifier.name == self.kw.ident_eval {
                        let is_eval = match self.binding_table.get(&identifier.name) {
                            None => true,
                            Some(name) => {
                                let decl = self.sem.decl(name.decl);
                                decl.scope.is_global() && decl.kind.is_var_like()
                            }
                        };
                        if is_eval {
                            self.register_local_eval();
                        }
                    }
                }

                node.visit_children(lock, self);
                if let ResolverMode::Module {
                    dependency_resolver,
                } = self.mode
                {
                    if self.is_require(lock, call) {
                        // Resolve `require()` call.
                        if let Node::StringLiteral(ast::StringLiteral { value, .. }) = arguments[0]
                        {
                            let target = String::from_utf16_lossy(&value.str);
                            match dependency_resolver.resolve_dependency(
                                lock,
                                self.file_id,
                                &target,
                                DependencyKind::Require,
                            ) {
                                Some(file_id) => {
                                    self.sem.add_require(NodeRc::from_node(lock, node), file_id);
                                }
                                None => {
                                    lock.sm().warning(
                                        *node.range(),
                                        format!("Unable to resolve require for {}", target),
                                    );
                                }
                            }
                        }
                    }
                }
            }

            _ => {
                node.visit_children(lock, self);
            }
        }
    }