#[cfg(feature = "annex-b")]
use crate::operations::annex_b_function_declarations_names;
use crate::{
Declaration, Module, Script, StatementListItem, ToJsString,
declaration::{Binding, ExportDeclaration, LexicalDeclaration, VariableList},
expression::{Identifier, literal::ObjectMethodDefinition},
function::{
ArrowFunction, AsyncArrowFunction, AsyncFunctionDeclaration, AsyncFunctionExpression,
AsyncGeneratorDeclaration, AsyncGeneratorExpression, ClassDeclaration, ClassElement,
ClassExpression, FormalParameterList, FunctionBody, FunctionDeclaration,
FunctionExpression, GeneratorDeclaration, GeneratorExpression,
},
operations::{
ContainsSymbol, LexicallyScopedDeclaration, VarScopedDeclaration, bound_names, contains,
lexically_declared_names, lexically_scoped_declarations, var_declared_names,
var_scoped_declarations,
},
property::PropertyName,
scope::{FunctionScopes, IdentifierReference, Scope},
statement::{
Block, Catch, ForInLoop, ForLoop, ForOfLoop, Switch, With,
iteration::{ForLoopInitializer, IterableLoopInitializer},
},
visitor::{NodeRef, NodeRefMut, VisitorMut},
};
use boa_interner::{Interner, Sym};
use rustc_hash::FxHashMap;
use std::ops::ControlFlow;
pub(crate) fn collect_bindings<'a, N>(
node: &'a mut N,
strict: bool,
eval: bool,
scope: &Scope,
interner: &Interner,
) -> Result<(), &'static str>
where
&'a mut N: Into<NodeRefMut<'a>>,
{
let mut visitor = BindingCollectorVisitor {
strict,
eval,
in_arrow: false,
scope: scope.clone(),
interner,
};
match visitor.visit(node) {
ControlFlow::Continue(()) => Ok(()),
ControlFlow::Break(reason) => Err(reason),
}
}
pub(crate) fn analyze_binding_escapes<'a, N>(
node: &'a mut N,
in_eval: bool,
scope: Scope,
interner: &Interner,
) -> Result<(), &'static str>
where
&'a mut N: Into<NodeRefMut<'a>>,
{
let mut visitor = BindingEscapeAnalyzer {
scope,
direct_eval: in_eval,
with: false,
interner,
};
match visitor.visit(node.into()) {
ControlFlow::Continue(()) => Ok(()),
ControlFlow::Break(reason) => Err(reason),
}
}
struct BindingEscapeAnalyzer<'interner> {
scope: Scope,
direct_eval: bool,
with: bool,
interner: &'interner Interner,
}
impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
type BreakTy = &'static str;
fn visit_identifier_mut(&mut self, node: &'ast mut Identifier) -> ControlFlow<Self::BreakTy> {
let name = node.to_js_string(self.interner);
self.scope
.access_binding(&name, self.direct_eval || self.with);
ControlFlow::Continue(())
}
fn visit_block_mut(&mut self, node: &'ast mut Block) -> ControlFlow<Self::BreakTy> {
let direct_eval_old = self.direct_eval;
self.direct_eval = node.contains_direct_eval || self.direct_eval;
if let Some(scope) = &mut node.scope {
if self.direct_eval {
scope.escape_all_bindings();
}
std::mem::swap(&mut self.scope, scope);
}
self.visit_statement_list_mut(&mut node.statements)?;
if let Some(scope) = &mut node.scope {
std::mem::swap(&mut self.scope, scope);
scope.reorder_binding_indices();
}
self.direct_eval = direct_eval_old;
ControlFlow::Continue(())
}
fn visit_switch_mut(&mut self, node: &'ast mut Switch) -> ControlFlow<Self::BreakTy> {
self.visit_expression_mut(&mut node.val)?;
let direct_eval_old = self.direct_eval;
self.direct_eval = node.contains_direct_eval || self.direct_eval;
if let Some(scope) = &mut node.scope {
if self.direct_eval {
scope.escape_all_bindings();
}
std::mem::swap(&mut self.scope, scope);
}
for case in &mut node.cases {
self.visit_case_mut(case)?;
}
if let Some(scope) = &mut node.scope {
std::mem::swap(&mut self.scope, scope);
scope.reorder_binding_indices();
}
self.direct_eval = direct_eval_old;
ControlFlow::Continue(())
}
fn visit_with_mut(&mut self, node: &'ast mut With) -> ControlFlow<Self::BreakTy> {
let with = self.with;
self.with = true;
if self.direct_eval {
node.scope.escape_all_bindings();
}
self.visit_expression_mut(&mut node.expression)?;
std::mem::swap(&mut self.scope, &mut node.scope);
self.visit_statement_mut(&mut node.statement)?;
std::mem::swap(&mut self.scope, &mut node.scope);
node.scope.reorder_binding_indices();
self.with = with;
ControlFlow::Continue(())
}
fn visit_catch_mut(&mut self, node: &'ast mut Catch) -> ControlFlow<Self::BreakTy> {
let direct_eval_old = self.direct_eval;
self.direct_eval = node.contains_direct_eval || self.direct_eval;
if self.direct_eval {
node.scope.escape_all_bindings();
}
std::mem::swap(&mut self.scope, &mut node.scope);
if let Some(binding) = &mut node.parameter {
self.visit_binding_mut(binding)?;
}
self.visit_block_mut(&mut node.block)?;
std::mem::swap(&mut self.scope, &mut node.scope);
node.scope.reorder_binding_indices();
self.direct_eval = direct_eval_old;
ControlFlow::Continue(())
}
fn visit_for_loop_mut(&mut self, node: &'ast mut ForLoop) -> ControlFlow<Self::BreakTy> {
let direct_eval_old = self.direct_eval;
self.direct_eval = node.inner.contains_direct_eval || self.direct_eval;
if let Some(ForLoopInitializer::Lexical(decl)) = &mut node.inner.init {
if self.direct_eval {
decl.scope.escape_all_bindings();
}
std::mem::swap(&mut self.scope, &mut decl.scope);
}
if let Some(init) = &mut node.inner.init {
self.visit_for_loop_initializer_mut(init)?;
}
if let Some(condition) = &mut node.inner.condition {
self.visit_expression_mut(condition)?;
}
if let Some(final_expr) = &mut node.inner.final_expr {
self.visit_expression_mut(final_expr)?;
}
self.visit_statement_mut(&mut node.inner.body)?;
if let Some(ForLoopInitializer::Lexical(decl)) = &mut node.inner.init {
std::mem::swap(&mut self.scope, &mut decl.scope);
decl.scope.reorder_binding_indices();
}
self.direct_eval = direct_eval_old;
ControlFlow::Continue(())
}
fn visit_for_in_loop_mut(&mut self, node: &'ast mut ForInLoop) -> ControlFlow<Self::BreakTy> {
let direct_eval_old = self.direct_eval;
if let Some(scope) = &mut node.target_scope {
self.direct_eval = node.target_contains_direct_eval || self.direct_eval;
if self.direct_eval {
scope.escape_all_bindings();
}
std::mem::swap(&mut self.scope, scope);
}
self.visit_expression_mut(&mut node.target)?;
if let Some(scope) = &mut node.target_scope {
self.direct_eval = direct_eval_old;
std::mem::swap(&mut self.scope, scope);
scope.reorder_binding_indices();
}
if let Some(scope) = &mut node.scope {
self.direct_eval = node.contains_direct_eval || self.direct_eval;
if self.direct_eval {
scope.escape_all_bindings();
}
std::mem::swap(&mut self.scope, scope);
}
self.visit_iterable_loop_initializer_mut(&mut node.initializer)?;
self.visit_statement_mut(&mut node.body)?;
if let Some(scope) = &mut node.scope {
std::mem::swap(&mut self.scope, scope);
scope.reorder_binding_indices();
}
self.direct_eval = direct_eval_old;
ControlFlow::Continue(())
}
fn visit_for_of_loop_mut(&mut self, node: &'ast mut ForOfLoop) -> ControlFlow<Self::BreakTy> {
let direct_eval_old = self.direct_eval;
if let Some(scope) = &mut node.iterable_scope {
self.direct_eval = node.iterable_contains_direct_eval || self.direct_eval;
if self.direct_eval {
scope.escape_all_bindings();
}
std::mem::swap(&mut self.scope, scope);
}
self.visit_expression_mut(&mut node.iterable)?;
if let Some(scope) = &mut node.iterable_scope {
self.direct_eval = direct_eval_old;
std::mem::swap(&mut self.scope, scope);
scope.reorder_binding_indices();
}
if let Some(scope) = &mut node.scope {
self.direct_eval = node.contains_direct_eval || self.direct_eval;
if self.direct_eval {
scope.escape_all_bindings();
}
std::mem::swap(&mut self.scope, scope);
}
self.visit_iterable_loop_initializer_mut(&mut node.init)?;
self.visit_statement_mut(&mut node.body)?;
if let Some(scope) = &mut node.scope {
std::mem::swap(&mut self.scope, scope);
scope.reorder_binding_indices();
}
self.direct_eval = direct_eval_old;
ControlFlow::Continue(())
}
fn visit_function_declaration_mut(
&mut self,
node: &'ast mut FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.parameters,
&mut node.body,
&mut node.scopes,
node.contains_direct_eval,
)
}
fn visit_generator_declaration_mut(
&mut self,
node: &'ast mut GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.parameters,
&mut node.body,
&mut node.scopes,
node.contains_direct_eval,
)
}
fn visit_async_function_declaration_mut(
&mut self,
node: &'ast mut AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.parameters,
&mut node.body,
&mut node.scopes,
node.contains_direct_eval,
)
}
fn visit_async_generator_declaration_mut(
&mut self,
node: &'ast mut AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.parameters,
&mut node.body,
&mut node.scopes,
node.contains_direct_eval,
)
}
fn visit_function_expression_mut(
&mut self,
node: &'ast mut FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.parameters,
&mut node.body,
&mut node.scopes,
node.contains_direct_eval,
)
}
fn visit_generator_expression_mut(
&mut self,
node: &'ast mut GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.parameters,
&mut node.body,
&mut node.scopes,
node.contains_direct_eval,
)
}
fn visit_async_function_expression_mut(
&mut self,
node: &'ast mut AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.parameters,
&mut node.body,
&mut node.scopes,
node.contains_direct_eval,
)
}
fn visit_async_generator_expression_mut(
&mut self,
node: &'ast mut AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.parameters,
&mut node.body,
&mut node.scopes,
node.contains_direct_eval,
)
}
fn visit_arrow_function_mut(
&mut self,
node: &'ast mut ArrowFunction,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.parameters,
&mut node.body,
&mut node.scopes,
node.contains_direct_eval,
)
}
fn visit_async_arrow_function_mut(
&mut self,
node: &'ast mut AsyncArrowFunction,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.parameters,
&mut node.body,
&mut node.scopes,
node.contains_direct_eval,
)
}
fn visit_class_declaration_mut(
&mut self,
node: &'ast mut ClassDeclaration,
) -> ControlFlow<Self::BreakTy> {
node.name_scope.escape_all_bindings();
std::mem::swap(&mut self.scope, &mut node.name_scope);
if let Some(super_ref) = &mut node.super_ref {
self.visit_expression_mut(super_ref)?;
}
if let Some(constructor) = &mut node.constructor {
self.visit_function_expression_mut(constructor)?;
}
for element in &mut *node.elements {
self.visit_class_element_mut(element)?;
}
std::mem::swap(&mut self.scope, &mut node.name_scope);
node.name_scope.reorder_binding_indices();
ControlFlow::Continue(())
}
fn visit_class_expression_mut(
&mut self,
node: &'ast mut ClassExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(name_scope) = &mut node.name_scope {
if self.direct_eval {
name_scope.escape_all_bindings();
}
name_scope.escape_all_bindings();
std::mem::swap(&mut self.scope, name_scope);
}
if let Some(super_ref) = &mut node.super_ref {
self.visit_expression_mut(super_ref)?;
}
if let Some(constructor) = &mut node.constructor {
self.visit_function_expression_mut(constructor)?;
}
for element in &mut *node.elements {
self.visit_class_element_mut(element)?;
}
if let Some(name_scope) = &mut node.name_scope {
std::mem::swap(&mut self.scope, name_scope);
name_scope.reorder_binding_indices();
}
ControlFlow::Continue(())
}
fn visit_class_element_mut(
&mut self,
node: &'ast mut ClassElement,
) -> ControlFlow<Self::BreakTy> {
match node {
ClassElement::MethodDefinition(node) => self.visit_function_like(
&mut node.parameters,
&mut node.body,
&mut node.scopes,
node.contains_direct_eval,
),
ClassElement::FieldDefinition(field) | ClassElement::StaticFieldDefinition(field) => {
self.visit_property_name_mut(&mut field.name)?;
if let Some(e) = &mut field.initializer {
self.visit_expression_mut(e)?;
}
ControlFlow::Continue(())
}
ClassElement::PrivateFieldDefinition(field)
| ClassElement::PrivateStaticFieldDefinition(field) => {
if let Some(e) = &mut field.initializer {
self.visit_expression_mut(e)?;
}
ControlFlow::Continue(())
}
ClassElement::StaticBlock(node) => {
let contains_direct_eval = contains(node.statements(), ContainsSymbol::DirectEval);
self.visit_function_like(
&mut FormalParameterList::default(),
&mut node.body,
&mut node.scopes,
contains_direct_eval,
)
}
}
}
fn visit_object_method_definition_mut(
&mut self,
node: &'ast mut ObjectMethodDefinition,
) -> ControlFlow<Self::BreakTy> {
self.visit_property_name_mut(&mut node.name)?;
self.visit_function_like(
&mut node.parameters,
&mut node.body,
&mut node.scopes,
node.contains_direct_eval,
)
}
fn visit_export_declaration_mut(
&mut self,
node: &'ast mut ExportDeclaration,
) -> ControlFlow<Self::BreakTy> {
match node {
ExportDeclaration::ReExport { specifier, kind } => {
self.visit_module_specifier_mut(specifier)?;
self.visit_re_export_kind_mut(kind)
}
ExportDeclaration::List(list) => {
for item in &mut **list {
self.visit_export_specifier_mut(item)?;
}
ControlFlow::Continue(())
}
ExportDeclaration::VarStatement(var) => self.visit_var_declaration_mut(var),
ExportDeclaration::Declaration(decl) => self.visit_declaration_mut(decl),
ExportDeclaration::DefaultFunctionDeclaration(f) => {
self.visit_function_declaration_mut(f)
}
ExportDeclaration::DefaultGeneratorDeclaration(g) => {
self.visit_generator_declaration_mut(g)
}
ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => {
self.visit_async_function_declaration_mut(af)
}
ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => {
self.visit_async_generator_declaration_mut(ag)
}
ExportDeclaration::DefaultClassDeclaration(c) => self.visit_class_declaration_mut(c),
ExportDeclaration::DefaultAssignmentExpression(expr) => {
let name = Sym::DEFAULT_EXPORT.to_js_string(self.interner);
drop(self.scope.create_mutable_binding(name.clone(), false));
self.scope.access_binding(&name, true);
self.visit_expression_mut(expr)
}
}
}
fn visit_module_mut(&mut self, node: &'ast mut Module) -> ControlFlow<Self::BreakTy> {
let mut scope = node.scope.clone();
scope.escape_all_bindings();
std::mem::swap(&mut self.scope, &mut scope);
self.visit_module_item_list_mut(&mut node.items)?;
std::mem::swap(&mut self.scope, &mut scope);
scope.reorder_binding_indices();
ControlFlow::Continue(())
}
}
impl BindingEscapeAnalyzer<'_> {
fn visit_function_like(
&mut self,
parameters: &mut FormalParameterList,
body: &mut FunctionBody,
scopes: &mut FunctionScopes,
contains_direct_eval: bool,
) -> ControlFlow<&'static str> {
let direct_eval_old = self.direct_eval;
self.direct_eval = contains_direct_eval || self.direct_eval;
if self.direct_eval {
scopes.escape_all_bindings();
}
let mut scope = scopes.parameter_scope();
std::mem::swap(&mut self.scope, &mut scope);
self.visit_formal_parameter_list_mut(parameters)?;
std::mem::swap(&mut self.scope, &mut scope);
scope = scopes.body_scope();
std::mem::swap(&mut self.scope, &mut scope);
self.visit_function_body_mut(body)?;
std::mem::swap(&mut self.scope, &mut scope);
if scopes.arguments_object_accessed() && scopes.mapped_arguments_object {
let parameter_names = bound_names(parameters);
for name in parameter_names {
scopes
.parameter_scope()
.access_binding(&name.to_js_string(self.interner), true);
}
}
scopes.reorder_binding_indices();
self.direct_eval = direct_eval_old;
ControlFlow::Continue(())
}
}
struct BindingCollectorVisitor<'interner> {
strict: bool,
eval: bool,
scope: Scope,
in_arrow: bool,
interner: &'interner Interner,
}
impl<'ast> VisitorMut<'ast> for BindingCollectorVisitor<'_> {
type BreakTy = &'static str;
fn visit_this_mut(
&mut self,
_node: &'ast mut crate::expression::This,
) -> ControlFlow<Self::BreakTy> {
if self.in_arrow {
self.scope.escape_this_in_enclosing_function_scope();
}
ControlFlow::Continue(())
}
fn visit_function_declaration_mut(
&mut self,
node: &'ast mut FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
None,
&mut None,
strict,
false,
)
}
fn visit_generator_declaration_mut(
&mut self,
node: &'ast mut GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
None,
&mut None,
strict,
false,
)
}
fn visit_async_function_declaration_mut(
&mut self,
node: &'ast mut AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
None,
&mut None,
strict,
false,
)
}
fn visit_async_generator_declaration_mut(
&mut self,
node: &'ast mut AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
None,
&mut None,
strict,
false,
)
}
fn visit_function_expression_mut(
&mut self,
node: &'ast mut FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
let name = if node.has_binding_identifier {
node.name()
} else {
None
};
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
name,
&mut node.name_scope,
strict,
false,
)
}
fn visit_generator_expression_mut(
&mut self,
node: &'ast mut GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
let name = if node.has_binding_identifier {
node.name()
} else {
None
};
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
name,
&mut node.name_scope,
strict,
false,
)
}
fn visit_async_function_expression_mut(
&mut self,
node: &'ast mut AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
let name = if node.has_binding_identifier {
node.name()
} else {
None
};
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
name,
&mut node.name_scope,
strict,
false,
)
}
fn visit_async_generator_expression_mut(
&mut self,
node: &'ast mut AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
let name = if node.has_binding_identifier {
node.name()
} else {
None
};
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
name,
&mut node.name_scope,
strict,
false,
)
}
fn visit_arrow_function_mut(
&mut self,
node: &'ast mut ArrowFunction,
) -> ControlFlow<Self::BreakTy> {
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
None,
&mut None,
strict,
true,
)
}
fn visit_async_arrow_function_mut(
&mut self,
node: &'ast mut AsyncArrowFunction,
) -> ControlFlow<Self::BreakTy> {
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
None,
&mut None,
strict,
true,
)
}
fn visit_class_declaration_mut(
&mut self,
node: &'ast mut ClassDeclaration,
) -> ControlFlow<Self::BreakTy> {
let mut name_scope = Scope::new(self.scope.clone(), false);
let name = node.name().to_js_string(self.interner);
name_scope.create_immutable_binding(name, true);
std::mem::swap(&mut self.scope, &mut name_scope);
if let Some(super_ref) = &mut node.super_ref {
self.visit_expression_mut(super_ref)?;
}
if let Some(constructor) = &mut node.constructor {
self.visit_function_expression_mut(constructor)?;
}
for element in &mut *node.elements {
self.visit_class_element_mut(element)?;
}
std::mem::swap(&mut self.scope, &mut name_scope);
node.name_scope = name_scope;
ControlFlow::Continue(())
}
fn visit_class_expression_mut(
&mut self,
node: &'ast mut ClassExpression,
) -> ControlFlow<Self::BreakTy> {
let mut name_scope = None;
if let Some(name) = node.name
&& node.name_scope.is_some()
{
let mut scope = Scope::new(self.scope.clone(), false);
let name = name.to_js_string(self.interner);
scope.create_immutable_binding(name, true);
node.name_scope = Some(scope.clone());
std::mem::swap(&mut self.scope, &mut scope);
name_scope = Some(scope);
}
if let Some(super_ref) = &mut node.super_ref {
self.visit_expression_mut(super_ref)?;
}
if let Some(constructor) = &mut node.constructor {
self.visit_function_expression_mut(constructor)?;
}
for element in &mut *node.elements {
self.visit_class_element_mut(element)?;
}
if let Some(mut scope) = name_scope {
std::mem::swap(&mut self.scope, &mut scope);
}
ControlFlow::Continue(())
}
fn visit_class_element_mut(
&mut self,
node: &'ast mut ClassElement,
) -> ControlFlow<Self::BreakTy> {
match node {
ClassElement::MethodDefinition(node) => {
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
None,
&mut None,
strict,
false,
)
}
ClassElement::FieldDefinition(field) | ClassElement::StaticFieldDefinition(field) => {
self.visit_property_name_mut(&mut field.name)?;
let mut scope = Scope::new(self.scope.clone(), true);
std::mem::swap(&mut self.scope, &mut scope);
if let Some(e) = &mut field.initializer {
self.visit_expression_mut(e)?;
}
std::mem::swap(&mut self.scope, &mut scope);
field.scope = scope;
ControlFlow::Continue(())
}
ClassElement::PrivateFieldDefinition(field)
| ClassElement::PrivateStaticFieldDefinition(field) => {
let mut scope = Scope::new(self.scope.clone(), true);
std::mem::swap(&mut self.scope, &mut scope);
if let Some(e) = &mut field.initializer {
self.visit_expression_mut(e)?;
}
std::mem::swap(&mut self.scope, &mut scope);
field.scope = scope;
ControlFlow::Continue(())
}
ClassElement::StaticBlock(node) => {
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut FormalParameterList::default(),
&mut node.scopes,
None,
&mut None,
strict,
false,
)
}
}
}
fn visit_object_method_definition_mut(
&mut self,
node: &'ast mut ObjectMethodDefinition,
) -> ControlFlow<Self::BreakTy> {
match &mut node.name {
PropertyName::Literal(_) => {}
PropertyName::Computed(name) => {
self.visit_expression_mut(name)?;
}
}
let strict = node.body.strict();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
None,
&mut None,
strict,
false,
)
}
fn visit_block_mut(&mut self, node: &'ast mut Block) -> ControlFlow<Self::BreakTy> {
let mut scope = block_declaration_instantiation(node, self.scope.clone(), self.interner);
if let Some(scope) = &mut scope {
std::mem::swap(&mut self.scope, scope);
}
self.visit_statement_list_mut(&mut node.statements)?;
if let Some(scope) = &mut scope {
std::mem::swap(&mut self.scope, scope);
}
node.scope = scope;
ControlFlow::Continue(())
}
fn visit_switch_mut(&mut self, node: &'ast mut Switch) -> ControlFlow<Self::BreakTy> {
self.visit_expression_mut(&mut node.val)?;
let mut scope = block_declaration_instantiation(node, self.scope.clone(), self.interner);
if let Some(scope) = &mut scope {
std::mem::swap(&mut self.scope, scope);
}
for case in &mut *node.cases {
self.visit_case_mut(case)?;
}
if let Some(scope) = &mut scope {
std::mem::swap(&mut self.scope, scope);
}
node.scope = scope;
ControlFlow::Continue(())
}
fn visit_with_mut(&mut self, node: &'ast mut With) -> ControlFlow<Self::BreakTy> {
self.visit_expression_mut(&mut node.expression)?;
let mut scope = Scope::new(self.scope.clone(), false);
std::mem::swap(&mut self.scope, &mut scope);
self.visit_statement_mut(&mut node.statement)?;
std::mem::swap(&mut self.scope, &mut scope);
node.scope = scope;
ControlFlow::Continue(())
}
fn visit_catch_mut(&mut self, node: &'ast mut Catch) -> ControlFlow<Self::BreakTy> {
let mut scope = Scope::new(self.scope.clone(), false);
if let Some(binding) = node.parameter() {
match binding {
Binding::Identifier(ident) => {
let ident = ident.to_js_string(self.interner);
drop(scope.create_mutable_binding(ident.clone(), false));
}
Binding::Pattern(pattern) => {
for ident in bound_names(pattern) {
let ident = ident.to_js_string(self.interner);
drop(scope.create_mutable_binding(ident, false));
}
}
}
}
std::mem::swap(&mut self.scope, &mut scope);
if let Some(binding) = &mut node.parameter {
self.visit_binding_mut(binding)?;
}
self.visit_block_mut(&mut node.block)?;
std::mem::swap(&mut self.scope, &mut scope);
node.scope = scope;
ControlFlow::Continue(())
}
fn visit_for_loop_mut(&mut self, node: &'ast mut ForLoop) -> ControlFlow<Self::BreakTy> {
let scope = match &mut node.inner.init {
Some(ForLoopInitializer::Lexical(decl)) => {
let mut scope = Scope::new(self.scope.clone(), false);
let names = bound_names(&decl.declaration);
if decl.declaration.is_const() {
for name in &names {
let name = name.to_js_string(self.interner);
scope.create_immutable_binding(name, true);
}
} else {
for name in &names {
let name = name.to_js_string(self.interner);
drop(scope.create_mutable_binding(name, false));
}
}
decl.scope = scope.clone();
std::mem::swap(&mut self.scope, &mut scope);
Some(scope)
}
_ => None,
};
if let Some(fli) = &mut node.inner.init {
self.visit_for_loop_initializer_mut(fli)?;
}
if let Some(expr) = &mut node.inner.condition {
self.visit_expression_mut(expr)?;
}
if let Some(expr) = &mut node.inner.final_expr {
self.visit_expression_mut(expr)?;
}
self.visit_statement_mut(&mut node.inner.body)?;
if let Some(mut scope) = scope {
std::mem::swap(&mut self.scope, &mut scope);
}
ControlFlow::Continue(())
}
fn visit_for_in_loop_mut(&mut self, node: &'ast mut ForInLoop) -> ControlFlow<Self::BreakTy> {
let initializer_bound_names = match node.initializer() {
IterableLoopInitializer::Let(declaration)
| IterableLoopInitializer::Const(declaration) => bound_names(declaration),
_ => Vec::new(),
};
if initializer_bound_names.is_empty() {
self.visit_expression_mut(&mut node.target)?;
} else {
let mut scope = Scope::new(self.scope.clone(), false);
for name in &initializer_bound_names {
let name = name.to_js_string(self.interner);
drop(scope.create_mutable_binding(name, false));
}
std::mem::swap(&mut self.scope, &mut scope);
self.visit_expression_mut(&mut node.target)?;
std::mem::swap(&mut self.scope, &mut scope);
node.target_scope = Some(scope);
}
let scope = match node.initializer() {
IterableLoopInitializer::Let(declaration) => {
let scope = Scope::new(self.scope.clone(), false);
match declaration {
Binding::Identifier(ident) => {
let ident = ident.to_js_string(self.interner);
drop(scope.create_mutable_binding(ident.clone(), false));
}
Binding::Pattern(pattern) => {
for ident in bound_names(pattern) {
let ident = ident.to_js_string(self.interner);
drop(scope.create_mutable_binding(ident, false));
}
}
}
Some(scope)
}
IterableLoopInitializer::Const(declaration) => {
let scope = Scope::new(self.scope.clone(), false);
match declaration {
Binding::Identifier(ident) => {
let ident = ident.to_js_string(self.interner);
scope.create_immutable_binding(ident.clone(), true);
}
Binding::Pattern(pattern) => {
for ident in bound_names(pattern) {
let ident = ident.to_js_string(self.interner);
scope.create_immutable_binding(ident, true);
}
}
}
Some(scope)
}
_ => None,
};
if let Some(mut scope) = scope {
std::mem::swap(&mut self.scope, &mut scope);
self.visit_iterable_loop_initializer_mut(&mut node.initializer)?;
self.visit_statement_mut(&mut node.body)?;
std::mem::swap(&mut self.scope, &mut scope);
node.scope = Some(scope);
} else {
self.visit_iterable_loop_initializer_mut(&mut node.initializer)?;
self.visit_statement_mut(&mut node.body)?;
}
ControlFlow::Continue(())
}
fn visit_for_of_loop_mut(&mut self, node: &'ast mut ForOfLoop) -> ControlFlow<Self::BreakTy> {
let initializer_bound_names = match node.initializer() {
IterableLoopInitializer::Let(declaration)
| IterableLoopInitializer::Const(declaration) => bound_names(declaration),
_ => Vec::new(),
};
if initializer_bound_names.is_empty() {
self.visit_expression_mut(&mut node.iterable)?;
} else {
let mut scope = Scope::new(self.scope.clone(), false);
for name in &initializer_bound_names {
let name = name.to_js_string(self.interner);
drop(scope.create_mutable_binding(name, false));
}
std::mem::swap(&mut self.scope, &mut scope);
self.visit_expression_mut(&mut node.iterable)?;
std::mem::swap(&mut self.scope, &mut scope);
node.iterable_scope = Some(scope);
}
let scope = match node.initializer() {
IterableLoopInitializer::Let(declaration) => {
let scope = Scope::new(self.scope.clone(), false);
match declaration {
Binding::Identifier(ident) => {
let ident = ident.to_js_string(self.interner);
drop(scope.create_mutable_binding(ident.clone(), false));
}
Binding::Pattern(pattern) => {
for ident in bound_names(pattern) {
let ident = ident.to_js_string(self.interner);
drop(scope.create_mutable_binding(ident, false));
}
}
}
Some(scope)
}
IterableLoopInitializer::Const(declaration) => {
let scope = Scope::new(self.scope.clone(), false);
match declaration {
Binding::Identifier(ident) => {
let ident = ident.to_js_string(self.interner);
scope.create_immutable_binding(ident.clone(), true);
}
Binding::Pattern(pattern) => {
for ident in bound_names(pattern) {
let ident = ident.to_js_string(self.interner);
scope.create_immutable_binding(ident, true);
}
}
}
Some(scope)
}
_ => None,
};
if let Some(mut scope) = scope {
std::mem::swap(&mut self.scope, &mut scope);
self.visit_iterable_loop_initializer_mut(&mut node.init)?;
self.visit_statement_mut(&mut node.body)?;
std::mem::swap(&mut self.scope, &mut scope);
node.scope = Some(scope);
} else {
self.visit_iterable_loop_initializer_mut(&mut node.init)?;
self.visit_statement_mut(&mut node.body)?;
}
ControlFlow::Continue(())
}
fn visit_module_mut(&mut self, node: &'ast mut Module) -> ControlFlow<Self::BreakTy> {
let mut scope = Scope::new(self.scope.clone(), true);
module_instantiation(node, &scope, self.interner);
std::mem::swap(&mut self.scope, &mut scope);
self.visit_module_item_list_mut(&mut node.items)?;
std::mem::swap(&mut self.scope, &mut scope);
node.scope = scope;
ControlFlow::Continue(())
}
fn visit_script_mut(&mut self, node: &'ast mut Script) -> ControlFlow<Self::BreakTy> {
if self.eval {
self.visit_statement_list_mut(node.statements_mut())?;
} else {
match global_declaration_instantiation(node, &self.scope, self.interner) {
Ok(()) => {
self.visit_statement_list_mut(node.statements_mut())?;
}
Err(e) => return ControlFlow::Break(e),
}
}
ControlFlow::Continue(())
}
}
impl BindingCollectorVisitor<'_> {
#[allow(clippy::too_many_arguments)]
fn visit_function_like(
&mut self,
body: &mut FunctionBody,
parameters: &mut FormalParameterList,
scopes: &mut FunctionScopes,
name: Option<Identifier>,
name_scope: &mut Option<Scope>,
strict: bool,
arrow: bool,
) -> ControlFlow<&'static str> {
let strict = self.strict || strict;
let old_in_arrow = self.in_arrow;
self.in_arrow = arrow;
let function_scope = if let Some(name) = name {
let scope = Scope::new(self.scope.clone(), false);
let name = name.to_js_string(self.interner);
scope.create_immutable_binding(name, strict);
*name_scope = Some(scope.clone());
Scope::new(scope, true)
} else {
Scope::new(self.scope.clone(), true)
};
let function_scopes = function_declaration_instantiation(
body,
parameters,
arrow,
strict,
function_scope.clone(),
self.interner,
);
let mut params_scope = function_scopes.parameter_scope();
let mut body_scope = function_scopes.body_scope();
std::mem::swap(&mut self.scope, &mut params_scope);
self.visit_formal_parameter_list_mut(parameters)?;
std::mem::swap(&mut self.scope, &mut params_scope);
std::mem::swap(&mut self.scope, &mut body_scope);
self.visit_function_body_mut(body)?;
std::mem::swap(&mut self.scope, &mut body_scope);
*scopes = function_scopes;
self.in_arrow = old_in_arrow;
ControlFlow::Continue(())
}
}
pub(crate) fn optimize_scope_indicies<'a, N>(node: &'a mut N, scope: &Scope)
where
&'a mut N: Into<NodeRefMut<'a>>,
{
let mut visitor = ScopeIndexVisitor {
index: scope.scope_index(),
};
let _ = visitor.visit(node.into());
}
struct ScopeIndexVisitor {
index: u32,
}
impl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {
type BreakTy = ();
fn visit_function_declaration_mut(
&mut self,
node: &'ast mut FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
false,
contains_direct_eval,
)
}
fn visit_generator_declaration_mut(
&mut self,
node: &'ast mut GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
false,
contains_direct_eval,
)
}
fn visit_async_function_declaration_mut(
&mut self,
node: &'ast mut AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
false,
contains_direct_eval,
)
}
fn visit_async_generator_declaration_mut(
&mut self,
node: &'ast mut AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
false,
contains_direct_eval,
)
}
fn visit_function_expression_mut(
&mut self,
node: &'ast mut FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut node.name_scope,
false,
contains_direct_eval,
)
}
fn visit_generator_expression_mut(
&mut self,
node: &'ast mut GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut node.name_scope,
false,
contains_direct_eval,
)
}
fn visit_async_function_expression_mut(
&mut self,
node: &'ast mut AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut node.name_scope,
false,
contains_direct_eval,
)
}
fn visit_async_generator_expression_mut(
&mut self,
node: &'ast mut AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut node.name_scope,
false,
contains_direct_eval,
)
}
fn visit_arrow_function_mut(
&mut self,
node: &'ast mut ArrowFunction,
) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
true,
contains_direct_eval,
)
}
fn visit_async_arrow_function_mut(
&mut self,
node: &'ast mut AsyncArrowFunction,
) -> ControlFlow<Self::BreakTy> {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
true,
contains_direct_eval,
)
}
fn visit_class_declaration_mut(
&mut self,
node: &'ast mut ClassDeclaration,
) -> ControlFlow<Self::BreakTy> {
let index = self.index;
if !node.name_scope.all_bindings_local() {
self.index += 1;
}
node.name_scope.set_index(self.index);
if let Some(super_ref) = &mut node.super_ref {
self.visit_expression_mut(super_ref)?;
}
if let Some(constructor) = &mut node.constructor {
self.visit_function_expression_mut(constructor)?;
}
for element in &mut *node.elements {
self.visit_class_element_mut(element)?;
}
self.index = index;
ControlFlow::Continue(())
}
fn visit_class_expression_mut(
&mut self,
node: &'ast mut ClassExpression,
) -> ControlFlow<Self::BreakTy> {
let index = self.index;
if let Some(scope) = &node.name_scope {
if !scope.all_bindings_local() {
self.index += 1;
}
scope.set_index(self.index);
}
if let Some(super_ref) = &mut node.super_ref {
self.visit_expression_mut(super_ref)?;
}
if let Some(constructor) = &mut node.constructor {
self.visit_function_expression_mut(constructor)?;
}
for element in &mut *node.elements {
self.visit_class_element_mut(element)?;
}
self.index = index;
ControlFlow::Continue(())
}
fn visit_class_element_mut(
&mut self,
node: &'ast mut ClassElement,
) -> ControlFlow<Self::BreakTy> {
match node {
ClassElement::MethodDefinition(node) => {
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
false,
contains_direct_eval,
)
}
ClassElement::FieldDefinition(field) | ClassElement::StaticFieldDefinition(field) => {
self.visit_property_name_mut(&mut field.name)?;
let index = self.index;
self.index += 1;
field.scope.set_index(self.index);
if let Some(e) = &mut field.initializer {
self.visit_expression_mut(e)?;
}
self.index = index;
ControlFlow::Continue(())
}
ClassElement::PrivateFieldDefinition(field)
| ClassElement::PrivateStaticFieldDefinition(field) => {
let index = self.index;
self.index += 1;
field.scope.set_index(self.index);
if let Some(e) = &mut field.initializer {
self.visit_expression_mut(e)?;
}
self.index = index;
ControlFlow::Continue(())
}
ClassElement::StaticBlock(node) => {
let contains_direct_eval = contains(node.statements(), ContainsSymbol::DirectEval);
self.visit_function_like(
&mut node.body,
&mut FormalParameterList::default(),
&mut node.scopes,
&mut None,
false,
contains_direct_eval,
)
}
}
}
fn visit_object_method_definition_mut(
&mut self,
node: &'ast mut ObjectMethodDefinition,
) -> ControlFlow<Self::BreakTy> {
match &mut node.name {
PropertyName::Literal(_) => {}
PropertyName::Computed(name) => {
self.visit_expression_mut(name)?;
}
}
let contains_direct_eval = node.contains_direct_eval();
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
false,
contains_direct_eval,
)
}
fn visit_block_mut(&mut self, node: &'ast mut Block) -> ControlFlow<Self::BreakTy> {
let index = self.index;
if let Some(scope) = &node.scope {
if !scope.all_bindings_local() {
self.index += 1;
}
scope.set_index(self.index);
}
self.visit_statement_list_mut(&mut node.statements)?;
self.index = index;
ControlFlow::Continue(())
}
fn visit_switch_mut(&mut self, node: &'ast mut Switch) -> ControlFlow<Self::BreakTy> {
let index = self.index;
self.visit_expression_mut(&mut node.val)?;
if let Some(scope) = &node.scope {
if !scope.all_bindings_local() {
self.index += 1;
}
scope.set_index(self.index);
}
for case in &mut *node.cases {
self.visit_case_mut(case)?;
}
self.index = index;
ControlFlow::Continue(())
}
fn visit_with_mut(&mut self, node: &'ast mut With) -> ControlFlow<Self::BreakTy> {
let index = self.index;
self.visit_expression_mut(&mut node.expression)?;
self.index += 1;
node.scope.set_index(self.index);
self.visit_statement_mut(&mut node.statement)?;
self.index = index;
ControlFlow::Continue(())
}
fn visit_catch_mut(&mut self, node: &'ast mut Catch) -> ControlFlow<Self::BreakTy> {
let index = self.index;
if !node.scope.all_bindings_local() {
self.index += 1;
}
node.scope.set_index(self.index);
if let Some(binding) = &mut node.parameter {
self.visit_binding_mut(binding)?;
}
self.visit_block_mut(&mut node.block)?;
self.index = index;
ControlFlow::Continue(())
}
fn visit_for_loop_mut(&mut self, node: &'ast mut ForLoop) -> ControlFlow<Self::BreakTy> {
let index = self.index;
if let Some(ForLoopInitializer::Lexical(decl)) = &mut node.inner.init {
if !decl.scope.all_bindings_local() {
self.index += 1;
}
decl.scope.set_index(self.index);
}
if let Some(fli) = &mut node.inner.init {
self.visit_for_loop_initializer_mut(fli)?;
}
if let Some(expr) = &mut node.inner.condition {
self.visit_expression_mut(expr)?;
}
if let Some(expr) = &mut node.inner.final_expr {
self.visit_expression_mut(expr)?;
}
self.visit_statement_mut(&mut node.inner.body)?;
self.index = index;
ControlFlow::Continue(())
}
fn visit_for_in_loop_mut(&mut self, node: &'ast mut ForInLoop) -> ControlFlow<Self::BreakTy> {
{
let index = self.index;
if let Some(scope) = &node.target_scope {
if !scope.all_bindings_local() {
self.index += 1;
}
scope.set_index(self.index);
}
self.visit_expression_mut(&mut node.target)?;
self.index = index;
}
let index = self.index;
if let Some(scope) = &node.scope {
if !scope.all_bindings_local() {
self.index += 1;
}
scope.set_index(self.index);
}
self.visit_iterable_loop_initializer_mut(&mut node.initializer)?;
self.visit_statement_mut(&mut node.body)?;
self.index = index;
ControlFlow::Continue(())
}
fn visit_for_of_loop_mut(&mut self, node: &'ast mut ForOfLoop) -> ControlFlow<Self::BreakTy> {
{
let index = self.index;
if let Some(scope) = &node.iterable_scope {
if !scope.all_bindings_local() {
self.index += 1;
}
scope.set_index(self.index);
}
self.visit_expression_mut(&mut node.iterable)?;
self.index = index;
}
let index = self.index;
if let Some(scope) = &node.scope {
if !scope.all_bindings_local() {
self.index += 1;
}
scope.set_index(self.index);
}
self.visit_iterable_loop_initializer_mut(&mut node.init)?;
self.visit_statement_mut(&mut node.body)?;
self.index = index;
ControlFlow::Continue(())
}
}
impl ScopeIndexVisitor {
fn visit_function_like(
&mut self,
body: &mut FunctionBody,
parameters: &mut FormalParameterList,
scopes: &mut FunctionScopes,
name_scope: &mut Option<Scope>,
arrow: bool,
contains_direct_eval: bool,
) -> ControlFlow<()> {
let index = self.index;
if let Some(scope) = name_scope {
if !scope.all_bindings_local() {
self.index += 1;
}
scope.set_index(self.index);
}
if contains_direct_eval || !scopes.function_scope().all_bindings_local() {
scopes.requires_function_scope = true;
self.index += 1;
} else if !arrow {
assert!(scopes.function_scope().is_function());
scopes.requires_function_scope = scopes.function_scope().escaped_this()
|| contains(parameters, ContainsSymbol::Super)
|| contains(body, ContainsSymbol::Super)
|| contains(parameters, ContainsSymbol::NewTarget)
|| contains(body, ContainsSymbol::NewTarget);
self.index += u32::from(scopes.requires_function_scope);
}
scopes.function_scope.set_index(self.index);
if let Some(scope) = &scopes.parameters_eval_scope {
if !scope.all_bindings_local() {
self.index += 1;
}
scope.set_index(self.index);
}
self.visit_formal_parameter_list_mut(parameters)?;
if let Some(scope) = &scopes.parameters_scope {
if !scope.all_bindings_local() {
self.index += 1;
}
scope.set_index(self.index);
}
if let Some(scope) = &scopes.lexical_scope {
if !scope.all_bindings_local() {
self.index += 1;
}
scope.set_index(self.index);
}
self.visit_function_body_mut(body)?;
self.index = index;
ControlFlow::Continue(())
}
}
fn global_declaration_instantiation(
script: &Script,
env: &Scope,
interner: &Interner,
) -> Result<(), &'static str> {
let lex_names = lexically_declared_names(script);
let var_names = var_declared_names(script);
for name in lex_names {
let name = name.to_js_string(interner);
if env.has_binding(&name) {
return Err("duplicate lexical declaration");
}
}
for name in var_names {
let name = name.to_js_string(interner);
if env.has_lex_binding(&name) {
return Err("duplicate lexical declaration");
}
}
for statement in &**script.statements() {
if let StatementListItem::Declaration(declaration) = statement {
match declaration.as_ref() {
Declaration::ClassDeclaration(class) => {
for name in bound_names(class.as_ref()) {
let name = name.to_js_string(interner);
drop(env.create_mutable_binding(name, false));
}
}
Declaration::Lexical(LexicalDeclaration::Let(declaration)) => {
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
drop(env.create_mutable_binding(name, false));
}
}
Declaration::Lexical(LexicalDeclaration::Const(declaration)) => {
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
env.create_immutable_binding(name, true);
}
}
_ => {}
}
}
}
Ok(())
}
fn block_declaration_instantiation<'a, N>(
block: &'a N,
scope: Scope,
interner: &Interner,
) -> Option<Scope>
where
&'a N: Into<NodeRef<'a>>,
{
let scope = Scope::new(scope, false);
let declarations = lexically_scoped_declarations(block);
for d in &declarations {
if let LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::Const(d)) = d {
for dn in bound_names::<'_, VariableList>(d) {
let dn = dn.to_js_string(interner);
scope.create_immutable_binding(dn, true);
}
}
else {
for dn in d.bound_names() {
let dn = dn.to_js_string(interner);
#[cfg(not(feature = "annex-b"))]
drop(scope.create_mutable_binding(dn, false));
#[cfg(feature = "annex-b")]
if !scope.has_binding(&dn) {
drop(scope.create_mutable_binding(dn, false));
}
}
}
}
if scope.num_bindings() > 0 {
Some(scope)
} else {
None
}
}
fn function_declaration_instantiation(
body: &FunctionBody,
formals: &FormalParameterList,
arrow: bool,
strict: bool,
function_scope: Scope,
interner: &Interner,
) -> FunctionScopes {
let mut scopes = FunctionScopes {
function_scope,
parameters_eval_scope: None,
parameters_scope: None,
lexical_scope: None,
mapped_arguments_object: false,
requires_function_scope: false,
};
let mut parameter_names = bound_names(formals);
let has_parameter_expressions = formals.has_expressions();
let var_names = var_declared_names(body);
let var_declarations = var_scoped_declarations(body);
let lexical_names = lexically_declared_names(body);
let mut function_names = Vec::new();
for declaration in var_declarations.iter().rev() {
let name = match declaration {
VarScopedDeclaration::FunctionDeclaration(f) => f.name(),
VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => continue,
};
if !function_names.contains(&name.sym()) {
function_names.push(name.sym());
}
}
function_names.reverse();
let mut arguments_object_needed = true;
let arguments = Sym::ARGUMENTS;
if arrow || parameter_names.contains(&arguments) {
arguments_object_needed = false;
}
else if !has_parameter_expressions {
if function_names.contains(&arguments) || lexical_names.contains(&arguments) {
arguments_object_needed = false;
}
}
let env = if strict || !has_parameter_expressions {
scopes.function_scope.clone()
}
else {
let scope = Scope::new(scopes.function_scope.clone(), false);
scopes.parameters_eval_scope = Some(scope.clone());
scope
};
if arguments_object_needed {
let arguments = arguments.to_js_string(interner);
if strict {
env.create_immutable_binding(arguments.clone(), false);
}
else {
drop(env.create_mutable_binding(arguments.clone(), false));
}
}
for param_name in ¶meter_names {
let param_name = param_name.to_js_string(interner);
let already_declared = env.has_binding(¶m_name);
if !already_declared {
drop(env.create_mutable_binding(param_name.clone(), false));
if arguments_object_needed && !strict && formals.is_simple() {
scopes.mapped_arguments_object = true;
}
}
}
if arguments_object_needed {
parameter_names.push(arguments);
}
let parameter_bindings = parameter_names.clone();
#[allow(unused_variables, unused_mut)]
let (mut instantiated_var_names, mut var_env) = if has_parameter_expressions {
let var_env = Scope::new(env.clone(), false);
scopes.parameters_scope = Some(var_env.clone());
let mut instantiated_var_names = Vec::new();
for n in var_names {
if !instantiated_var_names.contains(&n) {
instantiated_var_names.push(n);
let n_string = n.to_js_string(interner);
drop(var_env.create_mutable_binding(n_string, false));
}
}
(instantiated_var_names, var_env)
} else {
let mut instantiated_var_names = parameter_bindings;
for n in var_names {
if !instantiated_var_names.contains(&n) {
instantiated_var_names.push(n);
let n = n.to_js_string(interner);
drop(env.create_mutable_binding(n, true));
}
}
(instantiated_var_names, env)
};
#[cfg(feature = "annex-b")]
if !strict {
for f in annex_b_function_declarations_names(body) {
if !lexical_names.contains(&f) && !parameter_names.contains(&f) {
if !instantiated_var_names.contains(&f) && f != arguments {
let f_string = f.to_js_string(interner);
drop(var_env.create_mutable_binding(f_string, false));
instantiated_var_names.push(f);
}
}
}
}
let lex_env = if strict {
var_env
} else {
let lex_env = Scope::new(var_env, false);
scopes.lexical_scope = Some(lex_env.clone());
lex_env
};
for statement in body.statements() {
if let StatementListItem::Declaration(declaration) = statement {
match declaration.as_ref() {
Declaration::ClassDeclaration(class) => {
for name in bound_names(class.as_ref()) {
let name = name.to_js_string(interner);
drop(lex_env.create_mutable_binding(name, false));
}
}
Declaration::Lexical(LexicalDeclaration::Let(declaration)) => {
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
drop(lex_env.create_mutable_binding(name, false));
}
}
Declaration::Lexical(LexicalDeclaration::Const(declaration)) => {
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
lex_env.create_immutable_binding(name, true);
}
}
_ => {}
}
}
}
if let Some(lexical_scope) = &scopes.lexical_scope
&& lexical_scope.num_bindings() == 0
{
scopes.lexical_scope = None;
}
scopes
}
fn module_instantiation(module: &Module, env: &Scope, interner: &Interner) {
for entry in module.items().import_entries() {
let local_name = entry.local_name().to_js_string(interner);
env.create_immutable_binding(local_name, true);
}
let var_declarations = var_scoped_declarations(module);
let mut declared_var_names = Vec::new();
for var in var_declarations {
for name in var.bound_names() {
let name = name.to_js_string(interner);
if !declared_var_names.contains(&name) {
drop(env.create_mutable_binding(name.clone(), false));
declared_var_names.push(name);
}
}
}
let lex_declarations = lexically_scoped_declarations(module);
for declaration in lex_declarations {
match declaration {
LexicallyScopedDeclaration::FunctionDeclaration(f) => {
let name = bound_names(f)[0].to_js_string(interner);
drop(env.create_mutable_binding(name, false));
}
LexicallyScopedDeclaration::GeneratorDeclaration(g) => {
let name = bound_names(g)[0].to_js_string(interner);
drop(env.create_mutable_binding(name, false));
}
LexicallyScopedDeclaration::AsyncFunctionDeclaration(af) => {
let name = bound_names(af)[0].to_js_string(interner);
drop(env.create_mutable_binding(name, false));
}
LexicallyScopedDeclaration::AsyncGeneratorDeclaration(ag) => {
let name = bound_names(ag)[0].to_js_string(interner);
drop(env.create_mutable_binding(name, false));
}
LexicallyScopedDeclaration::ClassDeclaration(class) => {
for name in bound_names(class) {
let name = name.to_js_string(interner);
drop(env.create_mutable_binding(name, false));
}
}
LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::Const(c)) => {
for name in bound_names(c) {
let name = name.to_js_string(interner);
env.create_immutable_binding(name, true);
}
}
LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::Let(l)) => {
for name in bound_names(l) {
let name = name.to_js_string(interner);
drop(env.create_mutable_binding(name, false));
}
}
LexicallyScopedDeclaration::AssignmentExpression(expr) => {
for name in bound_names(expr) {
let name = name.to_js_string(interner);
drop(env.create_mutable_binding(name, false));
}
}
}
}
}
#[derive(Debug, Default)]
pub struct EvalDeclarationBindings {
pub new_annex_b_function_names: Vec<IdentifierReference>,
pub new_function_names: FxHashMap<Identifier, (IdentifierReference, bool)>,
pub new_var_names: Vec<IdentifierReference>,
}
#[allow(clippy::missing_panics_doc)]
pub(crate) fn eval_declaration_instantiation_scope(
body: &Script,
strict: bool,
var_env: &Scope,
lex_env: &Scope,
#[allow(unused_variables)] annex_b_function_names: &[Sym],
interner: &Interner,
) -> Result<EvalDeclarationBindings, String> {
let mut result = EvalDeclarationBindings::default();
let var_declarations = var_scoped_declarations(body);
if !strict {
let var_names = var_declared_names(body);
if var_env.is_global() {
for name in &var_names {
let name = name.to_js_string(interner);
if var_env.has_lex_binding(&name) {
return Err(format!(
"duplicate lexical declaration {}",
name.to_std_string_escaped()
));
}
}
}
let mut this_env = lex_env.clone();
while this_env.scope_index() != var_env.scope_index() {
for name in &var_names {
let name = interner.resolve_expect(*name).utf16().into();
if this_env.has_binding(&name) {
return Err(format!(
"variable declaration {} in eval function already exists as a lexical variable",
name.to_std_string_escaped()
));
}
}
if let Some(outer) = this_env.outer() {
this_env = outer;
} else {
break;
}
}
}
let mut functions_to_initialize = Vec::new();
let mut declared_function_names = Vec::new();
for declaration in var_declarations.iter().rev() {
let name = match &declaration {
VarScopedDeclaration::FunctionDeclaration(f) => f.name(),
VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => continue,
};
if !declared_function_names.contains(&name.sym()) {
declared_function_names.push(name.sym());
functions_to_initialize.push(declaration.clone());
}
}
functions_to_initialize.reverse();
#[cfg(feature = "annex-b")]
if !strict {
if !var_env.is_global() {
for name in annex_b_function_names {
let f = name.to_js_string(interner);
if !var_env.has_binding(&f) {
let binding = var_env.create_mutable_binding(f, true);
result
.new_annex_b_function_names
.push(IdentifierReference::new(
binding,
!var_env.is_function(),
true,
));
}
}
}
}
let mut declared_var_names = Vec::new();
for declaration in var_declarations {
let VarScopedDeclaration::VariableDeclaration(declaration) = declaration else {
continue;
};
for name in bound_names(&declaration) {
if !declared_function_names.contains(&name) {
if !declared_var_names.contains(&name) {
declared_var_names.push(name);
}
}
}
}
for statement in &**body.statements() {
if let StatementListItem::Declaration(declaration) = statement {
match declaration.as_ref() {
Declaration::ClassDeclaration(class) => {
for name in bound_names(class.as_ref()) {
let name = name.to_js_string(interner);
drop(lex_env.create_mutable_binding(name, false));
}
}
Declaration::Lexical(LexicalDeclaration::Let(declaration)) => {
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
drop(lex_env.create_mutable_binding(name, false));
}
}
Declaration::Lexical(LexicalDeclaration::Const(declaration)) => {
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
lex_env.create_immutable_binding(name, true);
}
}
_ => {}
}
}
}
for function in functions_to_initialize {
let name = match &function {
VarScopedDeclaration::FunctionDeclaration(f) => f.name(),
VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => {
continue;
}
};
if !var_env.is_global() {
let n = name.to_js_string(interner);
let binding_exists = var_env.has_binding(&n);
if binding_exists {
let binding = var_env.set_mutable_binding(n).expect("must not fail");
result.new_function_names.insert(
name,
(
IdentifierReference::new(binding.locator(), !var_env.is_function(), true),
true,
),
);
} else {
let binding = var_env.create_mutable_binding(n, !strict);
result.new_function_names.insert(
name,
(
IdentifierReference::new(binding, !var_env.is_function(), true),
false,
),
);
}
}
}
for name in declared_var_names {
if !var_env.is_global() {
let name = name.to_js_string(interner);
let binding_exists = var_env.has_binding(&name);
if !binding_exists {
let binding = var_env.create_mutable_binding(name, true);
result.new_var_names.push(IdentifierReference::new(
binding,
!var_env.is_function(),
true,
));
}
}
}
Ok(result)
}