|
|
@ -8,12 +8,12 @@ |
|
|
|
//!
|
|
|
|
//!
|
|
|
|
//! There are 5 Environment record kinds. They all have methods in common, these are implemented as a the `EnvironmentRecordTrait`
|
|
|
|
//! 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::VariableScope, object::GcObject}; |
|
|
|
use crate::{ |
|
|
|
use crate::{ |
|
|
|
environment::lexical_environment::{Environment, EnvironmentType}, |
|
|
|
environment::lexical_environment::{Environment, EnvironmentType}, |
|
|
|
gc::{Finalize, Trace}, |
|
|
|
gc::{Finalize, Trace}, |
|
|
|
Value, |
|
|
|
Context, Result, Value, |
|
|
|
}; |
|
|
|
}; |
|
|
|
use std::fmt::Debug; |
|
|
|
use std::fmt::Debug; |
|
|
|
|
|
|
|
|
|
|
@ -22,7 +22,8 @@ use std::fmt::Debug; |
|
|
|
/// In the ECMAScript specification Environment Records are hierachical and have a base class with abstract methods.
|
|
|
|
/// In the ECMAScript specification Environment Records are hierachical and have a base class with abstract methods.
|
|
|
|
/// In this implementation we have a trait which represents the behaviour of all `EnvironmentRecord` types.
|
|
|
|
/// In this implementation we have a trait which represents the behaviour of all `EnvironmentRecord` types.
|
|
|
|
pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { |
|
|
|
pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { |
|
|
|
/// Determine if an Environment Record has a binding for the String value N. Return true if it does and false if it does not.
|
|
|
|
/// Determine if an Environment Record has a binding for the String value N.
|
|
|
|
|
|
|
|
/// Return true if it does and false if it does not.
|
|
|
|
fn has_binding(&self, name: &str) -> bool; |
|
|
|
fn has_binding(&self, name: &str) -> bool; |
|
|
|
|
|
|
|
|
|
|
|
/// Create a new but uninitialized mutable binding in an Environment Record. The String value N is the text of the bound name.
|
|
|
|
/// Create a new but uninitialized mutable binding in an Environment Record. The String value N is the text of the bound name.
|
|
|
@ -37,18 +38,25 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { |
|
|
|
name: String, |
|
|
|
name: String, |
|
|
|
deletion: bool, |
|
|
|
deletion: bool, |
|
|
|
allow_name_reuse: bool, |
|
|
|
allow_name_reuse: bool, |
|
|
|
) -> Result<(), ErrorKind>; |
|
|
|
context: &mut Context, |
|
|
|
|
|
|
|
) -> Result<()>; |
|
|
|
|
|
|
|
|
|
|
|
/// Create a new but uninitialized immutable binding in an Environment Record.
|
|
|
|
/// Create a new but uninitialized immutable binding in an Environment Record.
|
|
|
|
/// The String value N is the text of the bound name.
|
|
|
|
/// The String value N is the text of the bound name.
|
|
|
|
/// If strict is true then attempts to set it after it has been initialized will always throw an exception,
|
|
|
|
/// If strict is true then attempts to set it after it has been initialized will always throw an exception,
|
|
|
|
/// regardless of the strict mode setting of operations that reference that binding.
|
|
|
|
/// regardless of the strict mode setting of operations that reference that binding.
|
|
|
|
fn create_immutable_binding(&mut self, name: String, strict: bool) -> Result<(), ErrorKind>; |
|
|
|
fn create_immutable_binding( |
|
|
|
|
|
|
|
&mut self, |
|
|
|
|
|
|
|
name: String, |
|
|
|
|
|
|
|
strict: bool, |
|
|
|
|
|
|
|
context: &mut Context, |
|
|
|
|
|
|
|
) -> Result<()>; |
|
|
|
|
|
|
|
|
|
|
|
/// Set the value of an already existing but uninitialized binding in an Environment Record.
|
|
|
|
/// Set the value of an already existing but uninitialized binding in an Environment Record.
|
|
|
|
/// The String value N is the text of the bound name.
|
|
|
|
/// The String value N is the text of the bound name.
|
|
|
|
/// V is the value for the binding and is a value of any ECMAScript language type.
|
|
|
|
/// V is the value for the binding and is a value of any ECMAScript language type.
|
|
|
|
fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind>; |
|
|
|
fn initialize_binding(&mut self, name: &str, value: Value, context: &mut Context) |
|
|
|
|
|
|
|
-> Result<()>; |
|
|
|
|
|
|
|
|
|
|
|
/// Set the value of an already existing mutable binding in an Environment Record.
|
|
|
|
/// Set the value of an already existing mutable binding in an Environment Record.
|
|
|
|
/// The String value `name` is the text of the bound name.
|
|
|
|
/// The String value `name` is the text of the bound name.
|
|
|
@ -59,13 +67,14 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { |
|
|
|
name: &str, |
|
|
|
name: &str, |
|
|
|
value: Value, |
|
|
|
value: Value, |
|
|
|
strict: bool, |
|
|
|
strict: bool, |
|
|
|
) -> Result<(), ErrorKind>; |
|
|
|
context: &mut Context, |
|
|
|
|
|
|
|
) -> Result<()>; |
|
|
|
|
|
|
|
|
|
|
|
/// Returns the value of an already existing binding from an Environment Record.
|
|
|
|
/// Returns the value of an already existing binding from an Environment Record.
|
|
|
|
/// The String value N is the text of the bound name.
|
|
|
|
/// The String value N is the text of the bound name.
|
|
|
|
/// S is used to identify references originating in strict mode code or that
|
|
|
|
/// S is used to identify references originating in strict mode code or that
|
|
|
|
/// otherwise require strict mode reference semantics.
|
|
|
|
/// otherwise require strict mode reference semantics.
|
|
|
|
fn get_binding_value(&self, name: &str, strict: bool) -> Result<Value, ErrorKind>; |
|
|
|
fn get_binding_value(&self, name: &str, strict: bool, context: &mut Context) -> Result<Value>; |
|
|
|
|
|
|
|
|
|
|
|
/// Delete a binding from an Environment Record.
|
|
|
|
/// Delete a binding from an Environment Record.
|
|
|
|
/// The String value name is the text of the bound name.
|
|
|
|
/// The String value name is the text of the bound name.
|
|
|
@ -78,15 +87,15 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { |
|
|
|
fn has_this_binding(&self) -> bool; |
|
|
|
fn has_this_binding(&self) -> bool; |
|
|
|
|
|
|
|
|
|
|
|
/// Return the `this` binding from the environment
|
|
|
|
/// Return the `this` binding from the environment
|
|
|
|
fn get_this_binding(&self) -> Result<Value, ErrorKind>; |
|
|
|
fn get_this_binding(&self, context: &mut Context) -> Result<Value>; |
|
|
|
|
|
|
|
|
|
|
|
/// Determine if an Environment Record establishes a super method binding.
|
|
|
|
/// Determine if an Environment Record establishes a super method binding.
|
|
|
|
/// Return true if it does and false if it does not.
|
|
|
|
/// Return true if it does and false if it does not.
|
|
|
|
fn has_super_binding(&self) -> bool; |
|
|
|
fn has_super_binding(&self) -> bool; |
|
|
|
|
|
|
|
|
|
|
|
/// If this Environment Record is associated with a with statement, return the with object.
|
|
|
|
/// If this Environment Record is associated with a with statement, return the with object.
|
|
|
|
/// Otherwise, return undefined.
|
|
|
|
/// Otherwise, return None.
|
|
|
|
fn with_base_object(&self) -> Value; |
|
|
|
fn with_base_object(&self) -> Option<GcObject>; |
|
|
|
|
|
|
|
|
|
|
|
/// Get the next environment up
|
|
|
|
/// Get the next environment up
|
|
|
|
fn get_outer_environment_ref(&self) -> Option<&Environment>; |
|
|
|
fn get_outer_environment_ref(&self) -> Option<&Environment>; |
|
|
@ -100,16 +109,13 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { |
|
|
|
/// Get the type of environment this is
|
|
|
|
/// Get the type of environment this is
|
|
|
|
fn get_environment_type(&self) -> EnvironmentType; |
|
|
|
fn get_environment_type(&self) -> EnvironmentType; |
|
|
|
|
|
|
|
|
|
|
|
/// Fetch global variable
|
|
|
|
|
|
|
|
fn get_global_object(&self) -> Option<Value>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Return the `this` binding from the environment or try to get it from outer environments
|
|
|
|
/// Return the `this` binding from the environment or try to get it from outer environments
|
|
|
|
fn recursive_get_this_binding(&self) -> Result<Value, ErrorKind> { |
|
|
|
fn recursive_get_this_binding(&self, context: &mut Context) -> Result<Value> { |
|
|
|
if self.has_this_binding() { |
|
|
|
if self.has_this_binding() { |
|
|
|
self.get_this_binding() |
|
|
|
self.get_this_binding(context) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
match self.get_outer_environment_ref() { |
|
|
|
match self.get_outer_environment_ref() { |
|
|
|
Some(outer) => outer.borrow().recursive_get_this_binding(), |
|
|
|
Some(outer) => outer.borrow().recursive_get_this_binding(context), |
|
|
|
None => Ok(Value::Undefined), |
|
|
|
None => Ok(Value::Undefined), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -121,14 +127,15 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { |
|
|
|
name: String, |
|
|
|
name: String, |
|
|
|
deletion: bool, |
|
|
|
deletion: bool, |
|
|
|
scope: VariableScope, |
|
|
|
scope: VariableScope, |
|
|
|
) -> Result<(), ErrorKind> { |
|
|
|
context: &mut Context, |
|
|
|
|
|
|
|
) -> Result<()> { |
|
|
|
match scope { |
|
|
|
match scope { |
|
|
|
VariableScope::Block => self.create_mutable_binding(name, deletion, false), |
|
|
|
VariableScope::Block => self.create_mutable_binding(name, deletion, false, context), |
|
|
|
VariableScope::Function => self |
|
|
|
VariableScope::Function => self |
|
|
|
.get_outer_environment_ref() |
|
|
|
.get_outer_environment_ref() |
|
|
|
.expect("No function or global environment") |
|
|
|
.expect("No function or global environment") |
|
|
|
.borrow_mut() |
|
|
|
.borrow_mut() |
|
|
|
.recursive_create_mutable_binding(name, deletion, scope), |
|
|
|
.recursive_create_mutable_binding(name, deletion, scope, context), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -138,14 +145,15 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { |
|
|
|
name: String, |
|
|
|
name: String, |
|
|
|
deletion: bool, |
|
|
|
deletion: bool, |
|
|
|
scope: VariableScope, |
|
|
|
scope: VariableScope, |
|
|
|
) -> Result<(), ErrorKind> { |
|
|
|
context: &mut Context, |
|
|
|
|
|
|
|
) -> Result<()> { |
|
|
|
match scope { |
|
|
|
match scope { |
|
|
|
VariableScope::Block => self.create_immutable_binding(name, deletion), |
|
|
|
VariableScope::Block => self.create_immutable_binding(name, deletion, context), |
|
|
|
VariableScope::Function => self |
|
|
|
VariableScope::Function => self |
|
|
|
.get_outer_environment_ref() |
|
|
|
.get_outer_environment_ref() |
|
|
|
.expect("No function or global environment") |
|
|
|
.expect("No function or global environment") |
|
|
|
.borrow_mut() |
|
|
|
.borrow_mut() |
|
|
|
.recursive_create_immutable_binding(name, deletion, scope), |
|
|
|
.recursive_create_immutable_binding(name, deletion, scope, context), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -155,26 +163,32 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { |
|
|
|
name: &str, |
|
|
|
name: &str, |
|
|
|
value: Value, |
|
|
|
value: Value, |
|
|
|
strict: bool, |
|
|
|
strict: bool, |
|
|
|
) -> Result<(), ErrorKind> { |
|
|
|
context: &mut Context, |
|
|
|
|
|
|
|
) -> Result<()> { |
|
|
|
if self.has_binding(name) { |
|
|
|
if self.has_binding(name) { |
|
|
|
self.set_mutable_binding(name, value, strict) |
|
|
|
self.set_mutable_binding(name, value, strict, context) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
self.get_outer_environment_ref() |
|
|
|
self.get_outer_environment_ref() |
|
|
|
.expect("Environment stack underflow") |
|
|
|
.expect("Environment stack underflow") |
|
|
|
.borrow_mut() |
|
|
|
.borrow_mut() |
|
|
|
.recursive_set_mutable_binding(name, value, strict) |
|
|
|
.recursive_set_mutable_binding(name, value, strict, context) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Initialize binding while handling outer environments
|
|
|
|
/// Initialize binding while handling outer environments
|
|
|
|
fn recursive_initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> { |
|
|
|
fn recursive_initialize_binding( |
|
|
|
|
|
|
|
&mut self, |
|
|
|
|
|
|
|
name: &str, |
|
|
|
|
|
|
|
value: Value, |
|
|
|
|
|
|
|
context: &mut Context, |
|
|
|
|
|
|
|
) -> Result<()> { |
|
|
|
if self.has_binding(name) { |
|
|
|
if self.has_binding(name) { |
|
|
|
self.initialize_binding(name, value) |
|
|
|
self.initialize_binding(name, value, context) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
self.get_outer_environment_ref() |
|
|
|
self.get_outer_environment_ref() |
|
|
|
.expect("Environment stack underflow") |
|
|
|
.expect("Environment stack underflow") |
|
|
|
.borrow_mut() |
|
|
|
.borrow_mut() |
|
|
|
.recursive_initialize_binding(name, value) |
|
|
|
.recursive_initialize_binding(name, value, context) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -188,16 +202,13 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Retrieve binding from current or any outer environment
|
|
|
|
/// Retrieve binding from current or any outer environment
|
|
|
|
fn recursive_get_binding_value(&self, name: &str) -> Result<Value, ErrorKind> { |
|
|
|
fn recursive_get_binding_value(&self, name: &str, context: &mut Context) -> Result<Value> { |
|
|
|
if self.has_binding(name) { |
|
|
|
if self.has_binding(name) { |
|
|
|
self.get_binding_value(name, false) |
|
|
|
self.get_binding_value(name, false, context) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
match self.get_outer_environment_ref() { |
|
|
|
match self.get_outer_environment_ref() { |
|
|
|
Some(outer) => outer.borrow().recursive_get_binding_value(name), |
|
|
|
Some(outer) => outer.borrow().recursive_get_binding_value(name, context), |
|
|
|
None => Err(ErrorKind::new_reference_error(format!( |
|
|
|
None => context.throw_reference_error(format!("{} is not defined", name)), |
|
|
|
"{} is not defined", |
|
|
|
|
|
|
|
name |
|
|
|
|
|
|
|
))), |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|