From 8df3d007b7c24bc368f7123d206e8750d2c7e6bd Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:08:06 +0200 Subject: [PATCH] Do not allocate space for local bindings in runtime environments --- core/ast/src/scope.rs | 266 +++++++++++------- core/ast/src/scope_analyzer.rs | 75 +++-- core/ast/src/source.rs | 4 + .../engine/src/builtins/function/arguments.rs | 16 +- core/engine/src/bytecompiler/class.rs | 1 + core/engine/src/bytecompiler/function.rs | 1 + core/engine/src/bytecompiler/mod.rs | 8 +- core/engine/src/environments/runtime/mod.rs | 4 +- core/engine/src/module/synthetic.rs | 2 + core/engine/src/vm/opcode/push/environment.rs | 5 +- 10 files changed, 260 insertions(+), 122 deletions(-) diff --git a/core/ast/src/scope.rs b/core/ast/src/scope.rs index dc1fb84430..3052dbe316 100644 --- a/core/ast/src/scope.rs +++ b/core/ast/src/scope.rs @@ -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, index: RefCell, - bindings: RefCell>, + bindings: RefCell>, 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 { - 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 { - 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 { + 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 { - 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")] diff --git a/core/ast/src/scope_analyzer.rs b/core/ast/src/scope_analyzer.rs index a503046a67..b81b0cbcc2 100644 --- a/core/ast/src/scope_analyzer.rs +++ b/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)); 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(()) @@ -125,6 +126,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { } 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(()) @@ -140,6 +142,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { std::mem::swap(&mut self.scope, &mut node.scope); try_break!(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(()) } @@ -156,6 +159,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { } try_break!(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(()) } @@ -181,6 +185,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { try_break!(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(()) @@ -199,6 +204,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { 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; @@ -211,6 +217,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { try_break!(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(()) @@ -229,6 +236,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { 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; @@ -241,6 +249,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { try_break!(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(()) @@ -253,7 +262,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { self.visit_function_like( &mut node.parameters, &mut node.body, - &node.scopes, + &mut node.scopes, node.contains_direct_eval, ) } @@ -265,7 +274,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { self.visit_function_like( &mut node.parameters, &mut node.body, - &node.scopes, + &mut node.scopes, node.contains_direct_eval, ) } @@ -277,7 +286,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { self.visit_function_like( &mut node.parameters, &mut node.body, - &node.scopes, + &mut node.scopes, node.contains_direct_eval, ) } @@ -289,7 +298,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { self.visit_function_like( &mut node.parameters, &mut node.body, - &node.scopes, + &mut node.scopes, node.contains_direct_eval, ) } @@ -301,7 +310,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { self.visit_function_like( &mut node.parameters, &mut node.body, - &node.scopes, + &mut node.scopes, node.contains_direct_eval, ) } @@ -313,7 +322,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { self.visit_function_like( &mut node.parameters, &mut node.body, - &node.scopes, + &mut node.scopes, node.contains_direct_eval, ) } @@ -325,7 +334,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { self.visit_function_like( &mut node.parameters, &mut node.body, - &node.scopes, + &mut node.scopes, node.contains_direct_eval, ) } @@ -337,7 +346,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { self.visit_function_like( &mut node.parameters, &mut node.body, - &node.scopes, + &mut node.scopes, node.contains_direct_eval, ) } @@ -349,7 +358,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { self.visit_function_like( &mut node.parameters, &mut node.body, - &node.scopes, + &mut node.scopes, node.contains_direct_eval, ) } @@ -361,7 +370,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { self.visit_function_like( &mut node.parameters, &mut node.body, - &node.scopes, + &mut node.scopes, node.contains_direct_eval, ) } @@ -382,6 +391,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { try_break!(self.visit_class_element_mut(element)); } std::mem::swap(&mut self.scope, &mut node.name_scope); + node.name_scope.reorder_binding_indices(); ControlFlow::Continue(()) } @@ -407,6 +417,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { } if let Some(name_scope) = &mut node.name_scope { std::mem::swap(&mut self.scope, name_scope); + name_scope.reorder_binding_indices(); } ControlFlow::Continue(()) } @@ -415,15 +426,41 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { &mut self, node: &'ast mut ClassElement, ) -> ControlFlow { - if let ClassElement::MethodDefinition(node) = node { - self.visit_function_like( + match node { + ClassElement::MethodDefinition(node) => self.visit_function_like( &mut node.parameters, &mut node.body, - &node.scopes, + &mut node.scopes, node.contains_direct_eval, - ) - } else { - ControlFlow::Continue(()) + ), + ClassElement::FieldDefinition(field) | ClassElement::StaticFieldDefinition(field) => { + 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( &mut node.parameters, &mut node.body, - &node.scopes, + &mut node.scopes, node.contains_direct_eval, ) } @@ -485,6 +522,7 @@ impl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> { std::mem::swap(&mut self.scope, &mut scope); try_break!(self.visit_module_item_list_mut(&mut node.items)); std::mem::swap(&mut self.scope, &mut scope); + scope.reorder_binding_indices(); ControlFlow::Continue(()) } } @@ -494,7 +532,7 @@ impl BindingEscapeAnalyzer<'_> { &mut self, parameters: &mut FormalParameterList, body: &mut FunctionBody, - scopes: &FunctionScopes, + scopes: &mut FunctionScopes, contains_direct_eval: bool, ) -> ControlFlow<&'static str> { let direct_eval_old = self.direct_eval; @@ -510,6 +548,7 @@ impl BindingEscapeAnalyzer<'_> { std::mem::swap(&mut self.scope, &mut scope); try_break!(self.visit_function_body_mut(body)); std::mem::swap(&mut self.scope, &mut scope); + scopes.reorder_binding_indices(); self.direct_eval = direct_eval_old; ControlFlow::Continue(()) } diff --git a/core/ast/src/source.rs b/core/ast/src/source.rs index 0584653c90..e7a4ad1633 100644 --- a/core/ast/src/source.rs +++ b/core/ast/src/source.rs @@ -91,6 +91,10 @@ impl Script { if !analyze_binding_escapes(self, true, lexical_scope.clone(), interner) { 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); Ok(bindings) diff --git a/core/engine/src/builtins/function/arguments.rs b/core/engine/src/builtins/function/arguments.rs index 83fd821287..01f28a23ff 100644 --- a/core/engine/src/builtins/function/arguments.rs +++ b/core/engine/src/builtins/function/arguments.rs @@ -1,4 +1,5 @@ use crate::{ + bytecompiler::ToJsString, environments::DeclarativeEnvironment, object::{ internal_methods::{ @@ -11,8 +12,9 @@ use crate::{ property::{DescriptorKind, PropertyDescriptor, PropertyKey}, 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_interner::Interner; use rustc_hash::FxHashMap; use thin_vec::{thin_vec, ThinVec}; @@ -141,7 +143,11 @@ impl MappedArguments { } impl MappedArguments { - pub(crate) fn binding_indices(formals: &FormalParameterList) -> ThinVec> { + pub(crate) fn binding_indices( + formals: &FormalParameterList, + scope: &Scope, + interner: &Interner, + ) -> ThinVec> { // 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. @@ -180,8 +186,10 @@ impl MappedArguments { let mut bindings = FxHashMap::default(); let mut property_index = 0; for name in bound_names(formals) { - // NOTE(HalidOdat): Offset by +1 to account for the first binding ("argument"). - let binding_index = bindings.len() as u32 + 1; + let binding_index = scope + .get_binding(&name.to_js_string(interner)) + .expect("binding must exist") + .binding_index(); let entry = bindings .entry(name) diff --git a/core/engine/src/bytecompiler/class.rs b/core/engine/src/bytecompiler/class.rs index ab489974d4..1e360be492 100644 --- a/core/engine/src/bytecompiler/class.rs +++ b/core/engine/src/bytecompiler/class.rs @@ -101,6 +101,7 @@ impl ByteCompiler<'_> { compiler.length = expr.parameters().length(); compiler.params = expr.parameters().clone(); + compiler.parameter_scope = expr.scopes().parameter_scope(); compiler.function_declaration_instantiation( expr.body(), diff --git a/core/engine/src/bytecompiler/function.rs b/core/engine/src/bytecompiler/function.rs index d66d68c7b9..5909783b02 100644 --- a/core/engine/src/bytecompiler/function.rs +++ b/core/engine/src/bytecompiler/function.rs @@ -195,6 +195,7 @@ impl FunctionCompiler { compiler.compile_statement_list(body.statement_list(), false, false); compiler.params = parameters.clone(); + compiler.parameter_scope = scopes.parameter_scope(); let code = compiler.finish(); diff --git a/core/engine/src/bytecompiler/mod.rs b/core/engine/src/bytecompiler/mod.rs index a74310402b..657261f3e8 100644 --- a/core/engine/src/bytecompiler/mod.rs +++ b/core/engine/src/bytecompiler/mod.rs @@ -401,6 +401,9 @@ pub struct ByteCompiler<'ctx> { /// Parameters passed to this function. pub(crate) params: FormalParameterList, + /// Scope of the function parameters. + pub(crate) parameter_scope: Scope, + /// Bytecode pub(crate) bytecode: Vec, @@ -513,6 +516,7 @@ impl<'ctx> ByteCompiler<'ctx> { local_binding_registers: FxHashMap::default(), this_mode: ThisMode::Global, params: FormalParameterList::default(), + parameter_scope: Scope::default(), current_open_environments_count: 0, register_allocator, @@ -1772,7 +1776,9 @@ impl<'ctx> ByteCompiler<'ctx> { let mapped_arguments_binding_indices = self .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(); let max_local_binding_register_index = diff --git a/core/engine/src/environments/runtime/mod.rs b/core/engine/src/environments/runtime/mod.rs index ba2a03dda5..4dce94a9f8 100644 --- a/core/engine/src/environments/runtime/mod.rs +++ b/core/engine/src/environments/runtime/mod.rs @@ -186,7 +186,7 @@ impl EnvironmentStack { /// Push a function environment on the environments stack. 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) = { // Check if the outer environment is a declarative environment. @@ -214,7 +214,7 @@ impl EnvironmentStack { /// Push a module environment on the environments stack. 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( DeclarativeEnvironment::new(DeclarativeEnvironmentKind::Module( ModuleEnvironment::new(num_bindings, scope), diff --git a/core/engine/src/module/synthetic.rs b/core/engine/src/module/synthetic.rs index 5e8309e0b6..99e8f27826 100644 --- a/core/engine/src/module/synthetic.rs +++ b/core/engine/src/module/synthetic.rs @@ -307,6 +307,8 @@ impl SyntheticModule { }) .collect::>(); + module_scope.escape_all_bindings(); + let cb = Gc::new(compiler.finish()); let mut envs = EnvironmentStack::new(global_env); diff --git a/core/engine/src/vm/opcode/push/environment.rs b/core/engine/src/vm/opcode/push/environment.rs index cfd66eca84..81fb983569 100644 --- a/core/engine/src/vm/opcode/push/environment.rs +++ b/core/engine/src/vm/opcode/push/environment.rs @@ -17,7 +17,10 @@ impl PushScope { #[allow(clippy::unnecessary_wraps)] fn operation(context: &mut Context, index: usize) -> JsResult { 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) } }