Skip to main content

just_engine/runner/ds/
env_record.rs

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    /// Get all bindings as a vector of (name, value) pairs. Used for generators.
40    fn get_all_bindings(&self) -> Option<Vec<(String, JsValue)>> {
41        None // Default implementation returns None
42    }
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        // Extract values from borrow before moving f
316        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 f_clone = f.clone();
572        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        // Commenting out this code. The specs (on Pg. 89) requires this but not sure why, seems redundant.
605        // set(
606        //     &mut self.object_record.binding_object,
607        //     PropertyKey::Str(name.to_string()),
608        //     f_clone,
609        // )?;
610        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}