Browse Source

Skip environment creation when all bindings in the scope are local

reduce-environment-allocations
raskad 2 months ago
parent
commit
4c16b20899
No known key found for this signature in database
  1. 82
      core/ast/src/scope.rs
  2. 417
      core/ast/src/scope_analyzer.rs
  3. 16
      core/ast/src/source.rs
  4. 22
      core/engine/src/bytecompiler/class.rs
  5. 13
      core/engine/src/bytecompiler/declarations.rs
  6. 32
      core/engine/src/bytecompiler/env.rs
  7. 6
      core/engine/src/bytecompiler/function.rs
  8. 19
      core/engine/src/bytecompiler/statement/block.rs
  9. 96
      core/engine/src/bytecompiler/statement/loop.rs
  10. 17
      core/engine/src/bytecompiler/statement/switch.rs
  11. 10
      core/engine/src/bytecompiler/statement/try.rs

82
core/ast/src/scope.rs

@ -50,8 +50,9 @@ impl<'a> arbitrary::Arbitrary<'a> for Scope {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub(crate) struct Inner { pub(crate) struct Inner {
unique_id: u32,
outer: Option<Scope>, outer: Option<Scope>,
index: u32, index: RefCell<u32>,
bindings: RefCell<FxHashMap<JsString, Binding>>, bindings: RefCell<FxHashMap<JsString, Binding>>,
function: bool, function: bool,
} }
@ -62,8 +63,9 @@ impl Scope {
pub fn new_global() -> Self { pub fn new_global() -> Self {
Self { Self {
inner: Rc::new(Inner { inner: Rc::new(Inner {
unique_id: 0,
outer: None, outer: None,
index: 0, index: RefCell::default(),
bindings: RefCell::default(), bindings: RefCell::default(),
function: true, function: true,
}), }),
@ -73,17 +75,29 @@ impl Scope {
/// Creates a new scope. /// Creates a new scope.
#[must_use] #[must_use]
pub fn new(parent: Self, function: bool) -> Self { pub fn new(parent: Self, function: bool) -> Self {
let index = parent.inner.index + 1; let index = *parent.inner.index.borrow() + 1;
Self { Self {
inner: Rc::new(Inner { inner: Rc::new(Inner {
unique_id: index,
outer: Some(parent), outer: Some(parent),
index, index: RefCell::new(index),
bindings: RefCell::default(), bindings: RefCell::default(),
function, function,
}), }),
} }
} }
/// Checks if the scope has only local bindings.
#[must_use]
pub fn all_bindings_local(&self) -> bool {
// if self.inner.function && self.inn
self.inner
.bindings
.borrow()
.values()
.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().values_mut() {
@ -113,7 +127,12 @@ impl Scope {
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().get(&name) {
IdentifierReference::new( IdentifierReference::new(
BindingLocator::declarative(name, self.inner.index, binding.index), BindingLocator::declarative(
name,
*self.inner.index.borrow(),
binding.index,
self.inner.unique_id,
),
binding.lex, binding.lex,
binding.escapes, binding.escapes,
) )
@ -134,7 +153,12 @@ impl Scope {
/// 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 {
self.inner.index *self.inner.index.borrow()
}
/// Set the index of this scope.
pub(crate) fn set_index(&self, index: u32) {
*self.inner.index.borrow_mut() = index;
} }
/// Check if the scope is a function scope. /// Check if the scope is a function scope.
@ -153,7 +177,12 @@ impl Scope {
#[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.bindings.borrow().get(name).map(|binding| {
BindingLocator::declarative(name.clone(), self.inner.index, binding.index) BindingLocator::declarative(
name.clone(),
*self.inner.index.borrow(),
binding.index,
self.inner.unique_id,
)
}) })
} }
@ -162,7 +191,12 @@ impl Scope {
pub fn get_binding_reference(&self, name: &JsString) -> Option<IdentifierReference> { pub fn get_binding_reference(&self, name: &JsString) -> Option<IdentifierReference> {
self.inner.bindings.borrow().get(name).map(|binding| { self.inner.bindings.borrow().get(name).map(|binding| {
IdentifierReference::new( IdentifierReference::new(
BindingLocator::declarative(name.clone(), self.inner.index, binding.index), BindingLocator::declarative(
name.clone(),
*self.inner.index.borrow(),
binding.index,
self.inner.unique_id,
),
binding.lex, binding.lex,
binding.escapes, binding.escapes,
) )
@ -209,7 +243,12 @@ impl Scope {
escapes: self.is_global(), escapes: self.is_global(),
}, },
); );
BindingLocator::declarative(name, self.inner.index, binding_index) BindingLocator::declarative(
name,
*self.inner.index.borrow(),
binding_index,
self.inner.unique_id,
)
} }
/// Crate an immutable binding. /// Crate an immutable binding.
@ -238,7 +277,12 @@ impl Scope {
) -> Result<IdentifierReference, BindingLocatorError> { ) -> Result<IdentifierReference, BindingLocatorError> {
Ok(match self.inner.bindings.borrow().get(&name) { Ok(match self.inner.bindings.borrow().get(&name) {
Some(binding) if binding.mutable => IdentifierReference::new( Some(binding) if binding.mutable => IdentifierReference::new(
BindingLocator::declarative(name, self.inner.index, binding.index), BindingLocator::declarative(
name,
*self.inner.index.borrow(),
binding.index,
self.inner.unique_id,
),
binding.lex, binding.lex,
binding.escapes, binding.escapes,
), ),
@ -281,7 +325,12 @@ impl Scope {
Ok(match self.inner.bindings.borrow().get(&name) { Ok(match self.inner.bindings.borrow().get(&name) {
Some(binding) if binding.mutable => IdentifierReference::new( Some(binding) if binding.mutable => IdentifierReference::new(
BindingLocator::declarative(name, self.inner.index, binding.index), BindingLocator::declarative(
name,
*self.inner.index.borrow(),
binding.index,
self.inner.unique_id,
),
binding.lex, binding.lex,
binding.escapes, binding.escapes,
), ),
@ -358,15 +407,23 @@ pub struct BindingLocator {
/// Index of the binding in the scope. /// Index of the binding in the scope.
binding_index: u32, binding_index: u32,
unique_scope_id: u32,
} }
impl BindingLocator { impl BindingLocator {
/// Creates a new declarative binding locator that has knows indices. /// Creates a new declarative binding locator that has knows indices.
pub(crate) const fn declarative(name: JsString, scope_index: u32, binding_index: u32) -> Self { pub(crate) const fn declarative(
name: JsString,
scope_index: u32,
binding_index: u32,
unique_scope_id: u32,
) -> Self {
Self { Self {
name, name,
scope: scope_index + 1, scope: scope_index + 1,
binding_index, binding_index,
unique_scope_id,
} }
} }
@ -376,6 +433,7 @@ impl BindingLocator {
name, name,
scope: 0, scope: 0,
binding_index: 0, binding_index: 0,
unique_scope_id: 0,
} }
} }

417
core/ast/src/scope_analyzer.rs

@ -1149,6 +1149,423 @@ impl BindingCollectorVisitor<'_> {
} }
} }
/// Optimize scope indicies when scopes only contain local bindings.
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(),
};
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> {
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
)
}
fn visit_generator_declaration_mut(
&mut self,
node: &'ast mut GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
)
}
fn visit_async_function_declaration_mut(
&mut self,
node: &'ast mut AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
)
}
fn visit_async_generator_declaration_mut(
&mut self,
node: &'ast mut AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
)
}
fn visit_function_expression_mut(
&mut self,
node: &'ast mut FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut node.name_scope,
)
}
fn visit_generator_expression_mut(
&mut self,
node: &'ast mut GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut node.name_scope,
)
}
fn visit_async_function_expression_mut(
&mut self,
node: &'ast mut AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut node.name_scope,
)
}
fn visit_async_generator_expression_mut(
&mut self,
node: &'ast mut AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut node.name_scope,
)
}
fn visit_arrow_function_mut(
&mut self,
node: &'ast mut ArrowFunction,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
)
}
fn visit_async_arrow_function_mut(
&mut self,
node: &'ast mut AsyncArrowFunction,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
)
}
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 {
try_break!(self.visit_expression_mut(super_ref));
}
if let Some(constructor) = &mut node.constructor {
try_break!(self.visit_function_expression_mut(constructor));
}
for element in &mut *node.elements {
try_break!(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 {
try_break!(self.visit_expression_mut(super_ref));
}
if let Some(constructor) = &mut node.constructor {
try_break!(self.visit_function_expression_mut(constructor));
}
for element in &mut *node.elements {
try_break!(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) => self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
),
ClassElement::FieldDefinition(field) | ClassElement::StaticFieldDefinition(field) => {
try_break!(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.field {
try_break!(self.visit_expression_mut(e));
}
self.index = index;
ControlFlow::Continue(())
}
ClassElement::PrivateFieldDefinition(field) => {
let index = self.index;
self.index += 1;
field.scope.set_index(self.index);
if let Some(e) = &mut field.field {
try_break!(self.visit_expression_mut(e));
}
self.index = index;
ControlFlow::Continue(())
}
ClassElement::PrivateStaticFieldDefinition(_, e) => {
if let Some(e) = e {
try_break!(self.visit_expression_mut(e));
}
ControlFlow::Continue(())
}
ClassElement::StaticBlock(node) => self.visit_function_like(
&mut node.body,
&mut FormalParameterList::default(),
&mut node.scopes,
&mut None,
),
}
}
fn visit_object_method_definition_mut(
&mut self,
node: &'ast mut ObjectMethodDefinition,
) -> ControlFlow<Self::BreakTy> {
match &mut node.name {
PropertyName::Literal(_) => {}
PropertyName::Computed(name) => {
try_break!(self.visit_expression_mut(name));
}
}
self.visit_function_like(
&mut node.body,
&mut node.parameters,
&mut node.scopes,
&mut None,
)
}
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);
}
try_break!(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;
try_break!(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 {
try_break!(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;
try_break!(self.visit_expression_mut(&mut node.expression));
self.index += 1;
node.scope.set_index(self.index);
try_break!(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 {
try_break!(self.visit_binding_mut(binding));
}
try_break!(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 {
try_break!(self.visit_for_loop_initializer_mut(fli));
}
if let Some(expr) = &mut node.inner.condition {
try_break!(self.visit_expression_mut(expr));
}
if let Some(expr) = &mut node.inner.final_expr {
try_break!(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);
}
try_break!(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);
}
try_break!(self.visit_iterable_loop_initializer_mut(&mut node.initializer));
try_break!(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);
}
try_break!(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);
}
try_break!(self.visit_iterable_loop_initializer_mut(&mut node.init));
try_break!(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>,
) -> ControlFlow<()> {
let index = self.index;
if let Some(scope) = name_scope {
if !scope.all_bindings_local() {
self.index += 1;
}
scope.set_index(self.index);
}
self.index += 1;
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);
}
try_break!(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);
}
try_break!(self.visit_function_body_mut(body));
self.index = index;
ControlFlow::Continue(())
}
}
/// `GlobalDeclarationInstantiation ( script, env )` /// `GlobalDeclarationInstantiation ( script, env )`
/// ///
/// More information: /// More information:

