use gc::Gc;
use crate::{
environment::{
environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType},
},
gc::{Finalize, Trace},
object::JsObject,
property::PropertyDescriptor,
symbol::WellKnownSymbols,
Context, JsResult, JsValue,
};
#[derive(Debug, Trace, Finalize, Clone)]
pub struct ObjectEnvironmentRecord {
pub bindings: JsObject,
pub with_environment: bool,
pub outer_env: Option<Environment>,
}
impl ObjectEnvironmentRecord {
pub fn new(object: JsObject, environment: Option<Environment>) -> ObjectEnvironmentRecord {
ObjectEnvironmentRecord {
bindings: object,
outer_env: environment,
with_environment: false,
}
}
}
impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
fn has_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
if !self.bindings.has_property(name, context)? {
return Ok(false);
}
if !self.with_environment {
return Ok(true);
}
if let Some(unscopables) = self
.bindings
.get(WellKnownSymbols::unscopables(), context)?
.as_object()
{
if unscopables.get(name, context)?.to_boolean() {
return Ok(false);
}
}
Ok(true)
}
fn create_mutable_binding(
&self,
name: &str,
deletion: bool,
_allow_name_reuse: bool,
context: &mut Context,
) -> JsResult<()> {
self.bindings.define_property_or_throw(
name,
PropertyDescriptor::builder()
.value(JsValue::undefined())
.writable(true)
.enumerable(true)
.configurable(deletion),
context,
)?;
Ok(())
}
fn create_immutable_binding(
&self,
_name: &str,
_strict: bool,
_context: &mut Context,
) -> JsResult<()> {
Ok(())
}
fn initialize_binding(
&self,
name: &str,
value: JsValue,
context: &mut Context,
) -> JsResult<()> {
self.set_mutable_binding(name, value, false, context)
}
fn set_mutable_binding(
&self,
name: &str,
value: JsValue,
strict: bool,
context: &mut Context,
) -> JsResult<()> {
let still_exists = self.bindings.has_property(name, context)?;
if !still_exists && strict {
return Err(context.construct_reference_error("Binding already exists"));
}
self.bindings.set(name, value, strict, context)?;
Ok(())
}
fn get_binding_value(
&self,
name: &str,
strict: bool,
context: &mut Context,
) -> JsResult<JsValue> {
if !self.bindings.__has_property__(&name.into(), context)? {
if !strict {
return Ok(JsValue::undefined());
} else {
return context.throw_reference_error(format!("{} has no binding", name));
}
}
self.bindings.get(name, context)
}
fn delete_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
self.bindings.__delete__(&name.into(), context)
}
fn has_this_binding(&self) -> bool {
false
}
fn has_super_binding(&self) -> bool {
false
}
fn with_base_object(&self) -> Option<JsObject> {
if self.with_environment {
Some(self.bindings.clone())
} else {
None
}
}
fn get_this_binding(&self, _context: &mut Context) -> JsResult<JsValue> {
Ok(JsValue::undefined())
}
fn get_outer_environment_ref(&self) -> Option<&Environment> {
self.outer_env.as_ref()
}
fn set_outer_environment(&mut self, env: Environment) {
self.outer_env = Some(env);
}
fn get_environment_type(&self) -> EnvironmentType {
EnvironmentType::Function
}
}
impl From<ObjectEnvironmentRecord> for Environment {
fn from(env: ObjectEnvironmentRecord) -> Environment {
Gc::new(Box::new(env))
}
}