use gc::Gc;
use crate::{
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType, VariableScope},
},
gc::{empty_trace, Finalize, Trace},
object::JsObject,
Context, JsResult, JsValue,
};
#[derive(Copy, Finalize, Debug, Clone)]
pub enum BindingStatus {
Lexical,
Initialized,
Uninitialized,
}
unsafe impl Trace for BindingStatus {
empty_trace!();
}
#[derive(Debug, Trace, Finalize, Clone)]
pub struct FunctionEnvironmentRecord {
pub declarative_record: DeclarativeEnvironmentRecord,
pub this_value: JsValue,
pub this_binding_status: BindingStatus,
pub function: JsObject,
pub home_object: JsValue,
pub new_target: JsValue,
}
impl FunctionEnvironmentRecord {
pub fn new(
f: JsObject,
this: Option<JsValue>,
outer: Option<Environment>,
binding_status: BindingStatus,
new_target: JsValue,
context: &mut Context,
) -> JsResult<FunctionEnvironmentRecord> {
let mut func_env = FunctionEnvironmentRecord {
declarative_record: DeclarativeEnvironmentRecord::new(outer), function: f,
this_binding_status: binding_status,
home_object: JsValue::undefined(),
new_target,
this_value: JsValue::undefined(),
};
if let Some(v) = this {
func_env.bind_this_value(v, context)?;
}
Ok(func_env)
}
pub fn bind_this_value(&mut self, value: JsValue, context: &mut Context) -> JsResult<JsValue> {
match self.this_binding_status {
BindingStatus::Lexical => {
panic!("Cannot bind to an arrow function!");
}
BindingStatus::Initialized => {
context.throw_reference_error("Cannot bind to an initialized function!")
}
BindingStatus::Uninitialized => {
self.this_value = value.clone();
self.this_binding_status = BindingStatus::Initialized;
Ok(value)
}
}
}
pub fn get_super_base(&self) -> JsValue {
let home = &self.home_object;
if home.is_undefined() {
JsValue::undefined()
} else {
assert!(home.is_object());
home.as_object()
.expect("home_object must be an Object")
.prototype_instance()
}
}
}
impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
fn has_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
self.declarative_record.has_binding(name, context)
}
fn create_mutable_binding(
&self,
name: &str,
deletion: bool,
allow_name_reuse: bool,
context: &mut Context,
) -> JsResult<()> {
self.declarative_record
.create_mutable_binding(name, deletion, allow_name_reuse, context)
}
fn create_immutable_binding(
&self,
name: &str,
strict: bool,
context: &mut Context,
) -> JsResult<()> {
self.declarative_record
.create_immutable_binding(name, strict, context)
}
fn initialize_binding(
&self,
name: &str,
value: JsValue,
context: &mut Context,
) -> JsResult<()> {
self.declarative_record
.initialize_binding(name, value, context)
}
fn set_mutable_binding(
&self,
name: &str,
value: JsValue,
strict: bool,
context: &mut Context,
) -> JsResult<()> {
self.declarative_record
.set_mutable_binding(name, value, strict, context)
}
fn get_binding_value(
&self,
name: &str,
strict: bool,
context: &mut Context,
) -> JsResult<JsValue> {
self.declarative_record
.get_binding_value(name, strict, context)
}
fn delete_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
self.declarative_record.delete_binding(name, context)
}
fn has_this_binding(&self) -> bool {
!matches!(self.this_binding_status, BindingStatus::Lexical)
}
fn has_super_binding(&self) -> bool {
if let BindingStatus::Lexical = self.this_binding_status {
false
} else {
!self.home_object.is_undefined()
}
}
fn get_this_binding(&self, context: &mut Context) -> JsResult<JsValue> {
match self.this_binding_status {
BindingStatus::Lexical => {
panic!("There is no this for a lexical function record");
}
BindingStatus::Uninitialized => {
context.throw_reference_error("Uninitialized binding for this function")
}
BindingStatus::Initialized => Ok(self.this_value.clone()),
}
}
fn with_base_object(&self) -> Option<JsObject> {
None
}
fn get_outer_environment_ref(&self) -> Option<&Environment> {
self.declarative_record.get_outer_environment_ref()
}
fn set_outer_environment(&mut self, env: Environment) {
self.declarative_record.set_outer_environment(env)
}
fn get_environment_type(&self) -> EnvironmentType {
EnvironmentType::Function
}
fn recursive_create_mutable_binding(
&self,
name: &str,
deletion: bool,
_scope: VariableScope,
context: &mut Context,
) -> JsResult<()> {
self.create_mutable_binding(name, deletion, false, context)
}
fn recursive_create_immutable_binding(
&self,
name: &str,
deletion: bool,
_scope: VariableScope,
context: &mut Context,
) -> JsResult<()> {
self.create_immutable_binding(name, deletion, context)
}
}
impl From<FunctionEnvironmentRecord> for Environment {
fn from(env: FunctionEnvironmentRecord) -> Environment {
Gc::new(Box::new(env))
}
}