|
|
|
@ -3,12 +3,12 @@
|
|
|
|
|
//! Scopes are used to track the bindings of identifiers in the AST.
|
|
|
|
|
|
|
|
|
|
use boa_string::JsString; |
|
|
|
|
use rustc_hash::FxHashMap; |
|
|
|
|
use std::{cell::RefCell, fmt::Debug, rc::Rc}; |
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)] |
|
|
|
|
#[allow(clippy::struct_excessive_bools)] |
|
|
|
|
struct Binding { |
|
|
|
|
name: JsString, |
|
|
|
|
index: u32, |
|
|
|
|
mutable: bool, |
|
|
|
|
lex: bool, |
|
|
|
@ -53,7 +53,7 @@ pub(crate) struct Inner {
|
|
|
|
|
unique_id: u32, |
|
|
|
|
outer: Option<Scope>, |
|
|
|
|
index: RefCell<u32>, |
|
|
|
|
bindings: RefCell<FxHashMap<JsString, Binding>>, |
|
|
|
|
bindings: RefCell<Vec<Binding>>, |
|
|
|
|
function: bool, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -94,13 +94,13 @@ impl Scope {
|
|
|
|
|
self.inner |
|
|
|
|
.bindings |
|
|
|
|
.borrow() |
|
|
|
|
.values() |
|
|
|
|
.iter() |
|
|
|
|
.all(|binding| !binding.escapes) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Marks all bindings in this scope as escaping.
|
|
|
|
|
pub fn escape_all_bindings(&self) { |
|
|
|
|
for binding in self.inner.bindings.borrow_mut().values_mut() { |
|
|
|
|
for binding in self.inner.bindings.borrow_mut().iter_mut() { |
|
|
|
|
binding.escapes = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -111,21 +111,22 @@ impl Scope {
|
|
|
|
|
self.inner |
|
|
|
|
.bindings |
|
|
|
|
.borrow() |
|
|
|
|
.get(name) |
|
|
|
|
.iter() |
|
|
|
|
.find(|b| &b.name == name) |
|
|
|
|
.map_or(false, |binding| binding.lex) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Check if the scope has a binding with the given name.
|
|
|
|
|
#[must_use] |
|
|
|
|
pub fn has_binding(&self, name: &JsString) -> bool { |
|
|
|
|
self.inner.bindings.borrow().contains_key(name) |
|
|
|
|
self.inner.bindings.borrow().iter().any(|b| &b.name == name) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Get the binding locator for a binding with the given name.
|
|
|
|
|
/// Fall back to the global scope if the binding is not found.
|
|
|
|
|
#[must_use] |
|
|
|
|
pub fn get_identifier_reference(&self, name: JsString) -> IdentifierReference { |
|
|
|
|
if let Some(binding) = self.inner.bindings.borrow().get(&name) { |
|
|
|
|
if let Some(binding) = self.inner.bindings.borrow().iter().find(|b| b.name == name) { |
|
|
|
|
IdentifierReference::new( |
|
|
|
|
BindingLocator::declarative( |
|
|
|
|
name, |
|
|
|
@ -150,6 +151,32 @@ impl Scope {
|
|
|
|
|
self.inner.bindings.borrow().len() as u32 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns the number of bindings in this scope that are not local.
|
|
|
|
|
#[must_use] |
|
|
|
|
#[allow(clippy::cast_possible_truncation)] |
|
|
|
|
pub fn num_bindings_non_local(&self) -> u32 { |
|
|
|
|
self.inner |
|
|
|
|
.bindings |
|
|
|
|
.borrow() |
|
|
|
|
.iter() |
|
|
|
|
.filter(|binding| binding.escapes) |
|
|
|
|
.count() as u32 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Adjust the binding indices to exclude local bindings.
|
|
|
|
|
pub(crate) fn reorder_binding_indices(&self) { |
|
|
|
|
let mut bindings = self.inner.bindings.borrow_mut(); |
|
|
|
|
let mut index = 0; |
|
|
|
|
for binding in bindings.iter_mut() { |
|
|
|
|
if !binding.escapes { |
|
|
|
|
binding.index = 0; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
binding.index = index; |
|
|
|
|
index += 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns the index of this scope.
|
|
|
|
|
#[must_use] |
|
|
|
|
pub fn scope_index(&self) -> u32 { |
|
|
|
@ -176,31 +203,41 @@ impl Scope {
|
|
|
|
|
/// Get the locator for a binding name.
|
|
|
|
|
#[must_use] |
|
|
|
|
pub fn get_binding(&self, name: &JsString) -> Option<BindingLocator> { |
|
|
|
|
self.inner.bindings.borrow().get(name).map(|binding| { |
|
|
|
|
BindingLocator::declarative( |
|
|
|
|
name.clone(), |
|
|
|
|
*self.inner.index.borrow(), |
|
|
|
|
binding.index, |
|
|
|
|
self.inner.unique_id, |
|
|
|
|
) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Get the locator for a binding name.
|
|
|
|
|
#[must_use] |
|
|
|
|
pub fn get_binding_reference(&self, name: &JsString) -> Option<IdentifierReference> { |
|
|
|
|
self.inner.bindings.borrow().get(name).map(|binding| { |
|
|
|
|
IdentifierReference::new( |
|
|
|
|
self.inner |
|
|
|
|
.bindings |
|
|
|
|
.borrow() |
|
|
|
|
.iter() |
|
|
|
|
.find(|b| &b.name == name) |
|
|
|
|
.map(|binding| { |
|
|
|
|
BindingLocator::declarative( |
|
|
|
|
name.clone(), |
|
|
|
|
*self.inner.index.borrow(), |
|
|
|
|
binding.index, |
|
|
|
|
self.inner.unique_id, |
|
|
|
|
), |
|
|
|
|
binding.lex, |
|
|
|
|
binding.escapes, |
|
|
|
|
) |
|
|
|
|
}) |
|
|
|
|
) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Get the locator for a binding name.
|
|
|
|
|
#[must_use] |
|
|
|
|
pub fn get_binding_reference(&self, name: &JsString) -> Option<IdentifierReference> { |
|
|
|
|
self.inner |
|
|
|
|
.bindings |
|
|
|
|
.borrow() |
|
|
|
|
.iter() |
|
|
|
|
.find(|b| &b.name == name) |
|
|
|
|
.map(|binding| { |
|
|
|
|
IdentifierReference::new( |
|
|
|
|
BindingLocator::declarative( |
|
|
|
|
name.clone(), |
|
|
|
|
*self.inner.index.borrow(), |
|
|
|
|
binding.index, |
|
|
|
|
self.inner.unique_id, |
|
|
|
|
), |
|
|
|
|
binding.lex, |
|
|
|
|
binding.escapes, |
|
|
|
|
) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Simulate a binding access.
|
|
|
|
@ -211,7 +248,13 @@ impl Scope {
|
|
|
|
|
let mut crossed_function_border = false; |
|
|
|
|
let mut current = self; |
|
|
|
|
loop { |
|
|
|
|
if let Some(binding) = current.inner.bindings.borrow_mut().get_mut(name) { |
|
|
|
|
if let Some(binding) = current |
|
|
|
|
.inner |
|
|
|
|
.bindings |
|
|
|
|
.borrow_mut() |
|
|
|
|
.iter_mut() |
|
|
|
|
.find(|b| &b.name == name) |
|
|
|
|
{ |
|
|
|
|
if crossed_function_border || eval_or_with { |
|
|
|
|
binding.escapes = true; |
|
|
|
|
} |
|
|
|
@ -232,17 +275,24 @@ impl Scope {
|
|
|
|
|
#[must_use] |
|
|
|
|
#[allow(clippy::cast_possible_truncation)] |
|
|
|
|
pub fn create_mutable_binding(&self, name: JsString, function_scope: bool) -> BindingLocator { |
|
|
|
|
let binding_index = self.inner.bindings.borrow().len() as u32; |
|
|
|
|
self.inner.bindings.borrow_mut().insert( |
|
|
|
|
name.clone(), |
|
|
|
|
Binding { |
|
|
|
|
index: binding_index, |
|
|
|
|
mutable: true, |
|
|
|
|
lex: !function_scope, |
|
|
|
|
strict: false, |
|
|
|
|
escapes: self.is_global(), |
|
|
|
|
}, |
|
|
|
|
); |
|
|
|
|
let mut bindings = self.inner.bindings.borrow_mut(); |
|
|
|
|
let binding_index = bindings.len() as u32; |
|
|
|
|
if let Some(binding) = bindings.iter().find(|b| b.name == name) { |
|
|
|
|
return BindingLocator::declarative( |
|
|
|
|
name, |
|
|
|
|
*self.inner.index.borrow(), |
|
|
|
|
binding.index, |
|
|
|
|
self.inner.unique_id, |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
bindings.push(Binding { |
|
|
|
|
name: name.clone(), |
|
|
|
|
index: binding_index, |
|
|
|
|
mutable: true, |
|
|
|
|
lex: !function_scope, |
|
|
|
|
strict: false, |
|
|
|
|
escapes: self.is_global(), |
|
|
|
|
}); |
|
|
|
|
BindingLocator::declarative( |
|
|
|
|
name, |
|
|
|
|
*self.inner.index.borrow(), |
|
|
|
@ -254,17 +304,19 @@ impl Scope {
|
|
|
|
|
/// Crate an immutable binding.
|
|
|
|
|
#[allow(clippy::cast_possible_truncation)] |
|
|
|
|
pub(crate) fn create_immutable_binding(&self, name: JsString, strict: bool) { |
|
|
|
|
let binding_index = self.inner.bindings.borrow().len() as u32; |
|
|
|
|
self.inner.bindings.borrow_mut().insert( |
|
|
|
|
let mut bindings = self.inner.bindings.borrow_mut(); |
|
|
|
|
if bindings.iter().any(|b| b.name == name) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
let binding_index = bindings.len() as u32; |
|
|
|
|
bindings.push(Binding { |
|
|
|
|
name, |
|
|
|
|
Binding { |
|
|
|
|
index: binding_index, |
|
|
|
|
mutable: false, |
|
|
|
|
lex: true, |
|
|
|
|
strict, |
|
|
|
|
escapes: self.is_global(), |
|
|
|
|
}, |
|
|
|
|
); |
|
|
|
|
index: binding_index, |
|
|
|
|
mutable: false, |
|
|
|
|
lex: true, |
|
|
|
|
strict, |
|
|
|
|
escapes: self.is_global(), |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Return the binding locator for a mutable binding.
|
|
|
|
@ -275,30 +327,34 @@ impl Scope {
|
|
|
|
|
&self, |
|
|
|
|
name: JsString, |
|
|
|
|
) -> Result<IdentifierReference, BindingLocatorError> { |
|
|
|
|
Ok(match self.inner.bindings.borrow().get(&name) { |
|
|
|
|
Some(binding) if binding.mutable => IdentifierReference::new( |
|
|
|
|
BindingLocator::declarative( |
|
|
|
|
name, |
|
|
|
|
*self.inner.index.borrow(), |
|
|
|
|
binding.index, |
|
|
|
|
self.inner.unique_id, |
|
|
|
|
Ok( |
|
|
|
|
match self.inner.bindings.borrow().iter().find(|b| b.name == name) { |
|
|
|
|
Some(binding) if binding.mutable => IdentifierReference::new( |
|
|
|
|
BindingLocator::declarative( |
|
|
|
|
name, |
|
|
|
|
*self.inner.index.borrow(), |
|
|
|
|
binding.index, |
|
|
|
|
self.inner.unique_id, |
|
|
|
|
), |
|
|
|
|
binding.lex, |
|
|
|
|
binding.escapes, |
|
|
|
|
), |
|
|
|
|
binding.lex, |
|
|
|
|
binding.escapes, |
|
|
|
|
), |
|
|
|
|
Some(binding) if binding.strict => return Err(BindingLocatorError::MutateImmutable), |
|
|
|
|
Some(_) => return Err(BindingLocatorError::Silent), |
|
|
|
|
None => self.inner.outer.as_ref().map_or_else( |
|
|
|
|
|| { |
|
|
|
|
Ok(IdentifierReference::new( |
|
|
|
|
BindingLocator::global(name.clone()), |
|
|
|
|
false, |
|
|
|
|
true, |
|
|
|
|
)) |
|
|
|
|
}, |
|
|
|
|
|outer| outer.set_mutable_binding(name.clone()), |
|
|
|
|
)?, |
|
|
|
|
}) |
|
|
|
|
Some(binding) if binding.strict => { |
|
|
|
|
return Err(BindingLocatorError::MutateImmutable) |
|
|
|
|
} |
|
|
|
|
Some(_) => return Err(BindingLocatorError::Silent), |
|
|
|
|
None => self.inner.outer.as_ref().map_or_else( |
|
|
|
|
|| { |
|
|
|
|
Ok(IdentifierReference::new( |
|
|
|
|
BindingLocator::global(name.clone()), |
|
|
|
|
false, |
|
|
|
|
true, |
|
|
|
|
)) |
|
|
|
|
}, |
|
|
|
|
|outer| outer.set_mutable_binding(name.clone()), |
|
|
|
|
)?, |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[cfg(feature = "annex-b")] |
|
|
|
@ -323,30 +379,34 @@ impl Scope {
|
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(match self.inner.bindings.borrow().get(&name) { |
|
|
|
|
Some(binding) if binding.mutable => IdentifierReference::new( |
|
|
|
|
BindingLocator::declarative( |
|
|
|
|
name, |
|
|
|
|
*self.inner.index.borrow(), |
|
|
|
|
binding.index, |
|
|
|
|
self.inner.unique_id, |
|
|
|
|
Ok( |
|
|
|
|
match self.inner.bindings.borrow().iter().find(|b| b.name == name) { |
|
|
|
|
Some(binding) if binding.mutable => IdentifierReference::new( |
|
|
|
|
BindingLocator::declarative( |
|
|
|
|
name, |
|
|
|
|
*self.inner.index.borrow(), |
|
|
|
|
binding.index, |
|
|
|
|
self.inner.unique_id, |
|
|
|
|
), |
|
|
|
|
binding.lex, |
|
|
|
|
binding.escapes, |
|
|
|
|
), |
|
|
|
|
binding.lex, |
|
|
|
|
binding.escapes, |
|
|
|
|
), |
|
|
|
|
Some(binding) if binding.strict => return Err(BindingLocatorError::MutateImmutable), |
|
|
|
|
Some(_) => return Err(BindingLocatorError::Silent), |
|
|
|
|
None => self.inner.outer.as_ref().map_or_else( |
|
|
|
|
|| { |
|
|
|
|
Ok(IdentifierReference::new( |
|
|
|
|
BindingLocator::global(name.clone()), |
|
|
|
|
false, |
|
|
|
|
true, |
|
|
|
|
)) |
|
|
|
|
}, |
|
|
|
|
|outer| outer.set_mutable_binding_var(name.clone()), |
|
|
|
|
)?, |
|
|
|
|
}) |
|
|
|
|
Some(binding) if binding.strict => { |
|
|
|
|
return Err(BindingLocatorError::MutateImmutable) |
|
|
|
|
} |
|
|
|
|
Some(_) => return Err(BindingLocatorError::Silent), |
|
|
|
|
None => self.inner.outer.as_ref().map_or_else( |
|
|
|
|
|| { |
|
|
|
|
Ok(IdentifierReference::new( |
|
|
|
|
BindingLocator::global(name.clone()), |
|
|
|
|
false, |
|
|
|
|
true, |
|
|
|
|
)) |
|
|
|
|
}, |
|
|
|
|
|outer| outer.set_mutable_binding_var(name.clone()), |
|
|
|
|
)?, |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Gets the outer scope of this scope.
|
|
|
|
@ -538,7 +598,8 @@ impl FunctionScopes {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns the effective paramter scope for this function.
|
|
|
|
|
pub(crate) fn parameter_scope(&self) -> Scope { |
|
|
|
|
#[must_use] |
|
|
|
|
pub fn parameter_scope(&self) -> Scope { |
|
|
|
|
if let Some(parameters_eval_scope) = &self.parameters_eval_scope { |
|
|
|
|
return parameters_eval_scope.clone(); |
|
|
|
|
} |
|
|
|
@ -572,6 +633,19 @@ impl FunctionScopes {
|
|
|
|
|
lexical_scope.escape_all_bindings(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub(crate) fn reorder_binding_indices(&self) { |
|
|
|
|
self.function_scope.reorder_binding_indices(); |
|
|
|
|
if let Some(parameters_eval_scope) = &self.parameters_eval_scope { |
|
|
|
|
parameters_eval_scope.reorder_binding_indices(); |
|
|
|
|
} |
|
|
|
|
if let Some(parameters_scope) = &self.parameters_scope { |
|
|
|
|
parameters_scope.reorder_binding_indices(); |
|
|
|
|
} |
|
|
|
|
if let Some(lexical_scope) = &self.lexical_scope { |
|
|
|
|
lexical_scope.reorder_binding_indices(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[cfg(feature = "arbitrary")] |
|
|
|
|