use crate::environment::declerative_environment_record::DeclerativeEnvironmentRecord;
use crate::environment::environment_record_trait::EnvironmentRecordTrait;
use crate::environment::function_environment_record::{BindingStatus, FunctionEnvironmentRecord};
use crate::environment::global_environment_record::GlobalEnvironmentRecord;
use crate::environment::object_environment_record::ObjectEnvironmentRecord;
use crate::js::value::{Value, ValueData};
use gc::{Gc, GcCell};
use std::collections::hash_map::HashMap;
use std::collections::{HashSet, VecDeque};
use std::debug_assert;
use std::error;
use std::fmt;
pub type Environment = Gc<GcCell<Box<EnvironmentRecordTrait>>>;
#[derive(Debug)]
pub enum EnvironmentType {
Declerative,
Function,
Global,
Object,
}
pub struct LexicalEnvironment {
environment_stack: VecDeque<Environment>,
}
#[derive(Debug, Clone)]
pub struct EnvironmentError {
details: String,
}
impl EnvironmentError {
pub fn new(msg: &str) -> EnvironmentError {
EnvironmentError {
details: msg.to_string(),
}
}
}
impl fmt::Display for EnvironmentError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.details)
}
}
impl error::Error for EnvironmentError {
fn description(&self) -> &str {
&self.details
}
fn cause(&self) -> Option<&error::Error> {
None
}
}
impl LexicalEnvironment {
pub fn new(global: Value) -> LexicalEnvironment {
let global_env = new_global_environment(global.clone(), global.clone());
let mut lexical_env = LexicalEnvironment {
environment_stack: VecDeque::new(),
};
lexical_env.environment_stack.push_back(global_env);
lexical_env
}
pub fn push(&mut self, env: Environment) {
let current_env: Environment = self.get_current_environment().clone();
env.borrow_mut().set_outer_environment(current_env);
self.environment_stack.push_back(env);
}
pub fn pop(&mut self) {
self.environment_stack.pop_back();
}
pub fn get_global_object(&self) -> Option<Value> {
let global = &self.environment_stack[0];
global.borrow().get_global_object()
}
pub fn create_mutable_binding(&mut self, name: String, deletion: bool) {
self.get_current_environment()
.borrow_mut()
.create_mutable_binding(name, deletion)
}
pub fn create_immutable_binding(&mut self, name: String, deletion: bool) {
self.get_current_environment()
.borrow_mut()
.create_immutable_binding(name, deletion)
}
pub fn set_mutable_binding(&mut self, name: String, value: Value, strict: bool) {
let env = self.get_current_environment();
env.borrow_mut().set_mutable_binding(name, value, strict);
}
pub fn initialize_binding(&mut self, name: String, value: Value) {
let env = self.get_current_environment();
env.borrow_mut().initialize_binding(name, value);
}
pub fn get_current_environment_ref(&self) -> &Environment {
&self
.environment_stack
.get(self.environment_stack.len() - 1)
.unwrap()
}
pub fn get_current_environment(&mut self) -> &mut Environment {
self.environment_stack.back_mut().unwrap()
}
pub fn get_binding_value(&mut self, name: String) -> Value {
let env: Environment = self.get_current_environment().clone();
let borrowed_env = env.borrow();
let result = borrowed_env.has_binding(&name);
if result {
return borrowed_env.get_binding_value(name, false);
}
if borrowed_env.get_outer_environment().is_some() {
let mut outer: Option<Environment> = borrowed_env.get_outer_environment();
while outer.is_some() {
if outer.as_ref().unwrap().borrow().has_binding(&name) {
return outer.unwrap().borrow().get_binding_value(name, false);
}
outer = outer.unwrap().borrow().get_outer_environment();
}
}
Gc::new(ValueData::Undefined)
}
}
pub fn new_declerative_environment(env: Option<Environment>) -> Environment {
let boxed_env = Box::new(DeclerativeEnvironmentRecord {
env_rec: HashMap::new(),
outer_env: env,
});
Gc::new(GcCell::new(boxed_env))
}
pub fn new_function_environment(
f: Value,
new_target: Value,
outer: Option<Environment>,
) -> Environment {
debug_assert!(f.is_function());
debug_assert!(new_target.is_object() || new_target.is_undefined());
Gc::new(GcCell::new(Box::new(FunctionEnvironmentRecord {
env_rec: HashMap::new(),
function_object: f.clone(),
this_binding_status: BindingStatus::Uninitialized, home_object: Gc::new(ValueData::Undefined),
new_target: new_target,
outer_env: outer, this_value: Gc::new(ValueData::Undefined), })))
}
pub fn new_object_environment(object: Value, environment: Option<Environment>) -> Environment {
Gc::new(GcCell::new(Box::new(ObjectEnvironmentRecord {
bindings: object,
outer_env: environment,
with_environment: false,
})))
}
pub fn new_global_environment(global: Value, this_value: Value) -> Environment {
let obj_rec = Box::new(ObjectEnvironmentRecord {
bindings: global,
outer_env: None,
with_environment: false,
});
let dcl_rec = Box::new(DeclerativeEnvironmentRecord {
env_rec: HashMap::new(),
outer_env: None,
});
Gc::new(GcCell::new(Box::new(GlobalEnvironmentRecord {
object_record: obj_rec,
global_this_binding: this_value,
declerative_record: dcl_rec,
var_names: HashSet::new(),
})))
}