diff --git a/boa/src/environment/declarative_environment_record.rs b/boa/src/environment/declarative_environment_record.rs index 7d585b739a4..0ba2e4f6153 100644 --- a/boa/src/environment/declarative_environment_record.rs +++ b/boa/src/environment/declarative_environment_record.rs @@ -184,8 +184,8 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord { Value::undefined() } - fn get_outer_environment(&self) -> Option { - self.outer_env.as_ref().cloned() + fn get_outer_environment_ref(&self) -> Option<&Environment> { + self.outer_env.as_ref() } fn set_outer_environment(&mut self, env: Environment) { diff --git a/boa/src/environment/environment_record_trait.rs b/boa/src/environment/environment_record_trait.rs index 1cf0243121b..aedb5a70fe4 100644 --- a/boa/src/environment/environment_record_trait.rs +++ b/boa/src/environment/environment_record_trait.rs @@ -9,6 +9,7 @@ //! There are 5 Environment record kinds. They all have methods in common, these are implemented as a the `EnvironmentRecordTrait` //! use super::ErrorKind; +use crate::environment::lexical_environment::VariableScope; use crate::{ environment::lexical_environment::{Environment, EnvironmentType}, gc::{Finalize, Trace}, @@ -88,7 +89,10 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { fn with_base_object(&self) -> Value; /// Get the next environment up - fn get_outer_environment(&self) -> Option; + fn get_outer_environment_ref(&self) -> Option<&Environment>; + fn get_outer_environment(&self) -> Option { + self.get_outer_environment_ref().cloned() + } /// Set the next environment up fn set_outer_environment(&mut self, env: Environment); @@ -98,4 +102,110 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { /// Fetch global variable fn get_global_object(&self) -> Option; + + fn recursive_get_this_binding(&self) -> Result { + if self.has_this_binding() { + self.get_this_binding() + } else { + match self.get_outer_environment_ref() { + Some(outer) => outer.borrow().recursive_get_this_binding(), + None => Ok(Value::Undefined), + } + } + } + + /// Create mutable binding while handling outer environments + fn recursive_create_mutable_binding( + &mut self, + name: String, + deletion: bool, + scope: VariableScope, + ) -> Result<(), ErrorKind> { + match (scope, self.get_environment_type()) { + (VariableScope::Block, _) + | (VariableScope::Function, EnvironmentType::Function) + | (VariableScope::Function, EnvironmentType::Global) => { + self.create_mutable_binding(name, deletion, false) + } + _ => self + .get_outer_environment_ref() + .expect("No function or global environment") + .borrow_mut() + .recursive_create_mutable_binding(name, deletion, scope), + } + } + + /// Create immutable binding while handling outer environments + fn recursive_create_immutable_binding( + &mut self, + name: String, + deletion: bool, + scope: VariableScope, + ) -> Result<(), ErrorKind> { + match (scope, self.get_environment_type()) { + (VariableScope::Block, _) + | (VariableScope::Function, EnvironmentType::Function) + | (VariableScope::Function, EnvironmentType::Global) => { + self.create_immutable_binding(name, deletion) + } + _ => self + .get_outer_environment_ref() + .expect("No function or global environment") + .borrow_mut() + .recursive_create_immutable_binding(name, deletion, scope), + } + } + + /// Set mutable binding while handling outer environments + fn recursive_set_mutable_binding( + &mut self, + name: &str, + value: Value, + strict: bool, + ) -> Result<(), ErrorKind> { + if self.has_binding(name) || self.get_environment_type() == EnvironmentType::Global { + self.set_mutable_binding(name, value, strict) + } else { + self.get_outer_environment_ref() + .expect("Environment stack underflow") + .borrow_mut() + .recursive_set_mutable_binding(name, value, strict) + } + } + + /// Initialize binding while handling outer environments + fn recursive_initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> { + if self.has_binding(name) || self.get_environment_type() == EnvironmentType::Global { + self.initialize_binding(name, value) + } else { + self.get_outer_environment_ref() + .expect("Environment stack underflow") + .borrow_mut() + .recursive_initialize_binding(name, value) + } + } + + /// Check if a binding exists in current or any outer environment + fn recursive_has_binding(&self, name: &str) -> bool { + self.has_binding(name) + || match self.get_outer_environment_ref() { + Some(outer) => outer.borrow().recursive_has_binding(name), + None => false, + } + } + + /// Retrieve binding from current or any outer environment + fn recursive_get_binding_value(&self, name: &str) -> Result { + if self.has_binding(name) { + self.get_binding_value(name, false) + } else { + match self.get_outer_environment_ref() { + Some(outer) => outer.borrow().recursive_get_binding_value(name), + None => Err(ErrorKind::new_reference_error(format!( + "{} is not defined", + name + ))), + } + } + } } diff --git a/boa/src/environment/function_environment_record.rs b/boa/src/environment/function_environment_record.rs index 5b569d384ee..9de88da7b32 100644 --- a/boa/src/environment/function_environment_record.rs +++ b/boa/src/environment/function_environment_record.rs @@ -160,8 +160,8 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord { Value::undefined() } - fn get_outer_environment(&self) -> Option { - self.declarative_record.get_outer_environment() + fn get_outer_environment_ref(&self) -> Option<&Environment> { + self.declarative_record.get_outer_environment_ref() } fn set_outer_environment(&mut self, env: Environment) { diff --git a/boa/src/environment/global_environment_record.rs b/boa/src/environment/global_environment_record.rs index 1d26d2e4c9b..efbc16a20f1 100644 --- a/boa/src/environment/global_environment_record.rs +++ b/boa/src/environment/global_environment_record.rs @@ -228,6 +228,10 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord { None } + fn get_outer_environment_ref(&self) -> Option<&Environment> { + None + } + fn set_outer_environment(&mut self, _env: Environment) { // TODO: Implement panic!("Not implemented yet") diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index 5ce5f41f832..cd758ac5b66 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -100,25 +100,16 @@ impl LexicalEnvironment { self.environment_stack.pop_back() } - fn environments(&self) -> impl Iterator { - std::iter::successors(Some(self.get_current_environment_ref().clone()), |env| { - env.borrow().get_outer_environment() - }) - } - pub fn get_global_object(&self) -> Option { - self.environment_stack - .get(0) - .expect("") + self.get_current_environment_ref() .borrow() .get_global_object() } pub fn get_this_binding(&self) -> Result { - self.environments() - .find(|env| env.borrow().has_this_binding()) - .map(|env| env.borrow().get_this_binding()) - .unwrap_or_else(|| Ok(Value::Undefined)) + self.get_current_environment_ref() + .borrow() + .recursive_get_this_binding() } pub fn create_mutable_binding( @@ -127,25 +118,9 @@ impl LexicalEnvironment { deletion: bool, scope: VariableScope, ) -> Result<(), ErrorKind> { - match scope { - VariableScope::Block => self - .get_current_environment() - .borrow_mut() - .create_mutable_binding(name, deletion, false), - VariableScope::Function => { - // Find the first function or global environment (from the top of the stack) - self.environments() - .find(|env| { - matches!( - env.borrow().get_environment_type(), - EnvironmentType::Function | EnvironmentType::Global - ) - }) - .expect("No function or global environment") - .borrow_mut() - .create_mutable_binding(name, deletion, false) - } - } + self.get_current_environment() + .borrow_mut() + .recursive_create_mutable_binding(name, deletion, scope) } pub fn create_immutable_binding( @@ -154,25 +129,9 @@ impl LexicalEnvironment { deletion: bool, scope: VariableScope, ) -> Result<(), ErrorKind> { - match scope { - VariableScope::Block => self - .get_current_environment() - .borrow_mut() - .create_immutable_binding(name, deletion), - VariableScope::Function => { - // Find the first function or global environment (from the top of the stack) - self.environments() - .find(|env| { - matches!( - env.borrow().get_environment_type(), - EnvironmentType::Function | EnvironmentType::Global - ) - }) - .expect("No function or global environment") - .borrow_mut() - .create_immutable_binding(name, deletion) - } - } + self.get_current_environment() + .borrow_mut() + .recursive_create_immutable_binding(name, deletion, scope) } pub fn set_mutable_binding( @@ -181,41 +140,15 @@ impl LexicalEnvironment { value: Value, strict: bool, ) -> Result<(), ErrorKind> { - // Find the first environment which has the given binding - let env = self - .environments() - .find(|env| env.borrow().has_binding(name)); - - if let Some(ref env) = env { - env - } else { - // global_env doesn't need has_binding to be satisfied in non strict mode - self.environment_stack - .get(0) - .expect("Environment stack underflow") - } - .borrow_mut() - .set_mutable_binding(name, value, strict)?; - Ok(()) + self.get_current_environment() + .borrow_mut() + .recursive_set_mutable_binding(name, value, strict) } pub fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> { - // Find the first environment which has the given binding - let env = self - .environments() - .find(|env| env.borrow().has_binding(name)); - - if let Some(ref env) = env { - env - } else { - // global_env doesn't need has_binding to be satisfied in non strict mode - self.environment_stack - .get(0) - .expect("Environment stack underflow") - } - .borrow_mut() - .initialize_binding(name, value)?; - Ok(()) + self.get_current_environment() + .borrow_mut() + .recursive_initialize_binding(name, value) } /// get_current_environment_ref is used when you only need to borrow the environment @@ -235,20 +168,15 @@ impl LexicalEnvironment { } pub fn has_binding(&self, name: &str) -> bool { - self.environments() - .any(|env| env.borrow().has_binding(name)) + self.get_current_environment_ref() + .borrow() + .recursive_has_binding(name) } pub fn get_binding_value(&self, name: &str) -> Result { - self.environments() - .find(|env| env.borrow().has_binding(name)) - .map(|env| env.borrow().get_binding_value(name, false)) - .unwrap_or_else(|| { - Err(ErrorKind::new_reference_error(format!( - "{} is not defined", - name - ))) - }) + self.get_current_environment_ref() + .borrow() + .recursive_get_binding_value(name) } } diff --git a/boa/src/environment/object_environment_record.rs b/boa/src/environment/object_environment_record.rs index 1c5639fc737..b2e83ee0318 100644 --- a/boa/src/environment/object_environment_record.rs +++ b/boa/src/environment/object_environment_record.rs @@ -127,11 +127,8 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord { Value::undefined() } - fn get_outer_environment(&self) -> Option { - match &self.outer_env { - Some(outer) => Some(outer.clone()), - None => None, - } + fn get_outer_environment_ref(&self) -> Option<&Environment> { + self.outer_env.as_ref() } fn set_outer_environment(&mut self, env: Environment) {