Browse Source

Do not allocate space for local bindings in runtime environments

reduce-environment-allocations
raskad 2 months ago
parent
commit
8df3d007b7
No known key found for this signature in database
  1. 266
      core/ast/src/scope.rs
  2. 75
      core/ast/src/scope_analyzer.rs
  3. 4
      core/ast/src/source.rs
  4. 16
      core/engine/src/builtins/function/arguments.rs
  5. 1
      core/engine/src/bytecompiler/class.rs
  6. 1
      core/engine/src/bytecompiler/function.rs
  7. 8
      core/engine/src/bytecompiler/mod.rs
  8. 4
      core/engine/src/environments/runtime/mod.rs
  9. 2
      core/engine/src/module/synthetic.rs
  10. 5
      core/engine/src/vm/opcode/push/environment.rs

266
core/ast/src/scope.rs

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

75
core/ast/src/scope_analyzer.rs

@ -105,6 +105,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
try_break!(self.visit_statement_list_mut(&mut node.statements)); try_break!(self.visit_statement_list_mut(&mut node.statements));
if let Some(scope) = &mut node.scope { if let Some(scope) = &mut node.scope {
std::mem::swap(&mut self.scope, scope); std::mem::swap(&mut self.scope, scope);
scope.reorder_binding_indices();
} }
self.direct_eval = direct_eval_old; self.direct_eval = direct_eval_old;
ControlFlow::Continue(()) ControlFlow::Continue(())
@ -125,6 +126,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
} }
if let Some(scope) = &mut node.scope { if let Some(scope) = &mut node.scope {
std::mem::swap(&mut self.scope, scope); std::mem::swap(&mut self.scope, scope);
scope.reorder_binding_indices();
} }
self.direct_eval = direct_eval_old; self.direct_eval = direct_eval_old;
ControlFlow::Continue(()) ControlFlow::Continue(())
@ -140,6 +142,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
std::mem::swap(&mut self.scope, &mut node.scope); std::mem::swap(&mut self.scope, &mut node.scope);
try_break!(self.visit_statement_mut(&mut node.statement)); try_break!(self.visit_statement_mut(&mut node.statement));
std::mem::swap(&mut self.scope, &mut node.scope); std::mem::swap(&mut self.scope, &mut node.scope);
node.scope.reorder_binding_indices();
self.with = with; self.with = with;
ControlFlow::Continue(()) ControlFlow::Continue(())
} }
@ -156,6 +159,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
} }
try_break!(self.visit_block_mut(&mut node.block)); try_break!(self.visit_block_mut(&mut node.block));
std::mem::swap(&mut self.scope, &mut node.scope); std::mem::swap(&mut self.scope, &mut node.scope);
node.scope.reorder_binding_indices();
self.direct_eval = direct_eval_old; self.direct_eval = direct_eval_old;
ControlFlow::Continue(()) ControlFlow::Continue(())
} }
@ -181,6 +185,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
try_break!(self.visit_statement_mut(&mut node.inner.body)); try_break!(self.visit_statement_mut(&mut node.inner.body));
if let Some(ForLoopInitializer::Lexical(decl)) = &mut node.inner.init { if let Some(ForLoopInitializer::Lexical(decl)) = &mut node.inner.init {
std::mem::swap(&mut self.scope, &mut decl.scope); std::mem::swap(&mut self.scope, &mut decl.scope);
decl.scope.reorder_binding_indices();
} }
self.direct_eval = direct_eval_old; self.direct_eval = direct_eval_old;
ControlFlow::Continue(()) ControlFlow::Continue(())
@ -199,6 +204,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
if let Some(scope) = &mut node.target_scope { if let Some(scope) = &mut node.target_scope {
self.direct_eval = direct_eval_old; self.direct_eval = direct_eval_old;
std::mem::swap(&mut self.scope, scope); std::mem::swap(&mut self.scope, scope);
scope.reorder_binding_indices();
} }
if let Some(scope) = &mut node.scope { if let Some(scope) = &mut node.scope {
self.direct_eval = node.contains_direct_eval || self.direct_eval; self.direct_eval = node.contains_direct_eval || self.direct_eval;
@ -211,6 +217,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
try_break!(self.visit_statement_mut(&mut node.body)); try_break!(self.visit_statement_mut(&mut node.body));
if let Some(scope) = &mut node.scope { if let Some(scope) = &mut node.scope {
std::mem::swap(&mut self.scope, scope); std::mem::swap(&mut self.scope, scope);
scope.reorder_binding_indices();
} }
self.direct_eval = direct_eval_old; self.direct_eval = direct_eval_old;
ControlFlow::Continue(()) ControlFlow::Continue(())
@ -229,6 +236,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
if let Some(scope) = &mut node.iterable_scope { if let Some(scope) = &mut node.iterable_scope {
self.direct_eval = direct_eval_old; self.direct_eval = direct_eval_old;
std::mem::swap(&mut self.scope, scope); std::mem::swap(&mut self.scope, scope);
scope.reorder_binding_indices();
} }
if let Some(scope) = &mut node.scope { if let Some(scope) = &mut node.scope {
self.direct_eval = node.contains_direct_eval || self.direct_eval; self.direct_eval = node.contains_direct_eval || self.direct_eval;
@ -241,6 +249,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
try_break!(self.visit_statement_mut(&mut node.body)); try_break!(self.visit_statement_mut(&mut node.body));
if let Some(scope) = &mut node.scope { if let Some(scope) = &mut node.scope {
std::mem::swap(&mut self.scope, scope); std::mem::swap(&mut self.scope, scope);
scope.reorder_binding_indices();
} }
self.direct_eval = direct_eval_old; self.direct_eval = direct_eval_old;
ControlFlow::Continue(()) ControlFlow::Continue(())
@ -253,7 +262,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
self.visit_function_like( self.visit_function_like(
&mut node.parameters, &mut node.parameters,
&mut node.body, &mut node.body,
&node.scopes, &mut node.scopes,
node.contains_direct_eval, node.contains_direct_eval,
) )
} }
@ -265,7 +274,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
self.visit_function_like( self.visit_function_like(
&mut node.parameters, &mut node.parameters,
&mut node.body, &mut node.body,
&node.scopes, &mut node.scopes,
node.contains_direct_eval, node.contains_direct_eval,
) )
} }
@ -277,7 +286,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
self.visit_function_like( self.visit_function_like(
&mut node.parameters, &mut node.parameters,
&mut node.body, &mut node.body,
&node.scopes, &mut node.scopes,
node.contains_direct_eval, node.contains_direct_eval,
) )
} }
@ -289,7 +298,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
self.visit_function_like( self.visit_function_like(
&mut node.parameters, &mut node.parameters,
&mut node.body, &mut node.body,
&node.scopes, &mut node.scopes,
node.contains_direct_eval, node.contains_direct_eval,
) )
} }
@ -301,7 +310,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
self.visit_function_like( self.visit_function_like(
&mut node.parameters, &mut node.parameters,
&mut node.body, &mut node.body,
&node.scopes, &mut node.scopes,
node.contains_direct_eval, node.contains_direct_eval,
) )
} }
@ -313,7 +322,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
self.visit_function_like( self.visit_function_like(
&mut node.parameters, &mut node.parameters,
&mut node.body, &mut node.body,
&node.scopes, &mut node.scopes,
node.contains_direct_eval, node.contains_direct_eval,
) )
} }
@ -325,7 +334,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
self.visit_function_like( self.visit_function_like(
&mut node.parameters, &mut node.parameters,
&mut node.body, &mut node.body,
&node.scopes, &mut node.scopes,
node.contains_direct_eval, node.contains_direct_eval,
) )
} }
@ -337,7 +346,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
self.visit_function_like( self.visit_function_like(
&mut node.parameters, &mut node.parameters,
&mut node.body, &mut node.body,
&node.scopes, &mut node.scopes,
node.contains_direct_eval, node.contains_direct_eval,
) )
} }
@ -349,7 +358,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
self.visit_function_like( self.visit_function_like(
&mut node.parameters, &mut node.parameters,
&mut node.body, &mut node.body,
&node.scopes, &mut node.scopes,
node.contains_direct_eval, node.contains_direct_eval,
) )
} }
@ -361,7 +370,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
self.visit_function_like( self.visit_function_like(
&mut node.parameters, &mut node.parameters,
&mut node.body, &mut node.body,
&node.scopes, &mut node.scopes,
node.contains_direct_eval, node.contains_direct_eval,
) )
} }
@ -382,6 +391,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
try_break!(self.visit_class_element_mut(element)); try_break!(self.visit_class_element_mut(element));
} }
std::mem::swap(&mut self.scope, &mut node.name_scope); std::mem::swap(&mut self.scope, &mut node.name_scope);
node.name_scope.reorder_binding_indices();
ControlFlow::Continue(()) ControlFlow::Continue(())
} }
@ -407,6 +417,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
} }
if let Some(name_scope) = &mut node.name_scope { if let Some(name_scope) = &mut node.name_scope {
std::mem::swap(&mut self.scope, name_scope); std::mem::swap(&mut self.scope, name_scope);
name_scope.reorder_binding_indices();
} }
ControlFlow::Continue(()) ControlFlow::Continue(())
} }
@ -415,15 +426,41 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
&mut self, &mut self,
node: &'ast mut ClassElement, node: &'ast mut ClassElement,
) -> ControlFlow<Self::BreakTy> { ) -> ControlFlow<Self::BreakTy> {
if let ClassElement::MethodDefinition(node) = node { match node {
self.visit_function_like( ClassElement::MethodDefinition(node) => self.visit_function_like(
&mut node.parameters, &mut node.parameters,
&mut node.body, &mut node.body,
&node.scopes, &mut node.scopes,
node.contains_direct_eval, node.contains_direct_eval,
) ),
} else { ClassElement::FieldDefinition(field) | ClassElement::StaticFieldDefinition(field) => {
ControlFlow::Continue(()) try_break!(self.visit_property_name_mut(&mut field.name));
if let Some(e) = &mut field.field {
try_break!(self.visit_expression_mut(e));
}
ControlFlow::Continue(())
}
ClassElement::PrivateFieldDefinition(field) => {
if let Some(e) = &mut field.field {
try_break!(self.visit_expression_mut(e));
}
ControlFlow::Continue(())
}
ClassElement::PrivateStaticFieldDefinition(_, e) => {
if let Some(e) = e {
try_break!(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,
)
}
} }
} }
@ -435,7 +472,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
self.visit_function_like( self.visit_function_like(
&mut node.parameters, &mut node.parameters,
&mut node.body, &mut node.body,
&node.scopes, &mut node.scopes,
node.contains_direct_eval, node.contains_direct_eval,
) )
} }
@ -485,6 +522,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {
std::mem::swap(&mut self.scope, &mut scope); std::mem::swap(&mut self.scope, &mut scope);
try_break!(self.visit_module_item_list_mut(&mut node.items)); try_break!(self.visit_module_item_list_mut(&mut node.items));
std::mem::swap(&mut self.scope, &mut scope); std::mem::swap(&mut self.scope, &mut scope);
scope.reorder_binding_indices();
ControlFlow::Continue(()) ControlFlow::Continue(())
} }
} }
@ -494,7 +532,7 @@ impl BindingEscapeAnalyzer<'_> {
&mut self, &mut self,
parameters: &mut FormalParameterList, parameters: &mut FormalParameterList,
body: &mut FunctionBody, body: &mut FunctionBody,
scopes: &FunctionScopes, scopes: &mut FunctionScopes,
contains_direct_eval: bool, contains_direct_eval: bool,
) -> ControlFlow<&'static str> { ) -> ControlFlow<&'static str> {
let direct_eval_old = self.direct_eval; let direct_eval_old = self.direct_eval;
@ -510,6 +548,7 @@ impl BindingEscapeAnalyzer<'_> {
std::mem::swap(&mut self.scope, &mut scope); std::mem::swap(&mut self.scope, &mut scope);
try_break!(self.visit_function_body_mut(body)); try_break!(self.visit_function_body_mut(body));
std::mem::swap(&mut self.scope, &mut scope); std::mem::swap(&mut self.scope, &mut scope);
scopes.reorder_binding_indices();
self.direct_eval = direct_eval_old; self.direct_eval = direct_eval_old;
ControlFlow::Continue(()) ControlFlow::Continue(())
} }

