use crate::{environment::lexical_environment::VariableScope, object::JsObject};
use crate::{
environment::lexical_environment::{Environment, EnvironmentType},
gc::{Finalize, Trace},
Context, JsResult, JsValue,
};
use std::fmt::Debug;
pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
fn has_binding(&self, name: &str, context: &mut Context) -> JsResult<bool>;
fn create_mutable_binding(
&self,
name: &str,
deletion: bool,
allow_name_reuse: bool,
context: &mut Context,
) -> JsResult<()>;
fn create_immutable_binding(
&self,
name: &str,
strict: bool,
context: &mut Context,
) -> JsResult<()>;
fn initialize_binding(&self, name: &str, value: JsValue, context: &mut Context)
-> JsResult<()>;
fn set_mutable_binding(
&self,
name: &str,
value: JsValue,
strict: bool,
context: &mut Context,
) -> JsResult<()>;
fn get_binding_value(
&self,
name: &str,
strict: bool,
context: &mut Context,
) -> JsResult<JsValue>;
fn delete_binding(&self, name: &str, context: &mut Context) -> JsResult<bool>;
fn has_this_binding(&self) -> bool;
fn get_this_binding(&self, context: &mut Context) -> JsResult<JsValue>;
fn has_super_binding(&self) -> bool;
fn with_base_object(&self) -> Option<JsObject>;
fn get_outer_environment_ref(&self) -> Option<&Environment>;
fn get_outer_environment(&self) -> Option<Environment> {
self.get_outer_environment_ref().cloned()
}
fn set_outer_environment(&mut self, env: Environment);
fn get_environment_type(&self) -> EnvironmentType;
fn recursive_get_this_binding(&self, context: &mut Context) -> JsResult<JsValue> {
if self.has_this_binding() {
self.get_this_binding(context)
} else {
match self.get_outer_environment_ref() {
Some(outer) => outer.recursive_get_this_binding(context),
None => Ok(JsValue::undefined()),
}
}
}
fn recursive_create_mutable_binding(
&self,
name: &str,
deletion: bool,
scope: VariableScope,
context: &mut Context,
) -> JsResult<()> {
match scope {
VariableScope::Block => self.create_mutable_binding(name, deletion, false, context),
VariableScope::Function => self
.get_outer_environment_ref()
.expect("No function or global environment")
.recursive_create_mutable_binding(name, deletion, scope, context),
}
}
fn recursive_create_immutable_binding(
&self,
name: &str,
deletion: bool,
scope: VariableScope,
context: &mut Context,
) -> JsResult<()> {
match scope {
VariableScope::Block => self.create_immutable_binding(name, deletion, context),
VariableScope::Function => self
.get_outer_environment_ref()
.expect("No function or global environment")
.recursive_create_immutable_binding(name, deletion, scope, context),
}
}
fn recursive_set_mutable_binding(
&self,
name: &str,
value: JsValue,
strict: bool,
context: &mut Context,
) -> JsResult<()> {
if self.has_binding(name, context)? {
self.set_mutable_binding(name, value, strict, context)
} else {
self.get_outer_environment_ref()
.expect("Environment stack underflow")
.recursive_set_mutable_binding(name, value, strict, context)
}
}
fn recursive_initialize_binding(
&self,
name: &str,
value: JsValue,
context: &mut Context,
) -> JsResult<()> {
if self.has_binding(name, context)? {
self.initialize_binding(name, value, context)
} else {
self.get_outer_environment_ref()
.expect("Environment stack underflow")
.recursive_initialize_binding(name, value, context)
}
}
fn recursive_has_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
Ok(self.has_binding(name, context)?
|| match self.get_outer_environment_ref() {
Some(outer) => outer.recursive_has_binding(name, context)?,
None => false,
})
}
fn recursive_get_binding_value(&self, name: &str, context: &mut Context) -> JsResult<JsValue> {
if self.has_binding(name, context)? {
self.get_binding_value(name, false, context)
} else {
match self.get_outer_environment_ref() {
Some(outer) => outer.recursive_get_binding_value(name, context),
None => context.throw_reference_error(format!("{} is not defined", name)),
}
}
}
}