|
|
@ -8,6 +8,7 @@ |
|
|
|
//! from within the function.
|
|
|
|
//! from within the function.
|
|
|
|
//! More info: <https://tc39.es/ecma262/#sec-function-environment-records>
|
|
|
|
//! More info: <https://tc39.es/ecma262/#sec-function-environment-records>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use super::ErrorKind; |
|
|
|
use crate::{ |
|
|
|
use crate::{ |
|
|
|
environment::{ |
|
|
|
environment::{ |
|
|
|
declarative_environment_record::DeclarativeEnvironmentRecordBinding, |
|
|
|
declarative_environment_record::DeclarativeEnvironmentRecordBinding, |
|
|
@ -60,39 +61,49 @@ pub struct FunctionEnvironmentRecord { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl FunctionEnvironmentRecord { |
|
|
|
impl FunctionEnvironmentRecord { |
|
|
|
pub fn bind_this_value(&mut self, value: Value) { |
|
|
|
pub fn bind_this_value(&mut self, value: Value) -> Result<Value, ErrorKind> { |
|
|
|
match self.this_binding_status { |
|
|
|
match self.this_binding_status { |
|
|
|
// You can not bind an arrow function, their `this` value comes from the lexical scope above
|
|
|
|
// You can not bind an arrow function, their `this` value comes from the lexical scope above
|
|
|
|
BindingStatus::Lexical => { |
|
|
|
BindingStatus::Lexical => { |
|
|
|
// TODO: change this when error handling comes into play
|
|
|
|
|
|
|
|
panic!("Cannot bind to an arrow function!"); |
|
|
|
panic!("Cannot bind to an arrow function!"); |
|
|
|
} |
|
|
|
} |
|
|
|
// You can not bind a function twice
|
|
|
|
// You can not bind a function twice
|
|
|
|
BindingStatus::Initialized => { |
|
|
|
BindingStatus::Initialized => Err(ErrorKind::new_reference_error( |
|
|
|
// TODO: change this when error handling comes into play
|
|
|
|
"Cannot bind to an initialised function!", |
|
|
|
panic!("Reference Error: Cannot bind to an initialised function!"); |
|
|
|
)), |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BindingStatus::Uninitialized => { |
|
|
|
BindingStatus::Uninitialized => { |
|
|
|
self.this_value = value; |
|
|
|
self.this_value = value.clone(); |
|
|
|
self.this_binding_status = BindingStatus::Initialized; |
|
|
|
self.this_binding_status = BindingStatus::Initialized; |
|
|
|
|
|
|
|
Ok(value) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn get_super_base(&self) -> Value { |
|
|
|
|
|
|
|
let home = &self.home_object; |
|
|
|
|
|
|
|
if home.is_undefined() { |
|
|
|
|
|
|
|
Value::Undefined |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
assert!(home.is_object()); |
|
|
|
|
|
|
|
home.as_object() |
|
|
|
|
|
|
|
.expect("home_object must be an Object") |
|
|
|
|
|
|
|
.prototype_instance() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl EnvironmentRecordTrait for FunctionEnvironmentRecord { |
|
|
|
impl EnvironmentRecordTrait for FunctionEnvironmentRecord { |
|
|
|
// TODO: get_super_base can't implement until GetPrototypeof is implemented on object
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn has_binding(&self, name: &str) -> bool { |
|
|
|
fn has_binding(&self, name: &str) -> bool { |
|
|
|
self.env_rec.contains_key(name) |
|
|
|
self.env_rec.contains_key(name) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn create_mutable_binding(&mut self, name: String, deletion: bool) { |
|
|
|
fn create_mutable_binding(&mut self, name: String, deletion: bool) -> Result<(), ErrorKind> { |
|
|
|
if self.env_rec.contains_key(&name) { |
|
|
|
assert!( |
|
|
|
// TODO: change this when error handling comes into play
|
|
|
|
!self.env_rec.contains_key(&name), |
|
|
|
panic!("Identifier {} has already been declared", name); |
|
|
|
"Identifier {} has already been declared", |
|
|
|
} |
|
|
|
name |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
self.env_rec.insert( |
|
|
|
self.env_rec.insert( |
|
|
|
name, |
|
|
|
name, |
|
|
@ -103,28 +114,28 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord { |
|
|
|
strict: false, |
|
|
|
strict: false, |
|
|
|
}, |
|
|
|
}, |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn get_this_binding(&self) -> Value { |
|
|
|
fn get_this_binding(&self) -> Result<Value, ErrorKind> { |
|
|
|
match self.this_binding_status { |
|
|
|
match self.this_binding_status { |
|
|
|
BindingStatus::Lexical => { |
|
|
|
BindingStatus::Lexical => { |
|
|
|
// TODO: change this when error handling comes into play
|
|
|
|
|
|
|
|
panic!("There is no this for a lexical function record"); |
|
|
|
panic!("There is no this for a lexical function record"); |
|
|
|
} |
|
|
|
} |
|
|
|
BindingStatus::Uninitialized => { |
|
|
|
BindingStatus::Uninitialized => Err(ErrorKind::new_reference_error( |
|
|
|
// TODO: change this when error handling comes into play
|
|
|
|
"Uninitialised binding for this function", |
|
|
|
panic!("Reference Error: Uninitialised binding for this function"); |
|
|
|
)), |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BindingStatus::Initialized => self.this_value.clone(), |
|
|
|
BindingStatus::Initialized => Ok(self.this_value.clone()), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn create_immutable_binding(&mut self, name: String, strict: bool) -> bool { |
|
|
|
fn create_immutable_binding(&mut self, name: String, strict: bool) -> Result<(), ErrorKind> { |
|
|
|
if self.env_rec.contains_key(&name) { |
|
|
|
assert!( |
|
|
|
// TODO: change this when error handling comes into play
|
|
|
|
!self.env_rec.contains_key(&name), |
|
|
|
panic!("Identifier {} has already been declared", name); |
|
|
|
"Identifier {} has already been declared", |
|
|
|
} |
|
|
|
name |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
self.env_rec.insert( |
|
|
|
self.env_rec.insert( |
|
|
|
name, |
|
|
|
name, |
|
|
@ -135,63 +146,73 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord { |
|
|
|
strict, |
|
|
|
strict, |
|
|
|
}, |
|
|
|
}, |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
true |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn initialize_binding(&mut self, name: &str, value: Value) { |
|
|
|
fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> { |
|
|
|
if let Some(ref mut record) = self.env_rec.get_mut(name) { |
|
|
|
if let Some(ref mut record) = self.env_rec.get_mut(name) { |
|
|
|
match record.value { |
|
|
|
if record.value.is_none() { |
|
|
|
Some(_) => { |
|
|
|
record.value = Some(value); |
|
|
|
// TODO: change this when error handling comes into play
|
|
|
|
return Ok(()); |
|
|
|
panic!("Identifier {} has already been defined", name); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
None => record.value = Some(value), |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
panic!("record must have binding for {}", name) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[allow(clippy::else_if_without_else)] |
|
|
|
#[allow(clippy::else_if_without_else)] |
|
|
|
fn set_mutable_binding(&mut self, name: &str, value: Value, mut strict: bool) { |
|
|
|
fn set_mutable_binding( |
|
|
|
|
|
|
|
&mut self, |
|
|
|
|
|
|
|
name: &str, |
|
|
|
|
|
|
|
value: Value, |
|
|
|
|
|
|
|
mut strict: bool, |
|
|
|
|
|
|
|
) -> Result<(), ErrorKind> { |
|
|
|
if self.env_rec.get(name).is_none() { |
|
|
|
if self.env_rec.get(name).is_none() { |
|
|
|
if strict { |
|
|
|
if strict { |
|
|
|
// TODO: change this when error handling comes into play
|
|
|
|
return Err(ErrorKind::new_reference_error(format!( |
|
|
|
panic!("Reference Error: Cannot set mutable binding for {}", name); |
|
|
|
"{} not found", |
|
|
|
|
|
|
|
name |
|
|
|
|
|
|
|
))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
self.create_mutable_binding(name.to_owned(), true); |
|
|
|
self.create_mutable_binding(name.to_owned(), true)?; |
|
|
|
self.initialize_binding(name, value); |
|
|
|
self.initialize_binding(name, value)?; |
|
|
|
return; |
|
|
|
return Ok(()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let record: &mut DeclarativeEnvironmentRecordBinding = self.env_rec.get_mut(name).unwrap(); |
|
|
|
let record: &mut DeclarativeEnvironmentRecordBinding = self.env_rec.get_mut(name).unwrap(); |
|
|
|
if record.strict { |
|
|
|
if record.strict { |
|
|
|
strict = true |
|
|
|
strict = true |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if record.value.is_none() { |
|
|
|
if record.value.is_none() { |
|
|
|
// TODO: change this when error handling comes into play
|
|
|
|
return Err(ErrorKind::new_reference_error(format!( |
|
|
|
panic!("Reference Error: Cannot set mutable binding for {}", name); |
|
|
|
"{} has not been initialized", |
|
|
|
|
|
|
|
name |
|
|
|
|
|
|
|
))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if record.mutable { |
|
|
|
if record.mutable { |
|
|
|
record.value = Some(value); |
|
|
|
record.value = Some(value); |
|
|
|
} else if strict { |
|
|
|
} else if strict { |
|
|
|
// TODO: change this when error handling comes into play
|
|
|
|
return Err(ErrorKind::new_type_error(format!( |
|
|
|
panic!("TypeError: Cannot mutate an immutable binding {}", name); |
|
|
|
"Cannot mutate an immutable binding {}", |
|
|
|
|
|
|
|
name |
|
|
|
|
|
|
|
))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn get_binding_value(&self, name: &str, _strict: bool) -> Value { |
|
|
|
fn get_binding_value(&self, name: &str, _strict: bool) -> Result<Value, ErrorKind> { |
|
|
|
if let Some(binding) = self.env_rec.get(name) { |
|
|
|
if let Some(binding) = self.env_rec.get(name) { |
|
|
|
binding |
|
|
|
if let Some(ref val) = binding.value { |
|
|
|
.value |
|
|
|
Ok(val.clone()) |
|
|
|
.as_ref() |
|
|
|
} else { |
|
|
|
.expect("Could not get record as reference") |
|
|
|
Err(ErrorKind::new_reference_error(format!( |
|
|
|
.clone() |
|
|
|
"{} is an uninitialized binding", |
|
|
|
|
|
|
|
name |
|
|
|
|
|
|
|
))) |
|
|
|
|
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// TODO: change this when error handling comes into play
|
|
|
|
panic!("Cannot get binding value for {}", name); |
|
|
|
panic!("ReferenceError: Cannot get binding value for {}", name); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -205,7 +226,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord { |
|
|
|
false |
|
|
|
false |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
None => false, |
|
|
|
None => panic!("env_rec has no binding for {}", name), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|