4
core/ast/src/source.rs

@ -91,6 +91,10 @@ impl Script {
if !analyze_binding_escapes(self, true, lexical_scope.clone(), interner) { if !analyze_binding_escapes(self, true, lexical_scope.clone(), interner) {
return Err(String::from("Failed to analyze scope")); return Err(String::from("Failed to analyze scope"));
} }
variable_scope.escape_all_bindings();
lexical_scope.escape_all_bindings();
variable_scope.reorder_binding_indices();
lexical_scope.reorder_binding_indices();
optimize_scope_indicies(self, lexical_scope); optimize_scope_indicies(self, lexical_scope);
Ok(bindings) Ok(bindings)

16
core/engine/src/builtins/function/arguments.rs

@ -1,4 +1,5 @@
use crate::{ use crate::{
bytecompiler::ToJsString,
environments::DeclarativeEnvironment, environments::DeclarativeEnvironment,
object::{ object::{
internal_methods::{ internal_methods::{
@ -11,8 +12,9 @@ use crate::{
property::{DescriptorKind, PropertyDescriptor, PropertyKey}, property::{DescriptorKind, PropertyDescriptor, PropertyKey},
Context, JsData, JsResult, JsValue, Context, JsData, JsResult, JsValue,
}; };
use boa_ast::{function::FormalParameterList, operations::bound_names}; use boa_ast::{function::FormalParameterList, operations::bound_names, scope::Scope};
use boa_gc::{Finalize, Gc, Trace}; use boa_gc::{Finalize, Gc, Trace};
use boa_interner::Interner;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use thin_vec::{thin_vec, ThinVec}; use thin_vec::{thin_vec, ThinVec};
@ -141,7 +143,11 @@ impl MappedArguments {
} }
impl MappedArguments { impl MappedArguments {
pub(crate) fn binding_indices(formals: &FormalParameterList) -> ThinVec<Option<u32>> { pub(crate) fn binding_indices(
formals: &FormalParameterList,
scope: &Scope,
interner: &Interner,
) -> ThinVec<Option<u32>> {
// Section 17-19 are done first, for easier object creation in 11. // Section 17-19 are done first, for easier object creation in 11.
// //
// The section 17-19 differs from the spec, due to the way the runtime environments work. // The section 17-19 differs from the spec, due to the way the runtime environments work.
@ -180,8 +186,10 @@ impl MappedArguments {
let mut bindings = FxHashMap::default(); let mut bindings = FxHashMap::default();
let mut property_index = 0; let mut property_index = 0;
for name in bound_names(formals) { for name in bound_names(formals) {
// NOTE(HalidOdat): Offset by +1 to account for the first binding ("argument"). let binding_index = scope
let binding_index = bindings.len() as u32 + 1; .get_binding(&name.to_js_string(interner))
.expect("binding must exist")
.binding_index();
let entry = bindings let entry = bindings
.entry(name) .entry(name)

1
core/engine/src/bytecompiler/class.rs

@ -101,6 +101,7 @@ impl ByteCompiler<'_> {
compiler.length = expr.parameters().length(); compiler.length = expr.parameters().length();
compiler.params = expr.parameters().clone(); compiler.params = expr.parameters().clone();
compiler.parameter_scope = expr.scopes().parameter_scope();
compiler.function_declaration_instantiation( compiler.function_declaration_instantiation(
expr.body(), expr.body(),

1
core/engine/src/bytecompiler/function.rs

@ -195,6 +195,7 @@ impl FunctionCompiler {
compiler.compile_statement_list(body.statement_list(), false, false); compiler.compile_statement_list(body.statement_list(), false, false);
compiler.params = parameters.clone(); compiler.params = parameters.clone();
compiler.parameter_scope = scopes.parameter_scope();
let code = compiler.finish(); let code = compiler.finish();

8
core/engine/src/bytecompiler/mod.rs

@ -401,6 +401,9 @@ pub struct ByteCompiler<'ctx> {
/// Parameters passed to this function. /// Parameters passed to this function.
pub(crate) params: FormalParameterList, pub(crate) params: FormalParameterList,
/// Scope of the function parameters.
pub(crate) parameter_scope: Scope,
/// Bytecode /// Bytecode
pub(crate) bytecode: Vec<u8>, pub(crate) bytecode: Vec<u8>,
@ -513,6 +516,7 @@ impl<'ctx> ByteCompiler<'ctx> {
local_binding_registers: FxHashMap::default(), local_binding_registers: FxHashMap::default(),
this_mode: ThisMode::Global, this_mode: ThisMode::Global,
params: FormalParameterList::default(), params: FormalParameterList::default(),
parameter_scope: Scope::default(),
current_open_environments_count: 0, current_open_environments_count: 0,
register_allocator, register_allocator,
@ -1772,7 +1776,9 @@ impl<'ctx> ByteCompiler<'ctx> {
let mapped_arguments_binding_indices = self let mapped_arguments_binding_indices = self
.emitted_mapped_arguments_object_opcode .emitted_mapped_arguments_object_opcode
.then(|| MappedArguments::binding_indices(&self.params)) .then(|| {
MappedArguments::binding_indices(&self.params, &self.parameter_scope, self.interner)
})
.unwrap_or_default(); .unwrap_or_default();
let max_local_binding_register_index = let max_local_binding_register_index =

4
core/engine/src/environments/runtime/mod.rs

@ -186,7 +186,7 @@ impl EnvironmentStack {
/// Push a function environment on the environments stack. /// Push a function environment on the environments stack.
pub(crate) fn push_function(&mut self, scope: Scope, function_slots: FunctionSlots) { pub(crate) fn push_function(&mut self, scope: Scope, function_slots: FunctionSlots) {
let num_bindings = scope.num_bindings(); let num_bindings = scope.num_bindings_non_local();
let (poisoned, with) = { let (poisoned, with) = {
// Check if the outer environment is a declarative environment. // Check if the outer environment is a declarative environment.
@ -214,7 +214,7 @@ impl EnvironmentStack {
/// Push a module environment on the environments stack. /// Push a module environment on the environments stack.
pub(crate) fn push_module(&mut self, scope: Scope) { pub(crate) fn push_module(&mut self, scope: Scope) {
let num_bindings = scope.num_bindings(); let num_bindings = scope.num_bindings_non_local();
self.stack.push(Environment::Declarative(Gc::new( self.stack.push(Environment::Declarative(Gc::new(
DeclarativeEnvironment::new(DeclarativeEnvironmentKind::Module( DeclarativeEnvironment::new(DeclarativeEnvironmentKind::Module(
ModuleEnvironment::new(num_bindings, scope), ModuleEnvironment::new(num_bindings, scope),

2
core/engine/src/module/synthetic.rs

@ -307,6 +307,8 @@ impl SyntheticModule {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
module_scope.escape_all_bindings();
let cb = Gc::new(compiler.finish()); let cb = Gc::new(compiler.finish());
let mut envs = EnvironmentStack::new(global_env); let mut envs = EnvironmentStack::new(global_env);

5
core/engine/src/vm/opcode/push/environment.rs

@ -17,7 +17,10 @@ impl PushScope {
#[allow(clippy::unnecessary_wraps)] #[allow(clippy::unnecessary_wraps)]
fn operation(context: &mut Context, index: usize) -> JsResult<CompletionType> { fn operation(context: &mut Context, index: usize) -> JsResult<CompletionType> {
let scope = context.vm.frame().code_block().constant_scope(index); let scope = context.vm.frame().code_block().constant_scope(index);
context.vm.environments.push_lexical(scope.num_bindings()); context
.vm
.environments
.push_lexical(scope.num_bindings_non_local());
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }
} }

Loading…
Cancel
Save