16
core/ast/src/source.rs

@ -7,7 +7,7 @@ use crate::{
scope::Scope, scope::Scope,
scope_analyzer::{ scope_analyzer::{
analyze_binding_escapes, collect_bindings, eval_declaration_instantiation_scope, analyze_binding_escapes, collect_bindings, eval_declaration_instantiation_scope,
EvalDeclarationBindings, optimize_scope_indicies, EvalDeclarationBindings,
}, },
visitor::{VisitWith, Visitor, VisitorMut}, visitor::{VisitWith, Visitor, VisitorMut},
ModuleItemList, StatementList, ModuleItemList, StatementList,
@ -56,7 +56,11 @@ impl Script {
if !collect_bindings(self, self.strict(), false, scope, interner) { if !collect_bindings(self, self.strict(), false, scope, interner) {
return false; return false;
} }
analyze_binding_escapes(self, false, scope.clone(), interner) if !analyze_binding_escapes(self, false, scope.clone(), interner) {
return false;
}
optimize_scope_indicies(self, scope);
true
} }
/// Analyze the scope of the script in eval mode. /// Analyze the scope of the script in eval mode.
@ -84,10 +88,10 @@ impl Script {
if !collect_bindings(self, strict, true, lexical_scope, interner) { if !collect_bindings(self, strict, true, lexical_scope, interner) {
return Err(String::from("Failed to analyze scope")); return Err(String::from("Failed to analyze scope"));
} }
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"));
} }
optimize_scope_indicies(self, lexical_scope);
Ok(bindings) Ok(bindings)
} }
@ -158,7 +162,11 @@ impl Module {
if !collect_bindings(self, true, false, scope, interner) { if !collect_bindings(self, true, false, scope, interner) {
return false; return false;
} }
analyze_binding_escapes(self, false, scope.clone(), interner) if !analyze_binding_escapes(self, false, scope.clone(), interner) {
return false;
}
optimize_scope_indicies(self, &self.scope.clone());
true
} }
} }

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

