boa/environment/
lexical_environment.rs1use super::global_environment_record::GlobalEnvironmentRecord;
9use crate::{
10 environment::environment_record_trait::EnvironmentRecordTrait, object::JsObject, BoaProfiler,
11 Context, JsResult, JsValue,
12};
13use gc::Gc;
14use std::{collections::VecDeque, error, fmt};
15
16pub type Environment = Gc<Box<dyn EnvironmentRecordTrait>>;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub enum EnvironmentType {
23 Declarative,
24 Function,
25 Global,
26 Object,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub enum VariableScope {
32 Block,
34 Function,
36}
37
38#[derive(Debug, Clone)]
39pub struct LexicalEnvironment {
40 environment_stack: VecDeque<Environment>,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
45pub struct EnvironmentError {
46 details: String,
47}
48
49impl EnvironmentError {
50 pub fn new(msg: &str) -> Self {
51 Self {
52 details: msg.to_string(),
53 }
54 }
55}
56
57impl fmt::Display for EnvironmentError {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 write!(f, "{}", self.details)
60 }
61}
62
63impl error::Error for EnvironmentError {}
64
65impl LexicalEnvironment {
66 pub fn new(global: JsObject) -> Self {
67 let _timer = BoaProfiler::global().start_event("LexicalEnvironment::new", "env");
68 let global_env = GlobalEnvironmentRecord::new(global.clone(), global);
69 let mut lexical_env = Self {
70 environment_stack: VecDeque::new(),
71 };
72
73 lexical_env.environment_stack.push_back(global_env.into());
75 lexical_env
76 }
77}
78
79impl Context {
80 pub(crate) fn push_environment<T: Into<Environment>>(&mut self, env: T) {
81 self.realm
82 .environment
83 .environment_stack
84 .push_back(env.into());
85 }
86
87 pub(crate) fn pop_environment(&mut self) -> Option<Environment> {
88 self.realm.environment.environment_stack.pop_back()
89 }
90
91 pub(crate) fn get_this_binding(&mut self) -> JsResult<JsValue> {
92 self.get_current_environment()
93 .recursive_get_this_binding(self)
94 }
95
96 pub(crate) fn create_mutable_binding(
97 &mut self,
98 name: &str,
99 deletion: bool,
100 scope: VariableScope,
101 ) -> JsResult<()> {
102 self.get_current_environment()
103 .recursive_create_mutable_binding(name, deletion, scope, self)
104 }
105
106 pub(crate) fn create_immutable_binding(
107 &mut self,
108 name: &str,
109 deletion: bool,
110 scope: VariableScope,
111 ) -> JsResult<()> {
112 self.get_current_environment()
113 .recursive_create_immutable_binding(name, deletion, scope, self)
114 }
115
116 pub(crate) fn set_mutable_binding(
117 &mut self,
118 name: &str,
119 value: JsValue,
120 strict: bool,
121 ) -> JsResult<()> {
122 self.get_current_environment()
123 .recursive_set_mutable_binding(name, value, strict, self)
124 }
125
126 pub(crate) fn initialize_binding(&mut self, name: &str, value: JsValue) -> JsResult<()> {
127 self.get_current_environment()
128 .recursive_initialize_binding(name, value, self)
129 }
130
131 pub(crate) fn get_current_environment(&mut self) -> Environment {
134 self.realm
135 .environment
136 .environment_stack
137 .back_mut()
138 .expect("Could not get mutable reference to back object")
139 .clone()
140 }
141
142 pub(crate) fn has_binding(&mut self, name: &str) -> JsResult<bool> {
143 self.get_current_environment()
144 .recursive_has_binding(name, self)
145 }
146
147 pub(crate) fn get_binding_value(&mut self, name: &str) -> JsResult<JsValue> {
148 self.get_current_environment()
149 .recursive_get_binding_value(name, self)
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use crate::exec;
156
157 #[test]
158 fn let_is_blockscoped() {
159 let scenario = r#"
160 {
161 let bar = "bar";
162 }
163
164 try{
165 bar;
166 } catch (err) {
167 err.message
168 }
169 "#;
170
171 assert_eq!(&exec(scenario), "\"bar is not defined\"");
172 }
173
174 #[test]
175 fn const_is_blockscoped() {
176 let scenario = r#"
177 {
178 const bar = "bar";
179 }
180
181 try{
182 bar;
183 } catch (err) {
184 err.message
185 }
186 "#;
187
188 assert_eq!(&exec(scenario), "\"bar is not defined\"");
189 }
190
191 #[test]
192 fn var_not_blockscoped() {
193 let scenario = r#"
194 {
195 var bar = "bar";
196 }
197 bar == "bar";
198 "#;
199
200 assert_eq!(&exec(scenario), "true");
201 }
202
203 #[test]
204 fn functions_use_declaration_scope() {
205 let scenario = r#"
206 function foo() {
207 try {
208 bar;
209 } catch (err) {
210 return err.message;
211 }
212 }
213 {
214 let bar = "bar";
215 foo();
216 }
217 "#;
218
219 assert_eq!(&exec(scenario), "\"bar is not defined\"");
220 }
221
222 #[test]
223 fn set_outer_var_in_blockscope() {
224 let scenario = r#"
225 var bar;
226 {
227 bar = "foo";
228 }
229 bar == "foo";
230 "#;
231
232 assert_eq!(&exec(scenario), "true");
233 }
234
235 #[test]
236 fn set_outer_let_in_blockscope() {
237 let scenario = r#"
238 let bar;
239 {
240 bar = "foo";
241 }
242 bar == "foo";
243 "#;
244
245 assert_eq!(&exec(scenario), "true");
246 }
247}