Skip to main content

just_engine/runner/ds/
object.rs

1use crate::runner::ds::error::JErrorType;
2use crate::runner::ds::execution_context::ExecutionContextStack;
3use crate::runner::ds::function_object::JsFunctionObject;
4use crate::runner::ds::iterator_object::JsIteratorObject;
5use crate::runner::ds::object_property::{
6    PropertyDescriptor, PropertyDescriptorAccessor, PropertyDescriptorData,
7    PropertyDescriptorSetter, PropertyKey,
8};
9use crate::runner::ds::operations::object::{get, get_function_realm};
10use crate::runner::ds::operations::test_and_comparison::{
11    is_constructor, same_js_object, same_object, same_value,
12};
13use crate::runner::ds::operations::type_conversion::{
14    canonical_numeric_index_string, to_string_int,
15};
16use crate::runner::ds::realm::WellKnownIntrinsics;
17use crate::runner::ds::value::{JsValue, JsValueOrSelf};
18use std::cell::RefCell;
19use std::collections::HashMap;
20use std::fmt;
21use std::fmt::{Display, Formatter};
22use std::ops::Deref;
23use std::rc::Rc;
24
25lazy_static! {
26    pub static ref OBJECT_PROTOTYPE_PROP: PropertyKey = PropertyKey::Str("prototype".to_string());
27}
28
29pub type JsObjectType = Rc<RefCell<ObjectType>>;
30
31pub enum ObjectType {
32    Ordinary(Box<dyn JsObject>),
33    Function(Box<dyn JsFunctionObject>),
34}
35impl ObjectType {
36    pub fn is_callable(&self) -> bool {
37        match self {
38            ObjectType::Function(_) => true,
39            ObjectType::Ordinary(obj) => {
40                // Check for SimpleFunctionObject marker property
41                let marker = PropertyKey::Str("__simple_function__".to_string());
42                obj.get_object_base().properties.contains_key(&marker)
43            }
44        }
45    }
46
47    pub fn is_constructor(&self) -> bool {
48        is_constructor(self)
49    }
50
51    pub fn as_js_object(&self) -> &dyn JsObject {
52        match self {
53            ObjectType::Ordinary(o) => o.as_js_object(),
54            ObjectType::Function(o) => o.as_js_object(),
55        }
56    }
57
58    pub fn as_js_object_mut(&mut self) -> &mut dyn JsObject {
59        match self {
60            ObjectType::Ordinary(o) => o.as_js_object_mut(),
61            ObjectType::Function(o) => o.as_js_object_mut(),
62        }
63    }
64
65    pub fn as_js_function_object(&self) -> &dyn JsFunctionObject {
66        match self {
67            ObjectType::Ordinary(_o) => panic!("Not callable hence cannot get as JsFunctionObject"),
68            ObjectType::Function(o) => o.as_js_function_object(),
69        }
70    }
71
72    pub fn as_js_function_object_mut(&mut self) -> &mut dyn JsFunctionObject {
73        match self {
74            ObjectType::Ordinary(_o) => panic!("Not callable hence cannot get as JsFunctionObject"),
75            ObjectType::Function(o) => o.as_js_function_object_mut(),
76        }
77    }
78}
79impl PartialEq for ObjectType {
80    fn eq(&self, other: &Self) -> bool {
81        match self {
82            ObjectType::Ordinary(o) => {
83                if let ObjectType::Ordinary(o1) = other {
84                    same_js_object(o.deref(), o1.deref())
85                } else {
86                    false
87                }
88            }
89            ObjectType::Function(o) => {
90                if let ObjectType::Function(o1) = other {
91                    same_js_object(o.deref().as_js_object(), o1.deref().as_js_object())
92                } else {
93                    false
94                }
95            }
96        }
97    }
98}
99impl Display for ObjectType {
100    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
101        write!(f, "{}", self.as_js_object().to_string())
102    }
103}
104
105pub struct ObjectBase {
106    pub properties: HashMap<PropertyKey, PropertyDescriptor>,
107    pub is_extensible: bool,
108    pub prototype: Option<JsObjectType>,
109}
110impl ObjectBase {
111    pub fn new() -> Self {
112        ObjectBase {
113            properties: HashMap::new(),
114            is_extensible: true,
115            prototype: None,
116        }
117    }
118}
119
120pub trait JsObject {
121    fn get_object_base_mut(&mut self) -> &mut ObjectBase;
122
123    fn get_object_base(&self) -> &ObjectBase;
124
125    fn as_js_object(&self) -> &dyn JsObject;
126
127    fn as_js_object_mut(&mut self) -> &mut dyn JsObject;
128
129    fn get_prototype_of(&self) -> Option<JsObjectType> {
130        match &self.get_object_base().prototype {
131            None => None,
132            Some(j) => Some(j.clone()),
133        }
134    }
135
136    fn set_prototype_of(&mut self, prototype: Option<JsObjectType>) -> bool {
137        let current_value = &self.get_object_base().prototype;
138        let new_value: Option<JsObjectType> = match prototype {
139            None => {
140                if current_value.is_none() {
141                    return true;
142                } else {
143                    None
144                }
145            }
146            Some(p) => {
147                if let Some(current_p) = current_value {
148                    if same_object(
149                        p.deref().borrow().deref(),
150                        current_p.deref().borrow().deref(),
151                    ) {
152                        return true;
153                    } else {
154                        Some(p.clone())
155                    }
156                } else {
157                    None
158                }
159            }
160        };
161        if self.is_extensible() {
162            if let Some(new_value) = new_value {
163                let mut p = Some(new_value.clone());
164                loop {
165                    if let Some(some_p) = p {
166                        if same_js_object(self.as_js_object(), (*(*some_p).borrow()).as_js_object())
167                        {
168                            // To prevent circular chain
169                            return false;
170                        } else {
171                            let t1 = (*some_p).borrow();
172                            let t2 = (*t1).as_js_object().get_prototype_of();
173                            p = t2;
174                        }
175                    } else {
176                        break;
177                    }
178                }
179                self.get_object_base_mut().prototype = Some(new_value);
180            } else {
181                self.get_object_base_mut().prototype = None;
182            }
183            true
184        } else {
185            false
186        }
187    }
188
189    fn is_extensible(&self) -> bool {
190        self.get_object_base().is_extensible
191    }
192
193    fn prevent_extensions(&mut self) -> bool {
194        self.get_object_base_mut().is_extensible = false;
195        true
196    }
197
198    fn get_own_property(
199        &self,
200        property: &PropertyKey,
201    ) -> Result<Option<&PropertyDescriptor>, JErrorType> {
202        Ok(self.get_object_base().properties.get(property))
203    }
204
205    fn define_own_property(
206        &mut self,
207        property: PropertyKey,
208        descriptor_setter: PropertyDescriptorSetter,
209    ) -> Result<bool, JErrorType> {
210        ordinary_define_own_property(self, property, descriptor_setter)
211    }
212
213    fn has_property(&self, property: &PropertyKey) -> bool {
214        if self.get_object_base().properties.contains_key(property) {
215            true
216        } else {
217            match &self.get_object_base().prototype {
218                None => false,
219                Some(o) => (**o).borrow().as_js_object().has_property(property),
220            }
221        }
222    }
223
224    fn get(
225        &self,
226        ctx_stack: &mut ExecutionContextStack,
227        property: &PropertyKey,
228        receiver: JsValueOrSelf,
229    ) -> Result<JsValue, JErrorType> {
230        match self.get_own_property(property)? {
231            None => match self.get_prototype_of() {
232                None => Ok(JsValue::Undefined),
233                Some(p) => (*(*p).borrow())
234                    .as_js_object()
235                    .get(ctx_stack, property, receiver),
236            },
237            Some(pd) => match pd {
238                PropertyDescriptor::Data(PropertyDescriptorData { value, .. }) => Ok(value.clone()),
239                PropertyDescriptor::Accessor(PropertyDescriptorAccessor { get, .. }) => match get {
240                    None => Ok(JsValue::Undefined),
241                    Some(getter) => (**getter).borrow().as_js_function_object().call(
242                        getter,
243                        ctx_stack,
244                        receiver,
245                        Vec::new(),
246                    ),
247                },
248            },
249        }
250    }
251
252    fn set(
253        &mut self,
254        ctx_stack: &mut ExecutionContextStack,
255        property: PropertyKey,
256        value: JsValue,
257        receiver: JsValueOrSelf,
258    ) -> Result<bool, JErrorType> {
259        // Get own property first (immutable borrow)
260        let own_prop = self.get_own_property(&property)?;
261
262        match own_prop {
263            None => match self.get_prototype_of() {
264                None => {
265                    self.get_object_base_mut().properties.insert(
266                        property,
267                        PropertyDescriptor::Data(PropertyDescriptorData {
268                            value,
269                            writable: true,
270                            enumerable: true,
271                            configurable: true,
272                        }),
273                    );
274                    Ok(true)
275                }
276                Some(p) => (*p)
277                    .borrow_mut()
278                    .as_js_object_mut()
279                    .set(ctx_stack, property, value, receiver),
280            },
281            Some(pd) => match pd {
282                PropertyDescriptor::Data(PropertyDescriptorData {
283                    value: _, writable, ..
284                }) => {
285                    if *writable {
286                        match receiver {
287                            JsValueOrSelf::ValueRef(value_ref) => match value_ref {
288                                JsValue::Object(o) => {
289                                    let mut ot_obj = (**o).borrow_mut();
290                                    let obj = (*ot_obj).as_js_object_mut();
291                                    set_or_update_data_descriptor(obj, property, &value)
292                                }
293                                _ => Ok(false),
294                            },
295                            JsValueOrSelf::SelfValue => {
296                                set_or_update_data_descriptor(self.as_js_object_mut(), property, &value)
297                            }
298                        }
299                    } else {
300                        Ok(false)
301                    }
302                }
303                PropertyDescriptor::Accessor(PropertyDescriptorAccessor { set, .. }) => match set {
304                    None => Ok(false),
305                    Some(setter) => {
306                        (**setter).borrow_mut().as_js_function_object_mut().call(
307                            setter,
308                            ctx_stack,
309                            receiver,
310                            vec![value],
311                        )?;
312                        Ok(true)
313                    }
314                },
315            },
316        }
317    }
318
319    fn delete(&mut self, property: &PropertyKey) -> Result<bool, JErrorType> {
320        Ok(match self.get_own_property(property)? {
321            None => true,
322            Some(pd) => {
323                if pd.is_configurable() {
324                    self.get_object_base_mut().properties.remove(property);
325                    true
326                } else {
327                    false
328                }
329            }
330        })
331    }
332
333    fn enumerate(&self) -> JsIteratorObject {
334        todo!()
335    }
336
337    fn own_property_keys(&self, ctx_stack: &mut ExecutionContextStack) -> Vec<PropertyKey> {
338        let mut int_keys = vec![];
339        let mut str_keys = vec![];
340        let mut sym_keys = vec![];
341        for (key, _) in &self.get_object_base().properties {
342            match key {
343                PropertyKey::Str(d) => {
344                    if let Some(idx) = canonical_numeric_index_string(ctx_stack, d) {
345                        int_keys.push(idx);
346                    } else {
347                        str_keys.push(d.to_string());
348                    }
349                }
350                PropertyKey::Sym(d) => {
351                    sym_keys.push(d.clone());
352                }
353            }
354        }
355        int_keys.sort();
356
357        let mut result = vec![];
358        result.append(
359            &mut int_keys
360                .into_iter()
361                .map(|v| PropertyKey::Str(to_string_int(v as i64)))
362                .collect::<Vec<PropertyKey>>(),
363        );
364        result.append(
365            &mut str_keys
366                .into_iter()
367                .map(|v| PropertyKey::Str(v))
368                .collect::<Vec<PropertyKey>>(),
369        );
370        result.append(
371            &mut sym_keys
372                .into_iter()
373                .map(|v| PropertyKey::Sym(v))
374                .collect::<Vec<PropertyKey>>(),
375        );
376        result
377    }
378
379    fn to_string(&self) -> String {
380        format!("object")
381    }
382}
383
384pub struct CoreObject {
385    base: ObjectBase,
386}
387impl CoreObject {
388    fn new(proto: Option<JsObjectType>) -> Self {
389        let mut o = CoreObject {
390            base: ObjectBase::new(),
391        };
392        finish_object_create(&mut o, proto);
393        o
394    }
395
396    fn new_from_constructor(
397        ctx_stack: &mut ExecutionContextStack,
398        constructor: JsObjectType,
399        intrinsic_default_proto: &WellKnownIntrinsics,
400    ) -> Result<Self, JErrorType> {
401        let mut o = CoreObject {
402            base: ObjectBase::new(),
403        };
404        finish_object_create_from_constructor(
405            ctx_stack,
406            &mut o,
407            constructor,
408            intrinsic_default_proto,
409        )?;
410        Ok(o)
411    }
412}
413impl JsObject for CoreObject {
414    fn get_object_base_mut(&mut self) -> &mut ObjectBase {
415        &mut self.base
416    }
417
418    fn get_object_base(&self) -> &ObjectBase {
419        &self.base
420    }
421
422    fn as_js_object(&self) -> &dyn JsObject {
423        self
424    }
425
426    fn as_js_object_mut(&mut self) -> &mut dyn JsObject {
427        self
428    }
429}
430
431pub fn ordinary_define_own_property<J: JsObject + ?Sized>(
432    o: &mut J,
433    property: PropertyKey,
434    mut descriptor_setter: PropertyDescriptorSetter,
435) -> Result<bool, JErrorType> {
436    let current_descriptor = o.get_own_property(&property)?;
437    Ok(if let Some(current_descriptor) = current_descriptor {
438        if descriptor_setter.is_empty() {
439            true
440        } else if descriptor_setter.are_all_fields_set()
441            && current_descriptor == &descriptor_setter.descriptor
442        {
443            true
444        } else {
445            let descriptor = &descriptor_setter.descriptor;
446            if !current_descriptor.is_configurable() {
447                if descriptor.is_configurable() {
448                    return Ok(false);
449                } else {
450                    if (current_descriptor.is_enumerable() && !descriptor.is_enumerable())
451                        || (!current_descriptor.is_enumerable() && descriptor.is_enumerable())
452                    {
453                        return Ok(false);
454                    }
455                }
456            }
457            if !descriptor_setter.is_generic_descriptor() {
458                if (current_descriptor.is_data_descriptor() && !descriptor.is_data_descriptor())
459                    || (!current_descriptor.is_data_descriptor() && descriptor.is_data_descriptor())
460                {
461                    if !current_descriptor.is_configurable() {
462                        return Ok(false);
463                    }
464                    descriptor_setter.descriptor = match descriptor_setter.descriptor {
465                        PropertyDescriptor::Data(PropertyDescriptorData {
466                            value,
467                            writable,
468                            ..
469                        }) => PropertyDescriptor::Data(PropertyDescriptorData {
470                            value,
471                            writable,
472                            enumerable: current_descriptor.is_enumerable(),
473                            configurable: current_descriptor.is_configurable(),
474                        }),
475                        PropertyDescriptor::Accessor(PropertyDescriptorAccessor {
476                            set: setter,
477                            get: getter,
478                            ..
479                        }) => PropertyDescriptor::Accessor(PropertyDescriptorAccessor {
480                            set: setter,
481                            get: getter,
482                            enumerable: current_descriptor.is_enumerable(),
483                            configurable: current_descriptor.is_configurable(),
484                        }),
485                    };
486                } else if current_descriptor.is_data_descriptor() && descriptor.is_data_descriptor()
487                {
488                    if !current_descriptor.is_configurable() {
489                        if let PropertyDescriptor::Data(PropertyDescriptorData {
490                            writable: current_writable,
491                            value: current_value,
492                            ..
493                        }) = current_descriptor
494                        {
495                            if let PropertyDescriptor::Data(PropertyDescriptorData {
496                                writable: desc_writable,
497                                value: desc_value,
498                                ..
499                            }) = &descriptor
500                            {
501                                if !*current_writable && *desc_writable {
502                                    return Ok(false);
503                                }
504                                if !*current_writable {
505                                    if descriptor_setter.honour_value
506                                        && !same_value(current_value, desc_value)
507                                    {
508                                        return Ok(false);
509                                    }
510                                }
511                            }
512                        }
513                    }
514                } else if current_descriptor.is_accessor_descriptor()
515                    && descriptor.is_accessor_descriptor()
516                {
517                    if !current_descriptor.is_configurable() {
518                        if let PropertyDescriptor::Accessor(PropertyDescriptorAccessor {
519                            set: current_set,
520                            get: current_get,
521                            ..
522                        }) = current_descriptor
523                        {
524                            if let PropertyDescriptor::Accessor(PropertyDescriptorAccessor {
525                                set: desc_set,
526                                get: desc_get,
527                                ..
528                            }) = &descriptor
529                            {
530                                if !(current_set.is_none() && desc_set.is_none())
531                                    && descriptor_setter.honour_set
532                                    && ((!current_set.is_none() && desc_set.is_none())
533                                        || (current_set.is_none() && !desc_set.is_none())
534                                        || !same_js_object(
535                                            current_set.as_ref().unwrap().borrow().as_js_object(),
536                                            desc_set.as_ref().unwrap().borrow().as_js_object(),
537                                        ))
538                                {
539                                    return Ok(false);
540                                }
541
542                                if !(current_get.is_none() && desc_get.is_none())
543                                    && descriptor_setter.honour_get
544                                    && ((!current_get.is_none() && desc_get.is_none())
545                                        || (current_get.is_none() && !desc_get.is_none())
546                                        || !same_object(
547                                            &(*current_get.as_ref().unwrap()).borrow(),
548                                            &(*desc_get.as_ref().unwrap()).borrow(),
549                                        ))
550                                {
551                                    return Ok(false);
552                                }
553                            }
554                        }
555                    }
556                }
557            }
558            o.get_object_base_mut().properties.insert(
559                property,
560                PropertyDescriptor::new_from_property_descriptor_setter(descriptor_setter),
561            );
562            true
563        }
564    } else {
565        if o.is_extensible() {
566            o.get_object_base_mut().properties.insert(
567                property,
568                PropertyDescriptor::new_from_property_descriptor_setter(descriptor_setter),
569            );
570            true
571        } else {
572            false
573        }
574    })
575}
576
577fn set_or_update_data_descriptor(
578    obj: &mut dyn JsObject,
579    property: PropertyKey,
580    value: &JsValue,
581) -> Result<bool, JErrorType> {
582    match obj.get_own_property(&property)? {
583        None => {
584            let desc_setter = PropertyDescriptorSetter::new_from_property_descriptor(
585                PropertyDescriptor::Data(PropertyDescriptorData {
586                    value: value.clone(),
587                    writable: true,
588                    enumerable: true,
589                    configurable: true,
590                }),
591            );
592            obj.define_own_property(property, desc_setter)?;
593            Ok(true)
594        }
595        Some(pd) => match pd {
596            PropertyDescriptor::Data(PropertyDescriptorData { writable, .. }) => {
597                if *writable {
598                    obj.define_own_property(
599                        property,
600                        PropertyDescriptorSetter {
601                            honour_value: true,
602                            honour_writable: false,
603                            honour_set: false,
604                            honour_get: false,
605                            honour_enumerable: false,
606                            honour_configurable: false,
607                            descriptor: PropertyDescriptor::Data(PropertyDescriptorData {
608                                value: value.clone(),
609                                writable: false,
610                                enumerable: false,
611                                configurable: false,
612                            }),
613                        },
614                    )
615                } else {
616                    Ok(false)
617                }
618            }
619            PropertyDescriptor::Accessor { .. } => Ok(false),
620        },
621    }
622}
623
624pub fn object_create(proto: Option<JsObjectType>) -> impl JsObject {
625    CoreObject::new(proto)
626}
627
628pub fn object_create_from_constructor(
629    ctx_stack: &mut ExecutionContextStack,
630    constructor: JsObjectType,
631    intrinsic_default_proto: &WellKnownIntrinsics,
632) -> Result<impl JsObject, JErrorType> {
633    CoreObject::new_from_constructor(ctx_stack, constructor, intrinsic_default_proto)
634}
635
636pub fn finish_object_create(obj: &mut dyn JsObject, proto: Option<JsObjectType>) {
637    obj.get_object_base_mut().prototype = proto;
638    obj.get_object_base_mut().is_extensible = true;
639}
640
641pub fn finish_object_create_from_constructor(
642    ctx_stack: &mut ExecutionContextStack,
643    obj: &mut dyn JsObject,
644    constructor: JsObjectType,
645    intrinsic_default_proto: &WellKnownIntrinsics,
646) -> Result<(), JErrorType> {
647    let proto = get_prototype_from_constructor(ctx_stack, constructor, intrinsic_default_proto)?;
648    finish_object_create(obj, Some(proto));
649    Ok(())
650}
651
652pub fn get_prototype_from_constructor(
653    ctx_stack: &mut ExecutionContextStack,
654    constructor: JsObjectType,
655    intrinsic_default_proto: &WellKnownIntrinsics,
656) -> Result<JsObjectType, JErrorType> {
657    let proto = get(ctx_stack, &constructor, &*OBJECT_PROTOTYPE_PROP)?;
658    if let JsValue::Object(o) = proto {
659        Ok(o)
660    } else {
661        let realm = get_function_realm(&*(*constructor).borrow())?;
662        let proto = (*realm)
663            .borrow()
664            .get_intrinsics_value(intrinsic_default_proto)
665            .clone();
666        Ok(proto)
667    }
668}