1use std::collections::HashMap;
2
3use crate::runner::ds::error::JErrorType;
4use crate::runner::ds::execution_context::ExecutionContextStack;
5use crate::runner::ds::lex_env::{JsLexEnvironmentType, LexEnvironment};
6use crate::runner::ds::object::JsObjectType;
7use crate::runner::ds::object_property::{
8 PropertyDescriptor, PropertyDescriptorData, PropertyDescriptorSetter, PropertyKey,
9};
10use crate::runner::ds::operations::object::{define_property_or_throw, get, has_own_property, set};
11use crate::runner::ds::value::JsValue;
12use std::cell::RefCell;
13use std::rc::Rc;
14
15pub trait EnvironmentRecord {
16 fn has_binding(&self, name: &String) -> bool;
17 fn create_mutable_binding(&mut self, name: String, can_delete: bool) -> Result<(), JErrorType>;
18 fn create_immutable_binding(&mut self, name: String) -> Result<(), JErrorType>;
19 fn initialize_binding(
20 &mut self,
21 ctx_stack: &mut ExecutionContextStack,
22 name: String,
23 value: JsValue,
24 ) -> Result<bool, JErrorType>;
25 fn set_mutable_binding(
26 &mut self,
27 ctx_stack: &mut ExecutionContextStack,
28 name: String,
29 value: JsValue,
30 ) -> Result<(), JErrorType>;
31 fn get_binding_value(
32 &self,
33 ctx_stack: &mut ExecutionContextStack,
34 name: &String,
35 ) -> Result<JsValue, JErrorType>;
36 fn delete_binding(&mut self, name: &String) -> Result<bool, JErrorType>;
37 fn has_this_binding(&self) -> bool;
38 fn has_super_binding(&self) -> bool;
39 fn get_all_bindings(&self) -> Option<Vec<(String, JsValue)>> {
41 None }
43}
44
45pub enum EnvironmentRecordType {
46 Declarative(DeclarativeEnvironmentRecord),
47 Object(ObjectEnvironmentRecord),
48 Function(FunctionEnvironmentRecord),
49 Global(GlobalEnvironmentRecord),
50}
51impl EnvironmentRecordType {
52 pub fn as_env_record(&self) -> &dyn EnvironmentRecord {
53 match self {
54 EnvironmentRecordType::Declarative(d) => d,
55 EnvironmentRecordType::Object(d) => d,
56 EnvironmentRecordType::Function(d) => d,
57 EnvironmentRecordType::Global(d) => d,
58 }
59 }
60}
61
62#[derive(PartialEq)]
63pub enum BindingFlag {
64 NoDelete,
65 IsImmutable,
66}
67
68pub struct DeclarativeEnvironmentRecord {
69 bindings: HashMap<String, Option<JsValue>>,
70 binding_flags: HashMap<String, Vec<BindingFlag>>,
71}
72impl DeclarativeEnvironmentRecord {
73 pub fn new() -> Self {
74 DeclarativeEnvironmentRecord {
75 bindings: HashMap::new(),
76 binding_flags: HashMap::new(),
77 }
78 }
79}
80impl EnvironmentRecord for DeclarativeEnvironmentRecord {
81 fn has_binding(&self, id: &String) -> bool {
82 self.bindings.contains_key(id)
83 }
84
85 fn create_mutable_binding(&mut self, id: String, can_delete: bool) -> Result<(), JErrorType> {
86 if !self.has_binding(&id) {
87 self.bindings.insert(id.to_string(), None);
88 if can_delete {
89 self.binding_flags.insert(id, vec![]);
90 } else {
91 self.binding_flags.insert(id, vec![BindingFlag::NoDelete]);
92 }
93 }
94 Ok(())
95 }
96
97 fn create_immutable_binding(&mut self, id: String) -> Result<(), JErrorType> {
98 if !self.has_binding(&id) {
99 self.bindings.insert(id.to_string(), None);
100 self.binding_flags
101 .insert(id, vec![BindingFlag::IsImmutable]);
102 }
103 Ok(())
104 }
105
106 fn initialize_binding(
107 &mut self,
108 _ctx_stack: &mut ExecutionContextStack,
109 id: String,
110 value: JsValue,
111 ) -> Result<bool, JErrorType> {
112 if let Some(v) = self.bindings.get(&id) {
113 if v.is_none() {
114 self.bindings.insert(id, Some(value));
115 Ok(true)
116 } else {
117 Ok(false)
118 }
119 } else {
120 Ok(false)
121 }
122 }
123
124 fn set_mutable_binding(
125 &mut self,
126 _ctx_stack: &mut ExecutionContextStack,
127 id: String,
128 value: JsValue,
129 ) -> Result<(), JErrorType> {
130 if let Some(v) = self.bindings.get(&id) {
131 if !v.is_none() {
132 if !self
133 .binding_flags
134 .get(&id)
135 .unwrap()
136 .contains(&BindingFlag::IsImmutable)
137 {
138 self.bindings.insert(id, Some(value));
139 Ok(())
140 } else {
141 Err(JErrorType::TypeError(format!(
142 "'{}' is set and immutable",
143 id
144 )))
145 }
146 } else {
147 Err(JErrorType::ReferenceError(format!(
148 "'{}' is not initialized",
149 id
150 )))
151 }
152 } else {
153 Err(JErrorType::ReferenceError(format!(
154 "'{}' is not defined",
155 id
156 )))
157 }
158 }
159
160 fn get_binding_value(
161 &self,
162 _ctx_stack: &mut ExecutionContextStack,
163 id: &String,
164 ) -> Result<JsValue, JErrorType> {
165 match self.bindings.get(id) {
166 None => Err(JErrorType::ReferenceError(format!(
167 "'{}' is not defined",
168 id
169 ))),
170 Some(v) => match v {
171 None => Err(JErrorType::ReferenceError(format!(
172 "'{}' is not initialized",
173 id
174 ))),
175 Some(v) => Ok(v.clone()),
176 },
177 }
178 }
179
180 fn delete_binding(&mut self, id: &String) -> Result<bool, JErrorType> {
181 Ok(if let Some(flags) = self.binding_flags.get(id) {
182 if flags.contains(&BindingFlag::NoDelete) {
183 false
184 } else {
185 self.bindings.remove(id);
186 self.binding_flags.remove(id);
187 true
188 }
189 } else {
190 false
191 })
192 }
193
194 fn has_this_binding(&self) -> bool {
195 false
196 }
197
198 fn has_super_binding(&self) -> bool {
199 false
200 }
201
202 fn get_all_bindings(&self) -> Option<Vec<(String, JsValue)>> {
203 let mut result = Vec::new();
204 for (name, value) in &self.bindings {
205 if let Some(v) = value {
206 result.push((name.clone(), v.clone()));
207 }
208 }
209 Some(result)
210 }
211}
212
213pub struct ObjectEnvironmentRecord {
214 binding_object: JsObjectType,
215}
216impl ObjectEnvironmentRecord {
217 pub fn new(o: JsObjectType) -> Self {
218 ObjectEnvironmentRecord { binding_object: o }
219 }
220}
221impl EnvironmentRecord for ObjectEnvironmentRecord {
222 fn has_binding(&self, name: &String) -> bool {
223 self.binding_object
224 .borrow()
225 .as_js_object()
226 .has_property(&PropertyKey::Str(name.to_string()))
227 }
228
229 fn create_mutable_binding(&mut self, name: String, can_delete: bool) -> Result<(), JErrorType> {
230 define_property_or_throw(
231 (*self.binding_object).borrow_mut().as_js_object_mut(),
232 PropertyKey::Str(name),
233 PropertyDescriptorSetter::new_from_property_descriptor(PropertyDescriptor::Data(
234 PropertyDescriptorData {
235 value: JsValue::Undefined,
236 writable: true,
237 enumerable: true,
238 configurable: can_delete,
239 },
240 )),
241 )
242 }
243
244 fn create_immutable_binding(&mut self, _name: String) -> Result<(), JErrorType> {
245 panic!("Not supported")
246 }
247
248 fn initialize_binding(
249 &mut self,
250 ctx_stack: &mut ExecutionContextStack,
251 name: String,
252 value: JsValue,
253 ) -> Result<bool, JErrorType> {
254 self.set_mutable_binding(ctx_stack, name, value)?;
255 Ok(true)
256 }
257
258 fn set_mutable_binding(
259 &mut self,
260 ctx_stack: &mut ExecutionContextStack,
261 name: String,
262 value: JsValue,
263 ) -> Result<(), JErrorType> {
264 set(
265 ctx_stack,
266 &mut self.binding_object,
267 PropertyKey::Str(name),
268 value,
269 )?;
270 Ok(())
271 }
272
273 fn get_binding_value(
274 &self,
275 ctx_stack: &mut ExecutionContextStack,
276 name: &String,
277 ) -> Result<JsValue, JErrorType> {
278 let p = PropertyKey::Str(name.to_string());
279 if self.binding_object.borrow().as_js_object().has_property(&p) {
280 get(ctx_stack, &self.binding_object, &p)
281 } else {
282 Err(JErrorType::ReferenceError(format!(
283 "'{}' reference is undefined",
284 name
285 )))
286 }
287 }
288
289 fn delete_binding(&mut self, name: &String) -> Result<bool, JErrorType> {
290 (*self.binding_object)
291 .borrow_mut()
292 .as_js_object_mut()
293 .delete(&PropertyKey::Str(name.to_string()))
294 }
295
296 fn has_this_binding(&self) -> bool {
297 false
298 }
299
300 fn has_super_binding(&self) -> bool {
301 false
302 }
303}
304
305pub struct FunctionEnvironmentRecord {
306 base_env: DeclarativeEnvironmentRecord,
307 this_value: Option<JsValue>,
308 is_lexical_binding: bool,
309 _function_object: JsObjectType,
310 home_object: Option<JsObjectType>,
311 _new_target: Option<JsObjectType>,
312}
313impl FunctionEnvironmentRecord {
314 pub fn new(f: JsObjectType, new_target: Option<JsObjectType>) -> Self {
315 let (is_lexical, home_object) = {
317 let func = (*f).borrow();
318 let func_obj = func.as_js_function_object();
319 let base = func_obj.get_function_object_base();
320 (
321 base.is_lexical,
322 base.home_object.as_ref().map(|ho| ho.clone()),
323 )
324 };
325 FunctionEnvironmentRecord {
326 base_env: DeclarativeEnvironmentRecord::new(),
327 this_value: None,
328 is_lexical_binding: is_lexical,
329 _function_object: f,
330 home_object,
331 _new_target: new_target,
332 }
333 }
334
335 pub fn bind_this_value(&mut self, this: JsValue) -> Result<bool, JErrorType> {
336 if !self.is_lexical_binding {
337 if let Some(_) = &self.this_value {
338 Err(JErrorType::ReferenceError(
339 "'this' is already initialized".to_string(),
340 ))
341 } else {
342 self.this_value = Some(this);
343 Ok(true)
344 }
345 } else {
346 Err(JErrorType::TypeError(
347 "Cannot set 'this' of Arrow Function".to_string(),
348 ))
349 }
350 }
351
352 pub fn get_this_binding(&self) -> Result<&JsValue, JErrorType> {
353 if self.is_lexical_binding {
354 Err(JErrorType::TypeError(
355 "Cannot get 'this' of Arrow Function".to_string(),
356 ))
357 } else {
358 if let Some(this) = &self.this_value {
359 Ok(this)
360 } else {
361 Err(JErrorType::ReferenceError(
362 "'this' is not initialized".to_string(),
363 ))
364 }
365 }
366 }
367
368 pub fn get_super_base(&self) -> Option<JsObjectType> {
369 if let Some(ho) = &self.home_object {
370 ho.borrow().as_js_object().get_prototype_of()
371 } else {
372 None
373 }
374 }
375}
376impl EnvironmentRecord for FunctionEnvironmentRecord {
377 fn has_binding(&self, name: &String) -> bool {
378 self.base_env.has_binding(name)
379 }
380
381 fn create_mutable_binding(&mut self, name: String, can_delete: bool) -> Result<(), JErrorType> {
382 self.base_env.create_mutable_binding(name, can_delete)
383 }
384
385 fn create_immutable_binding(&mut self, name: String) -> Result<(), JErrorType> {
386 self.base_env.create_immutable_binding(name)
387 }
388
389 fn initialize_binding(
390 &mut self,
391 ctx_stack: &mut ExecutionContextStack,
392 name: String,
393 value: JsValue,
394 ) -> Result<bool, JErrorType> {
395 self.base_env.initialize_binding(ctx_stack, name, value)
396 }
397
398 fn set_mutable_binding(
399 &mut self,
400 ctx_stack: &mut ExecutionContextStack,
401 name: String,
402 value: JsValue,
403 ) -> Result<(), JErrorType> {
404 self.base_env.set_mutable_binding(ctx_stack, name, value)
405 }
406
407 fn get_binding_value(
408 &self,
409 ctx_stack: &mut ExecutionContextStack,
410 name: &String,
411 ) -> Result<JsValue, JErrorType> {
412 self.base_env.get_binding_value(ctx_stack, name)
413 }
414
415 fn delete_binding(&mut self, name: &String) -> Result<bool, JErrorType> {
416 self.base_env.delete_binding(name)
417 }
418
419 fn has_this_binding(&self) -> bool {
420 !self.is_lexical_binding
421 }
422
423 fn has_super_binding(&self) -> bool {
424 if self.is_lexical_binding {
425 false
426 } else {
427 self.home_object.is_some()
428 }
429 }
430}
431
432pub struct GlobalEnvironmentRecord {
433 object_record: ObjectEnvironmentRecord,
434 declarative_record: DeclarativeEnvironmentRecord,
435 var_names: Vec<String>,
436}
437impl GlobalEnvironmentRecord {
438 pub fn new(global_object: JsObjectType) -> Self {
439 GlobalEnvironmentRecord {
440 object_record: ObjectEnvironmentRecord::new(global_object),
441 declarative_record: DeclarativeEnvironmentRecord::new(),
442 var_names: Vec::new(),
443 }
444 }
445
446 pub fn get_this_binding(&self) -> &JsObjectType {
447 &self.object_record.binding_object
448 }
449
450 pub fn has_var_declaration(&self, name: &String) -> bool {
451 self.var_names.contains(name)
452 }
453
454 pub fn has_lexical_declaration(&self, name: &String) -> bool {
455 self.declarative_record.has_binding(name)
456 }
457
458 pub fn has_restricted_global_property(&self, name: &String) -> Result<bool, JErrorType> {
459 Ok(
460 if let Some(desc) = self
461 .object_record
462 .binding_object
463 .borrow()
464 .as_js_object()
465 .get_own_property(&PropertyKey::Str(name.to_string()))?
466 {
467 !desc.is_configurable()
468 } else {
469 false
470 },
471 )
472 }
473
474 pub fn can_declare_global_var(&self, name: &String) -> Result<bool, JErrorType> {
475 Ok(
476 if has_own_property(
477 &self.object_record.binding_object,
478 &PropertyKey::Str(name.to_string()),
479 )? {
480 true
481 } else {
482 self.object_record
483 .binding_object
484 .borrow()
485 .as_js_object()
486 .is_extensible()
487 },
488 )
489 }
490
491 pub fn can_declare_global_function(&self, name: &String) -> Result<bool, JErrorType> {
492 Ok(
493 if let Some(desc) = self
494 .object_record
495 .binding_object
496 .borrow()
497 .as_js_object()
498 .get_own_property(&PropertyKey::Str(name.to_string()))?
499 {
500 if desc.is_configurable() {
501 true
502 } else if desc.is_data_descriptor() && desc.is_enumerable() {
503 if let PropertyDescriptor::Data(PropertyDescriptorData { writable, .. }) = desc
504 {
505 if *writable {
506 return Ok(true);
507 }
508 }
509 false
510 } else {
511 false
512 }
513 } else {
514 self.object_record
515 .binding_object
516 .borrow()
517 .as_js_object()
518 .is_extensible()
519 },
520 )
521 }
522
523 pub fn create_global_var_binding(
524 &mut self,
525 ctx_stack: &mut ExecutionContextStack,
526 name: String,
527 can_delete: bool,
528 ) -> Result<(), JErrorType> {
529 let has_property = has_own_property(
530 &self.object_record.binding_object,
531 &PropertyKey::Str(name.to_string()),
532 )?;
533 let is_extensible = self
534 .object_record
535 .binding_object
536 .borrow()
537 .as_js_object()
538 .is_extensible();
539 if !has_property && is_extensible {
540 self.object_record
541 .create_mutable_binding(name.to_string(), can_delete)?;
542 self.object_record.initialize_binding(
543 ctx_stack,
544 name.to_string(),
545 JsValue::Undefined,
546 )?;
547 }
548 if !self.var_names.contains(&name) {
549 self.var_names.push(name);
550 }
551 Ok(())
552 }
553
554 pub fn create_global_function_binding(
555 &mut self,
556 name: String,
557 f: JsValue,
558 can_delete: bool,
559 ) -> Result<(), JErrorType> {
560 let set_new_desc = if let Some(desc) = self
561 .object_record
562 .binding_object
563 .borrow()
564 .as_js_object()
565 .get_own_property(&PropertyKey::Str(name.to_string()))?
566 {
567 desc.is_configurable()
568 } else {
569 true
570 };
571 let new_desc = if set_new_desc {
573 PropertyDescriptorSetter::new_from_property_descriptor(PropertyDescriptor::Data(
574 PropertyDescriptorData {
575 value: f,
576 writable: true,
577 enumerable: true,
578 configurable: can_delete,
579 },
580 ))
581 } else {
582 PropertyDescriptorSetter {
583 honour_value: true,
584 honour_writable: false,
585 honour_set: false,
586 honour_get: false,
587 honour_enumerable: false,
588 honour_configurable: false,
589 descriptor: PropertyDescriptor::Data(PropertyDescriptorData {
590 value: f,
591 writable: false,
592 enumerable: false,
593 configurable: false,
594 }),
595 }
596 };
597 define_property_or_throw(
598 (*self.object_record.binding_object)
599 .borrow_mut()
600 .as_js_object_mut(),
601 PropertyKey::Str(name.to_string()),
602 new_desc,
603 )?;
604 if !self.var_names.contains(&name) {
611 self.var_names.push(name);
612 }
613 Ok(())
614 }
615}
616impl EnvironmentRecord for GlobalEnvironmentRecord {
617 fn has_binding(&self, name: &String) -> bool {
618 if self.declarative_record.has_binding(name) {
619 true
620 } else {
621 self.object_record.has_binding(name)
622 }
623 }
624
625 fn create_mutable_binding(&mut self, name: String, can_delete: bool) -> Result<(), JErrorType> {
626 if self.declarative_record.has_binding(&name) {
627 Err(JErrorType::TypeError(format!(
628 "'{}' binding is already present",
629 name
630 )))
631 } else {
632 self.declarative_record
633 .create_mutable_binding(name, can_delete)
634 }
635 }
636
637 fn create_immutable_binding(&mut self, name: String) -> Result<(), JErrorType> {
638 if self.declarative_record.has_binding(&name) {
639 Err(JErrorType::TypeError(format!(
640 "'{}' binding is already present",
641 name
642 )))
643 } else {
644 self.declarative_record.create_immutable_binding(name)
645 }
646 }
647
648 fn initialize_binding(
649 &mut self,
650 ctx_stack: &mut ExecutionContextStack,
651 name: String,
652 value: JsValue,
653 ) -> Result<bool, JErrorType> {
654 if self.declarative_record.has_binding(&name) {
655 self.declarative_record
656 .initialize_binding(ctx_stack, name, value)
657 } else if self.object_record.has_binding(&name) {
658 self.object_record
659 .initialize_binding(ctx_stack, name, value)
660 } else {
661 Ok(false)
662 }
663 }
664
665 fn set_mutable_binding(
666 &mut self,
667 ctx_stack: &mut ExecutionContextStack,
668 name: String,
669 value: JsValue,
670 ) -> Result<(), JErrorType> {
671 if self.declarative_record.has_binding(&name) {
672 self.declarative_record
673 .set_mutable_binding(ctx_stack, name, value)
674 } else if self.object_record.has_binding(&name) {
675 self.object_record
676 .set_mutable_binding(ctx_stack, name, value)
677 } else {
678 Ok(())
679 }
680 }
681
682 fn get_binding_value(
683 &self,
684 ctx_stack: &mut ExecutionContextStack,
685 name: &String,
686 ) -> Result<JsValue, JErrorType> {
687 if self.declarative_record.has_binding(name) {
688 self.declarative_record.get_binding_value(ctx_stack, name)
689 } else {
690 self.object_record.get_binding_value(ctx_stack, name)
691 }
692 }
693
694 fn delete_binding(&mut self, name: &String) -> Result<bool, JErrorType> {
695 if self.declarative_record.has_binding(name) {
696 self.declarative_record.delete_binding(name)
697 } else {
698 Ok(
699 if has_own_property(
700 &self.object_record.binding_object,
701 &PropertyKey::Str(name.to_string()),
702 )? {
703 if self.object_record.delete_binding(name)? {
704 self.var_names.retain(|n| n != name);
705 true
706 } else {
707 false
708 }
709 } else {
710 true
711 },
712 )
713 }
714 }
715
716 fn has_this_binding(&self) -> bool {
717 true
718 }
719
720 fn has_super_binding(&self) -> bool {
721 false
722 }
723}
724
725pub fn new_declarative_environment(
726 outer_lex: Option<JsLexEnvironmentType>,
727) -> JsLexEnvironmentType {
728 Rc::new(RefCell::new(LexEnvironment {
729 inner: Box::new(EnvironmentRecordType::Declarative(
730 DeclarativeEnvironmentRecord::new(),
731 )),
732 outer: match outer_lex {
733 None => None,
734 Some(o) => Some(o.clone()),
735 },
736 }))
737}
738
739pub fn new_object_environment(
740 o: JsObjectType,
741 outer_lex: Option<JsLexEnvironmentType>,
742) -> JsLexEnvironmentType {
743 Rc::new(RefCell::new(LexEnvironment {
744 inner: Box::new(EnvironmentRecordType::Object(ObjectEnvironmentRecord::new(
745 o,
746 ))),
747 outer: match outer_lex {
748 None => None,
749 Some(o) => Some(o.clone()),
750 },
751 }))
752}
753
754pub fn new_function_environment(
755 f: JsObjectType,
756 new_target: Option<JsObjectType>,
757) -> JsLexEnvironmentType {
758 assert!((*f).borrow().is_callable(), "f needs to be callable");
759 if let Some(new_target) = &new_target {
760 assert!(
761 (**new_target).borrow().is_callable(),
762 "new_target needs to be callable"
763 );
764 }
765 let outer_lex = (*f)
766 .borrow()
767 .as_js_function_object()
768 .get_function_object_base()
769 .environment
770 .clone();
771 Rc::new(RefCell::new(LexEnvironment {
772 inner: Box::new(EnvironmentRecordType::Function(
773 FunctionEnvironmentRecord::new(f, new_target),
774 )),
775 outer: Some(outer_lex),
776 }))
777}
778
779pub fn new_global_environment(global_object: JsObjectType) -> JsLexEnvironmentType {
780 Rc::new(RefCell::new(LexEnvironment {
781 inner: Box::new(EnvironmentRecordType::Global(GlobalEnvironmentRecord::new(
782 global_object,
783 ))),
784 outer: None,
785 }))
786}