@ -79,14 +79,7 @@ impl ByteCompiler<'_> {
.map_or(Sym::EMPTY_STRING, Identifier::sym) .map_or(Sym::EMPTY_STRING, Identifier::sym)
.to_js_string(self.interner()); .to_js_string(self.interner());
let outer_scope = if let Some(name_scope) = class.name_scope { let outer_scope = self.push_declarative_scope(class.name_scope);
let outer_scope = self.lexical_scope.clone();
let scope_index = self.push_scope(name_scope);
self.emit_with_varying_operand(Opcode::PushScope, scope_index);
Some(outer_scope)
} else {
None
};
let mut compiler = ByteCompiler::new( let mut compiler = ByteCompiler::new(
class_name.clone(), class_name.clone(),
@ -181,9 +174,11 @@ impl ByteCompiler<'_> {
let mut static_elements = Vec::new(); let mut static_elements = Vec::new();
let mut static_field_name_count = 0; let mut static_field_name_count = 0;
if outer_scope.is_some() { if let Some(scope) = class.name_scope {
let binding = scope.get_identifier_reference(class_name.clone());
let index = self.get_or_insert_binding(binding);
self.emit_opcode(Opcode::Dup); self.emit_opcode(Opcode::Dup);
self.emit_binding(BindingOpcode::InitLexical, class_name.clone()); self.emit_binding_access(Opcode::PutLexicalValue, &index);
} }
for element in class.elements { for element in class.elements {
@ -474,12 +469,7 @@ impl ByteCompiler<'_> {
self.emit_opcode(Opcode::Swap); self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::Pop); self.emit_opcode(Opcode::Pop);
if let Some(outer_scope) = outer_scope { self.pop_declarative_scope(outer_scope);
self.pop_scope();
self.lexical_scope = outer_scope;
self.emit_opcode(Opcode::PopEnvironment);
}
self.emit_opcode(Opcode::PopPrivateEnvironment); self.emit_opcode(Opcode::PopPrivateEnvironment);
if !expression { if !expression {

13
core/engine/src/bytecompiler/declarations.rs

@ -963,10 +963,7 @@ impl ByteCompiler<'_> {
} }
// 19-20 // 19-20
if let Some(scope) = scopes.parameters_eval_scope() { drop(self.push_declarative_scope(scopes.parameters_eval_scope()));
let scope_index = self.push_scope(scope);
self.emit_with_varying_operand(Opcode::PushScope, scope_index);
}
let scope = self.lexical_scope.clone(); let scope = self.lexical_scope.clone();
@ -1057,8 +1054,7 @@ impl ByteCompiler<'_> {
// visibility of declarations in the function body. // visibility of declarations in the function body.
// b. Let varEnv be NewDeclarativeEnvironment(env). // b. Let varEnv be NewDeclarativeEnvironment(env).
// c. Set the VariableEnvironment of calleeContext to varEnv. // c. Set the VariableEnvironment of calleeContext to varEnv.
let scope_index = self.push_scope(scope); drop(self.push_declarative_scope(Some(scope)));
self.emit_with_varying_operand(Opcode::PushScope, scope_index);
let mut variable_scope = self.lexical_scope.clone(); let mut variable_scope = self.lexical_scope.clone();
@ -1177,10 +1173,7 @@ impl ByteCompiler<'_> {
} }
// 30-31 // 30-31
if let Some(scope) = scopes.lexical_scope() { drop(self.push_declarative_scope(scopes.lexical_scope()));
let scope_index = self.push_scope(scope);
self.emit_with_varying_operand(Opcode::PushScope, scope_index);
}
// 35. Let privateEnv be the PrivateEnvironment of calleeContext. // 35. Let privateEnv be the PrivateEnvironment of calleeContext.
// 36. For each Parse Node f of functionsToInitialize, do // 36. For each Parse Node f of functionsToInitialize, do

32
core/engine/src/bytecompiler/env.rs

@ -1,5 +1,7 @@
use boa_ast::scope::Scope; use boa_ast::scope::Scope;
use crate::vm::{Constant, Opcode};
use super::ByteCompiler; use super::ByteCompiler;
impl ByteCompiler<'_> { impl ByteCompiler<'_> {
@ -9,8 +11,7 @@ impl ByteCompiler<'_> {
self.current_open_environments_count += 1; self.current_open_environments_count += 1;
let index = self.constants.len() as u32; let index = self.constants.len() as u32;
self.constants self.constants.push(Constant::Scope(scope.clone()));
.push(crate::vm::Constant::Scope(scope.clone()));
if scope.is_function() { if scope.is_function() {
self.variable_scope = scope.clone(); self.variable_scope = scope.clone();
@ -21,6 +22,33 @@ impl ByteCompiler<'_> {
index index
} }
/// Push a declarative scope.
///
/// Returns the outer scope.
#[must_use]
pub(crate) fn push_declarative_scope(&mut self, scope: Option<&Scope>) -> Option<Scope> {
let mut scope = scope?.clone();
if !scope.all_bindings_local() {
self.current_open_environments_count += 1;
let index = self.constants.len() as u32;
self.constants.push(Constant::Scope(scope.clone()));
self.emit_with_varying_operand(Opcode::PushScope, index);
}
std::mem::swap(&mut self.lexical_scope, &mut scope);
Some(scope)
}
/// Pop a declarative scope.
pub(crate) fn pop_declarative_scope(&mut self, scope: Option<Scope>) {
if let Some(mut scope) = scope {
std::mem::swap(&mut self.lexical_scope, &mut scope);
if !scope.all_bindings_local() {
self.current_open_environments_count -= 1;
self.emit_opcode(Opcode::PopEnvironment);
}
}
}
/// Pops the top scope. /// Pops the top scope.
pub(crate) fn pop_scope(&mut self) { pub(crate) fn pop_scope(&mut self) {
self.current_open_environments_count -= 1; self.current_open_environments_count -= 1;

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

@ -129,8 +129,10 @@ impl FunctionCompiler {
} }
if let Some(scope) = self.name_scope { if let Some(scope) = self.name_scope {
compiler.code_block_flags |= CodeBlockFlags::HAS_BINDING_IDENTIFIER; if !scope.all_bindings_local() {
let _ = compiler.push_scope(&scope); compiler.code_block_flags |= CodeBlockFlags::HAS_BINDING_IDENTIFIER;
let _ = compiler.push_scope(&scope);
}
} }
// Function environment // Function environment
let _ = compiler.push_scope(scopes.function_scope()); let _ = compiler.push_scope(scopes.function_scope());

19
core/engine/src/bytecompiler/statement/block.rs

@ -1,25 +1,12 @@
use crate::{bytecompiler::ByteCompiler, vm::Opcode}; use crate::bytecompiler::ByteCompiler;
use boa_ast::statement::Block; use boa_ast::statement::Block;
impl ByteCompiler<'_> { impl ByteCompiler<'_> {
/// Compile a [`Block`] `boa_ast` node /// Compile a [`Block`] `boa_ast` node
pub(crate) fn compile_block(&mut self, block: &Block, use_expr: bool) { pub(crate) fn compile_block(&mut self, block: &Block, use_expr: bool) {
let outer_scope = if let Some(scope) = block.scope() { let scope = self.push_declarative_scope(block.scope());
let outer_scope = self.lexical_scope.clone();
let scope_index = self.push_scope(scope);
self.emit_with_varying_operand(Opcode::PushScope, scope_index);
Some(outer_scope)
} else {
None
};
self.block_declaration_instantiation(block); self.block_declaration_instantiation(block);
self.compile_statement_list(block.statement_list(), use_expr, true); self.compile_statement_list(block.statement_list(), use_expr, true);
self.pop_declarative_scope(scope);
if let Some(outer_scope) = outer_scope {
self.pop_scope();
self.lexical_scope = outer_scope;
self.emit_opcode(Opcode::PopEnvironment);
}
} }
} }

96
core/engine/src/bytecompiler/statement/loop.rs

@ -22,6 +22,7 @@ impl ByteCompiler<'_> {
use_expr: bool, use_expr: bool,
) { ) {
let mut let_binding_indices = None; let mut let_binding_indices = None;
let mut outer_scope_local = None;
let mut outer_scope = None; let mut outer_scope = None;
if let Some(init) = for_loop.init() { if let Some(init) = for_loop.init() {
@ -31,9 +32,16 @@ impl ByteCompiler<'_> {
self.compile_var_decl(decl); self.compile_var_decl(decl);
} }
ForLoopInitializer::Lexical(decl) => { ForLoopInitializer::Lexical(decl) => {
outer_scope = Some(self.lexical_scope.clone()); let scope_index = if decl.scope().all_bindings_local() {
let scope_index = self.push_scope(decl.scope()); outer_scope_local = Some(self.lexical_scope.clone());
self.emit_with_varying_operand(Opcode::PushScope, scope_index); self.lexical_scope = decl.scope().clone();
None
} else {
outer_scope = Some(self.lexical_scope.clone());
let scope_index = self.push_scope(decl.scope());
self.emit_with_varying_operand(Opcode::PushScope, scope_index);
Some(scope_index)
};
let names = bound_names(decl.declaration()); let names = bound_names(decl.declaration());
if decl.declaration().is_const() { if decl.declaration().is_const() {
@ -41,8 +49,8 @@ impl ByteCompiler<'_> {
let mut indices = Vec::new(); let mut indices = Vec::new();
for name in &names { for name in &names {
let name = name.to_js_string(self.interner()); let name = name.to_js_string(self.interner());
let binding = self let binding = decl
.lexical_scope .scope()
.get_binding_reference(&name) .get_binding_reference(&name)
.expect("binding must exist"); .expect("binding must exist");
let index = self.get_or_insert_binding(binding); let index = self.get_or_insert_binding(binding);
@ -62,8 +70,10 @@ impl ByteCompiler<'_> {
self.emit_binding_access(Opcode::GetName, index); self.emit_binding_access(Opcode::GetName, index);
} }
self.emit_opcode(Opcode::PopEnvironment); if let Some(index) = scope_index {
self.emit_with_varying_operand(Opcode::PushScope, *scope_index); self.emit_opcode(Opcode::PopEnvironment);
self.emit_with_varying_operand(Opcode::PushScope, *index);
}
for index in let_binding_indices.iter().rev() { for index in let_binding_indices.iter().rev() {
self.emit_binding_access(Opcode::PutLexicalValue, index); self.emit_binding_access(Opcode::PutLexicalValue, index);
@ -85,8 +95,10 @@ impl ByteCompiler<'_> {
self.emit_binding_access(Opcode::GetName, index); self.emit_binding_access(Opcode::GetName, index);
} }
self.emit_opcode(Opcode::PopEnvironment); if let Some(index) = scope_index {
self.emit_with_varying_operand(Opcode::PushScope, *scope_index); self.emit_opcode(Opcode::PopEnvironment);
self.emit_with_varying_operand(Opcode::PushScope, *index);
}
for index in let_binding_indices.iter().rev() { for index in let_binding_indices.iter().rev() {
self.emit_binding_access(Opcode::PutLexicalValue, index); self.emit_binding_access(Opcode::PutLexicalValue, index);
@ -115,11 +127,10 @@ impl ByteCompiler<'_> {
self.patch_jump(exit); self.patch_jump(exit);
self.pop_loop_control_info(); self.pop_loop_control_info();
if let Some(outer_scope) = outer_scope { if let Some(outer_scope_local) = outer_scope_local {
self.pop_scope(); self.lexical_scope = outer_scope_local;
self.lexical_scope = outer_scope;
self.emit_opcode(Opcode::PopEnvironment);
} }
self.pop_declarative_scope(outer_scope);
} }
pub(crate) fn compile_for_in_loop( pub(crate) fn compile_for_in_loop(
@ -138,17 +149,9 @@ impl ByteCompiler<'_> {
} }
} }
} }
if let Some(scope) = for_in_loop.target_scope() { let outer_scope = self.push_declarative_scope(for_in_loop.target_scope());
let outer_scope = self.lexical_scope.clone(); self.compile_expr(for_in_loop.target(), true);
let scope_index = self.push_scope(scope); self.pop_declarative_scope(outer_scope);
self.emit_with_varying_operand(Opcode::PushScope, scope_index);
self.compile_expr(for_in_loop.target(), true);
self.pop_scope();
self.lexical_scope = outer_scope;
self.emit_opcode(Opcode::PopEnvironment);
} else {
self.compile_expr(for_in_loop.target(), true);
}
let early_exit = self.jump_if_null_or_undefined(); let early_exit = self.jump_if_null_or_undefined();
self.emit_opcode(Opcode::CreateForInIterator); self.emit_opcode(Opcode::CreateForInIterator);
@ -163,13 +166,7 @@ impl ByteCompiler<'_> {
self.emit_opcode(Opcode::IteratorValue); self.emit_opcode(Opcode::IteratorValue);
let mut outer_scope = None; let outer_scope = self.push_declarative_scope(for_in_loop.scope());
if let Some(scope) = for_in_loop.scope() {
outer_scope = Some(self.lexical_scope.clone());
let scope_index = self.push_scope(scope);
self.emit_with_varying_operand(Opcode::PushScope, scope_index);
}
match for_in_loop.initializer() { match for_in_loop.initializer() {
IterableLoopInitializer::Identifier(ident) => { IterableLoopInitializer::Identifier(ident) => {
@ -208,12 +205,7 @@ impl ByteCompiler<'_> {
} }
self.compile_stmt(for_in_loop.body(), use_expr, true); self.compile_stmt(for_in_loop.body(), use_expr, true);
self.pop_declarative_scope(outer_scope);
if let Some(outer_scope) = outer_scope {
self.pop_scope();
self.lexical_scope = outer_scope;
self.emit_opcode(Opcode::PopEnvironment);
}
self.emit(Opcode::Jump, &[Operand::U32(start_address)]); self.emit(Opcode::Jump, &[Operand::U32(start_address)]);
@ -234,17 +226,9 @@ impl ByteCompiler<'_> {
label: Option<Sym>, label: Option<Sym>,
use_expr: bool, use_expr: bool,
) { ) {
if let Some(scope) = for_of_loop.iterable_scope() { let outer_scope = self.push_declarative_scope(for_of_loop.iterable_scope());
let outer_scope = self.lexical_scope.clone(); self.compile_expr(for_of_loop.iterable(), true);
let scope_index = self.push_scope(scope); self.pop_declarative_scope(outer_scope);
self.emit_with_varying_operand(Opcode::PushScope, scope_index);
self.compile_expr(for_of_loop.iterable(), true);
self.pop_scope();
self.lexical_scope = outer_scope;
self.emit_opcode(Opcode::PopEnvironment);
} else {
self.compile_expr(for_of_loop.iterable(), true);
}
if for_of_loop.r#await() { if for_of_loop.r#await() {
self.emit_opcode(Opcode::GetAsyncIterator); self.emit_opcode(Opcode::GetAsyncIterator);
@ -271,14 +255,7 @@ impl ByteCompiler<'_> {
let exit = self.jump_if_true(); let exit = self.jump_if_true();
self.emit_opcode(Opcode::IteratorValue); self.emit_opcode(Opcode::IteratorValue);
let mut outer_scope = None; let outer_scope = self.push_declarative_scope(for_of_loop.scope());
if let Some(scope) = for_of_loop.scope() {
outer_scope = Some(self.lexical_scope.clone());
let scope_index = self.push_scope(scope);
self.emit_with_varying_operand(Opcode::PushScope, scope_index);
}
let handler_index = self.push_handler(); let handler_index = self.push_handler();
match for_of_loop.initializer() { match for_of_loop.initializer() {
@ -354,12 +331,7 @@ impl ByteCompiler<'_> {
self.patch_jump(exit); self.patch_jump(exit);
} }
if let Some(outer_scope) = outer_scope { self.pop_declarative_scope(outer_scope);
self.pop_scope();
self.lexical_scope = outer_scope;
self.emit_opcode(Opcode::PopEnvironment);
}
self.emit(Opcode::Jump, &[Operand::U32(start_address)]); self.emit(Opcode::Jump, &[Operand::U32(start_address)]);
self.patch_jump(exit); self.patch_jump(exit);

17
core/engine/src/bytecompiler/statement/switch.rs

@ -5,15 +5,7 @@ impl ByteCompiler<'_> {
/// Compile a [`Switch`] `boa_ast` node /// Compile a [`Switch`] `boa_ast` node
pub(crate) fn compile_switch(&mut self, switch: &Switch, use_expr: bool) { pub(crate) fn compile_switch(&mut self, switch: &Switch, use_expr: bool) {
self.compile_expr(switch.val(), true); self.compile_expr(switch.val(), true);
let outer_scope = self.push_declarative_scope(switch.scope());
let outer_scope = if let Some(scope) = switch.scope() {
let outer_scope = self.lexical_scope.clone();
let scope_index = self.push_scope(scope);
self.emit_with_varying_operand(Opcode::PushScope, scope_index);
Some(outer_scope)
} else {
None
};
self.block_declaration_instantiation(switch); self.block_declaration_instantiation(switch);
@ -55,11 +47,6 @@ impl ByteCompiler<'_> {
} }
self.pop_switch_control_info(); self.pop_switch_control_info();
self.pop_declarative_scope(outer_scope);
if let Some(outer_scope) = outer_scope {
self.pop_scope();
self.lexical_scope = outer_scope;
self.emit_opcode(Opcode::PopEnvironment);
}
} }
} }

10
core/engine/src/bytecompiler/statement/try.rs

@ -108,11 +108,7 @@ impl ByteCompiler<'_> {
} }
pub(crate) fn compile_catch_stmt(&mut self, catch: &Catch, _has_finally: bool, use_expr: bool) { pub(crate) fn compile_catch_stmt(&mut self, catch: &Catch, _has_finally: bool, use_expr: bool) {
// stack: exception let outer_scope = self.push_declarative_scope(Some(catch.scope()));
let outer_scope = self.lexical_scope.clone();
let scope_index = self.push_scope(catch.scope());
self.emit_with_varying_operand(Opcode::PushScope, scope_index);
if let Some(binding) = catch.parameter() { if let Some(binding) = catch.parameter() {
match binding { match binding {
@ -130,9 +126,7 @@ impl ByteCompiler<'_> {
self.compile_catch_finally_block(catch.block(), use_expr); self.compile_catch_finally_block(catch.block(), use_expr);
self.pop_scope(); self.pop_declarative_scope(outer_scope);
self.lexical_scope = outer_scope;
self.emit_opcode(Opcode::PopEnvironment);
} }
pub(crate) fn compile_finally_stmt(&mut self, finally: &Finally, has_catch: bool) { pub(crate) fn compile_finally_stmt(&mut self, finally: &Finally, has_catch: bool) {

Loading…
Cancel
Save