Skip to main content

boa_engine/object/
operations.rs

1use super::internal_methods::InternalMethodPropertyContext;
2use crate::js_error;
3use crate::value::JsVariant;
4use crate::{
5    Context, JsExpect, JsResult, JsSymbol, JsValue,
6    builtins::{
7        Array, Proxy,
8        function::{BoundFunction, ClassFieldDefinition, OrdinaryFunction, set_function_name},
9    },
10    context::intrinsics::{StandardConstructor, StandardConstructors},
11    error::JsNativeError,
12    native_function::NativeFunctionObject,
13    object::{CONSTRUCTOR, JsObject, PROTOTYPE, PrivateElement, PrivateName},
14    property::{PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey, PropertyNameKind},
15    realm::Realm,
16    string::StaticJsStrings,
17    value::Type,
18};
19
20/// Object integrity level.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum IntegrityLevel {
23    /// Sealed object integrity level.
24    ///
25    /// Preventing new properties from being added to it and marking all existing
26    /// properties as non-configurable. Values of present properties can still be
27    /// changed as long as they are writable.
28    Sealed,
29
30    /// Frozen object integrity level
31    ///
32    /// A frozen object can no longer be changed; freezing an object prevents new
33    /// properties from being added to it, existing properties from being removed,
34    /// prevents changing the enumerability, configurability, or writability of
35    /// existing properties, and prevents the values of existing properties from
36    /// being changed. In addition, freezing an object also prevents its prototype
37    /// from being changed.
38    Frozen,
39}
40
41impl IntegrityLevel {
42    /// Returns `true` if the integrity level is sealed.
43    #[must_use]
44    pub const fn is_sealed(&self) -> bool {
45        matches!(self, Self::Sealed)
46    }
47
48    /// Returns `true` if the integrity level is frozen.
49    #[must_use]
50    pub const fn is_frozen(&self) -> bool {
51        matches!(self, Self::Frozen)
52    }
53}
54
55impl JsObject {
56    /// Check if object is extensible.
57    ///
58    /// More information:
59    ///  - [ECMAScript reference][spec]
60    ///
61    /// [spec]: https://tc39.es/ecma262/#sec-isextensible-o
62    #[inline]
63    pub fn is_extensible(&self, context: &mut Context) -> JsResult<bool> {
64        // 1. Return ? O.[[IsExtensible]]().
65        self.__is_extensible__(context)
66    }
67
68    /// Get property from object or throw.
69    ///
70    /// More information:
71    ///  - [ECMAScript reference][spec]
72    ///
73    /// [spec]: https://tc39.es/ecma262/#sec-get-o-p
74    pub fn get<K>(&self, key: K, context: &mut Context) -> JsResult<JsValue>
75    where
76        K: Into<PropertyKey>,
77    {
78        // 1. Assert: Type(O) is Object.
79        // 2. Assert: IsPropertyKey(P) is true.
80        // 3. Return ? O.[[Get]](P, O).
81        self.__get__(
82            &key.into(),
83            self.clone().into(),
84            &mut InternalMethodPropertyContext::new(context),
85        )
86    }
87
88    /// set property of object or throw if bool flag is passed.
89    ///
90    /// More information:
91    ///  - [ECMAScript reference][spec]
92    ///
93    /// [spec]: https://tc39.es/ecma262/#sec-set-o-p-v-throw
94    pub fn set<K, V>(&self, key: K, value: V, throw: bool, context: &mut Context) -> JsResult<bool>
95    where
96        K: Into<PropertyKey>,
97        V: Into<JsValue>,
98    {
99        let key = key.into();
100        // 1. Assert: Type(O) is Object.
101        // 2. Assert: IsPropertyKey(P) is true.
102        // 3. Assert: Type(Throw) is Boolean.
103        // 4. Let success be ? O.[[Set]](P, V, O).
104        let success = self.__set__(
105            key.clone(),
106            value.into(),
107            self.clone().into(),
108            &mut InternalMethodPropertyContext::new(context),
109        )?;
110        // 5. If success is false and Throw is true, throw a TypeError exception.
111        if !success && throw {
112            return Err(JsNativeError::typ()
113                .with_message(format!("cannot set non-writable property: {key}"))
114                .into());
115        }
116        // 6. Return success.
117        Ok(success)
118    }
119
120    /// Create data property
121    ///
122    /// More information:
123    ///  - [ECMAScript reference][spec]
124    ///
125    /// [spec]: https://tc39.es/ecma262/#sec-createdataproperty
126    pub fn create_data_property<K, V>(
127        &self,
128        key: K,
129        value: V,
130        context: &mut Context,
131    ) -> JsResult<bool>
132    where
133        K: Into<PropertyKey>,
134        V: Into<JsValue>,
135    {
136        let key = key.into();
137        let value = value.into();
138        if let PropertyKey::Index(index) = &key
139            && Array::try_append_dense(self, u64::from(index.get()), &value, context)
140        {
141            return Ok(true);
142        }
143        // 1. Assert: Type(O) is Object.
144        // 2. Assert: IsPropertyKey(P) is true.
145        // 3. Let newDesc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
146        let new_desc = PropertyDescriptor::builder()
147            .value(value)
148            .writable(true)
149            .enumerable(true)
150            .configurable(true);
151        // 4. Return ? O.[[DefineOwnProperty]](P, newDesc).
152        self.__define_own_property__(
153            &key,
154            new_desc.into(),
155            &mut InternalMethodPropertyContext::new(context),
156        )
157    }
158
159    /// Create data property
160    ///
161    /// More information:
162    ///  - [ECMAScript reference][spec]
163    ///
164    /// [spec]: https://tc39.es/ecma262/#sec-createdataproperty
165    pub(crate) fn create_data_property_with_slot<K, V>(
166        &self,
167        key: K,
168        value: V,
169        context: &mut InternalMethodPropertyContext<'_>,
170    ) -> JsResult<bool>
171    where
172        K: Into<PropertyKey>,
173        V: Into<JsValue>,
174    {
175        // 1. Assert: Type(O) is Object.
176        // 2. Assert: IsPropertyKey(P) is true.
177        // 3. Let newDesc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
178        let new_desc = PropertyDescriptor::builder()
179            .value(value)
180            .writable(true)
181            .enumerable(true)
182            .configurable(true);
183        // 4. Return ? O.[[DefineOwnProperty]](P, newDesc).
184        self.__define_own_property__(&key.into(), new_desc.into(), context)
185    }
186
187    /// `10.2.8 DefineMethodProperty ( homeObject, key, closure, enumerable )`
188    ///
189    /// Defines a method property on an object with the specified attributes.
190    ///
191    /// More information:
192    ///  - [ECMAScript reference][spec]
193    ///
194    /// [spec]: https://tc39.es/ecma262/#sec-definemethodproperty
195    pub(crate) fn define_method_property<K, V>(
196        &self,
197        key: K,
198        value: V,
199        context: &mut InternalMethodPropertyContext<'_>,
200    ) -> JsResult<()>
201    where
202        K: Into<PropertyKey>,
203        V: Into<JsValue>,
204    {
205        // 1. Assert: homeObject is an ordinary, extensible object with no non-configurable properties.
206        // 2. If key is a Private Name, then
207        //    a. Return PrivateElement { [[Key]]: key, [[Kind]]: method, [[Value]]: closure }.
208        // 3. Else,
209        //    a. Let desc be the PropertyDescriptor { [[Value]]: closure, [[Writable]]: true, [[Enumerable]]: enumerable, [[Configurable]]: true }.
210        let new_desc = PropertyDescriptor::builder()
211            .value(value)
212            .writable(true)
213            .enumerable(false)
214            .configurable(true);
215
216        //    b. Perform ! DefinePropertyOrThrow(homeObject, key, desc).
217        self.__define_own_property__(&key.into(), new_desc.into(), context)?;
218
219        //    c. Return unused.
220        Ok(())
221    }
222
223    /// Create data property or throw
224    ///
225    /// More information:
226    ///  - [ECMAScript reference][spec]
227    ///
228    /// [spec]: https://tc39.es/ecma262/#sec-createdatapropertyorthrow
229    pub fn create_data_property_or_throw<K, V>(
230        &self,
231        key: K,
232        value: V,
233        context: &mut Context,
234    ) -> JsResult<bool>
235    where
236        K: Into<PropertyKey>,
237        V: Into<JsValue>,
238    {
239        let key = key.into();
240        // 1. Assert: Type(O) is Object.
241        // 2. Assert: IsPropertyKey(P) is true.
242        // 3. Let success be ? CreateDataProperty(O, P, V).
243        let success = self.create_data_property(key.clone(), value, context)?;
244        // 4. If success is false, throw a TypeError exception.
245        if !success {
246            return Err(JsNativeError::typ()
247                .with_message(format!("cannot redefine property: {key}"))
248                .into());
249        }
250        // 5. Return success.
251        Ok(success)
252    }
253
254    /// Create non-enumerable data property or throw
255    ///
256    /// More information:
257    ///  - [ECMAScript reference][spec]
258    ///
259    /// [spec]: https://tc39.es/ecma262/#sec-createnonenumerabledatapropertyinfallibly
260    pub(crate) fn create_non_enumerable_data_property_or_throw<K, V>(
261        &self,
262        key: K,
263        value: V,
264        context: &mut Context,
265    ) where
266        K: Into<PropertyKey>,
267        V: Into<JsValue>,
268    {
269        // 1. Assert: O is an ordinary, extensible object with no non-configurable properties.
270
271        // 2. Let newDesc be the PropertyDescriptor {
272        //    [[Value]]: V,
273        //    [[Writable]]: true,
274        //    [[Enumerable]]: false,
275        //    [[Configurable]]: true
276        //  }.
277        let new_desc = PropertyDescriptorBuilder::new()
278            .value(value)
279            .writable(true)
280            .enumerable(false)
281            .configurable(true)
282            .build();
283
284        // 3. Perform ! DefinePropertyOrThrow(O, P, newDesc).
285        self.define_property_or_throw(key, new_desc, context)
286            .expect("should not fail according to spec");
287
288        // 4. Return unused.
289    }
290
291    /// Define property or throw.
292    ///
293    /// More information:
294    ///  - [ECMAScript reference][spec]
295    ///
296    /// [spec]: https://tc39.es/ecma262/#sec-definepropertyorthrow
297    pub fn define_property_or_throw<K, P>(
298        &self,
299        key: K,
300        desc: P,
301        context: &mut Context,
302    ) -> JsResult<bool>
303    where
304        K: Into<PropertyKey>,
305        P: Into<PropertyDescriptor>,
306    {
307        let key = key.into();
308        // 1. Assert: Type(O) is Object.
309        // 2. Assert: IsPropertyKey(P) is true.
310        // 3. Let success be ? O.[[DefineOwnProperty]](P, desc).
311        let success = self.__define_own_property__(
312            &key,
313            desc.into(),
314            &mut InternalMethodPropertyContext::new(context),
315        )?;
316        // 4. If success is false, throw a TypeError exception.
317        if !success {
318            return Err(JsNativeError::typ()
319                .with_message(format!("cannot redefine property: {key}"))
320                .into());
321        }
322        // 5. Return success.
323        Ok(success)
324    }
325
326    /// Defines the property or throws a `TypeError` if the operation fails.
327    ///
328    /// More information:
329    /// - [ECMAScript reference][spec]
330    ///
331    /// [spec]: https://tc39.es/ecma262/#sec-deletepropertyorthrow
332    pub fn delete_property_or_throw<K>(&self, key: K, context: &mut Context) -> JsResult<bool>
333    where
334        K: Into<PropertyKey>,
335    {
336        let key = key.into();
337        // 1. Assert: Type(O) is Object.
338        // 2. Assert: IsPropertyKey(P) is true.
339        // 3. Let success be ? O.[[Delete]](P).
340        let success = self.__delete__(&key, &mut InternalMethodPropertyContext::new(context))?;
341        // 4. If success is false, throw a TypeError exception.
342        if !success {
343            return Err(JsNativeError::typ()
344                .with_message(format!("cannot delete non-configurable property: {key}"))
345                .into());
346        }
347        // 5. Return success.
348        Ok(success)
349    }
350
351    /// Check if object has property.
352    ///
353    /// More information:
354    ///  - [ECMAScript reference][spec]
355    ///
356    /// [spec]: https://tc39.es/ecma262/#sec-hasproperty
357    pub fn has_property<K>(&self, key: K, context: &mut Context) -> JsResult<bool>
358    where
359        K: Into<PropertyKey>,
360    {
361        // 1. Assert: Type(O) is Object.
362        // 2. Assert: IsPropertyKey(P) is true.
363        // 3. Return ? O.[[HasProperty]](P).
364
365        self.__has_property__(
366            &key.into(),
367            &mut InternalMethodPropertyContext::new(context),
368        )
369    }
370
371    /// Abstract optimization operation.
372    ///
373    /// Check if an object has a property and get it if it exists.
374    /// This operation combines the abstract operations `HasProperty` and `Get`.
375    ///
376    /// More information:
377    ///  - [ECMAScript reference HasProperty][spec0]
378    ///  - [ECMAScript reference Get][spec1]
379    ///
380    /// [spec0]: https://tc39.es/ecma262/#sec-hasproperty
381    /// [spec1]: https://tc39.es/ecma262/#sec-get-o-p
382    pub(crate) fn try_get<K>(&self, key: K, context: &mut Context) -> JsResult<Option<JsValue>>
383    where
384        K: Into<PropertyKey>,
385    {
386        self.__try_get__(
387            &key.into(),
388            self.clone().into(),
389            &mut InternalMethodPropertyContext::new(context),
390        )
391    }
392
393    /// Check if object has an own property.
394    ///
395    /// More information:
396    ///  - [ECMAScript reference][spec]
397    ///
398    /// [spec]: https://tc39.es/ecma262/#sec-hasownproperty
399    pub fn has_own_property<K>(&self, key: K, context: &mut Context) -> JsResult<bool>
400    where
401        K: Into<PropertyKey>,
402    {
403        let key = key.into();
404        // 1. Assert: Type(O) is Object.
405        // 2. Assert: IsPropertyKey(P) is true.
406        // 3. Let desc be ? O.[[GetOwnProperty]](P).
407        let desc =
408            self.__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;
409        // 4. If desc is undefined, return false.
410        // 5. Return true.
411        Ok(desc.is_some())
412    }
413
414    /// Get all the keys of the properties of this object.
415    ///
416    /// More information:
417    ///  - [ECMAScript reference][spec]
418    ///
419    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys
420    pub fn own_property_keys(&self, context: &mut Context) -> JsResult<Vec<PropertyKey>> {
421        self.__own_property_keys__(context)
422    }
423
424    /// `Call ( F, V [ , argumentsList ] )`
425    ///
426    /// # Panics
427    ///
428    /// Panics if the object is currently mutably borrowed.
429    ///
430    /// More information:
431    ///  - [ECMAScript reference][spec]
432    ///
433    /// [spec]: https://tc39.es/ecma262/#sec-call
434    #[track_caller]
435    #[inline]
436    pub fn call(
437        &self,
438        this: &JsValue,
439        args: &[JsValue],
440        context: &mut Context,
441    ) -> JsResult<JsValue> {
442        // SKIP: 1. If argumentsList is not present, set argumentsList to a new empty List.
443        // SKIP: 2. If IsCallable(F) is false, throw a TypeError exception.
444        // NOTE(HalidOdat): For object's that are not callable we implement a special __call__ internal method
445        //                  that throws on call.
446
447        context.vm.stack.push(this.clone()); // this
448        context.vm.stack.push(self.clone()); // func
449        let argument_count = args.len();
450        context.vm.stack.calling_convention_push_arguments(args);
451
452        // 3. Return ? F.[[Call]](V, argumentsList).
453        let frame_index = context.vm.frames.len();
454        if self.__call__(argument_count).resolve(context)? {
455            return Ok(context.vm.stack.pop());
456        }
457
458        if frame_index + 1 == context.vm.frames.len() {
459            context.vm.frame_mut().set_exit_early(true);
460        } else {
461            context.vm.frames[frame_index + 1].set_exit_early(true);
462        }
463
464        context.vm.host_call_depth += 1;
465        let result = context.run().consume();
466        context.vm.host_call_depth = context.vm.host_call_depth.saturating_sub(1);
467
468        context.vm.pop_frame().js_expect("frame must exist")?;
469
470        result
471    }
472
473    /// `Construct ( F [ , argumentsList [ , newTarget ] ] )`
474    ///
475    /// Construct an instance of this object with the specified arguments.
476    ///
477    /// # Panics
478    ///
479    /// Panics if the object is currently mutably borrowed.
480    ///
481    /// More information:
482    ///  - [ECMAScript reference][spec]
483    ///
484    /// [spec]: https://tc39.es/ecma262/#sec-construct
485    #[track_caller]
486    #[inline]
487    pub fn construct(
488        &self,
489        args: &[JsValue],
490        new_target: Option<&Self>,
491        context: &mut Context,
492    ) -> JsResult<Self> {
493        // 1. If newTarget is not present, set newTarget to F.
494        let new_target = new_target.unwrap_or(self);
495
496        context.vm.stack.push(JsValue::undefined());
497        context.vm.stack.push(self.clone()); // func
498        let argument_count = args.len();
499        context.vm.stack.calling_convention_push_arguments(args);
500        context.vm.stack.push(new_target.clone());
501
502        // 2. If argumentsList is not present, set argumentsList to a new empty List.
503        // 3. Return ? F.[[Construct]](argumentsList, newTarget).
504        let frame_index = context.vm.frames.len();
505
506        if self.__construct__(argument_count).resolve(context)? {
507            let result = context.vm.stack.pop();
508            return Ok(result
509                .as_object()
510                .js_expect("construct value should be an object")?
511                .clone());
512        }
513
514        if frame_index + 1 == context.vm.frames.len() {
515            context.vm.frame_mut().set_exit_early(true);
516        } else {
517            context.vm.frames[frame_index + 1].set_exit_early(true);
518        }
519
520        context.vm.host_call_depth += 1;
521        let result = context.run().consume();
522        context.vm.host_call_depth = context.vm.host_call_depth.saturating_sub(1);
523
524        context.vm.pop_frame().js_expect("frame must exist")?;
525
526        Ok(result?
527            .as_object()
528            .js_expect("should be an object")?
529            .clone())
530    }
531
532    /// Make the object [`sealed`][IntegrityLevel::Sealed] or [`frozen`][IntegrityLevel::Frozen].
533    ///
534    /// More information:
535    ///  - [ECMAScript reference][spec]
536    ///
537    /// [spec]: https://tc39.es/ecma262/#sec-setintegritylevel
538    pub fn set_integrity_level(
539        &self,
540        level: IntegrityLevel,
541        context: &mut Context,
542    ) -> JsResult<bool> {
543        // 1. Assert: Type(O) is Object.
544        // 2. Assert: level is either sealed or frozen.
545
546        // 3. Let status be ? O.[[PreventExtensions]]().
547        let status =
548            self.__prevent_extensions__(&mut InternalMethodPropertyContext::new(context))?;
549        // 4. If status is false, return false.
550        if !status {
551            return Ok(false);
552        }
553
554        // 5. Let keys be ? O.[[OwnPropertyKeys]]().
555        let keys = self.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;
556
557        match level {
558            // 6. If level is sealed, then
559            IntegrityLevel::Sealed => {
560                // a. For each element k of keys, do
561                for k in keys {
562                    // i. Perform ? DefinePropertyOrThrow(O, k, PropertyDescriptor { [[Configurable]]: false }).
563                    self.define_property_or_throw(
564                        k,
565                        PropertyDescriptor::builder().configurable(false).build(),
566                        context,
567                    )?;
568                }
569            }
570            // 7. Else,
571            //     a. Assert: level is frozen.
572            IntegrityLevel::Frozen => {
573                // b. For each element k of keys, do
574                for k in keys {
575                    // i. Let currentDesc be ? O.[[GetOwnProperty]](k).
576                    let current_desc = self.__get_own_property__(
577                        &k,
578                        &mut InternalMethodPropertyContext::new(context),
579                    )?;
580                    // ii. If currentDesc is not undefined, then
581                    if let Some(current_desc) = current_desc {
582                        // 1. If IsAccessorDescriptor(currentDesc) is true, then
583                        let desc = if current_desc.is_accessor_descriptor() {
584                            // a. Let desc be the PropertyDescriptor { [[Configurable]]: false }.
585                            PropertyDescriptor::builder().configurable(false).build()
586                        // 2. Else,
587                        } else {
588                            // a. Let desc be the PropertyDescriptor { [[Configurable]]: false, [[Writable]]: false }.
589                            PropertyDescriptor::builder()
590                                .configurable(false)
591                                .writable(false)
592                                .build()
593                        };
594                        // 3. Perform ? DefinePropertyOrThrow(O, k, desc).
595                        self.define_property_or_throw(k, desc, context)?;
596                    }
597                }
598            }
599        }
600
601        // 8. Return true.
602        Ok(true)
603    }
604
605    /// Check if the object is [`sealed`][IntegrityLevel::Sealed] or [`frozen`][IntegrityLevel::Frozen].
606    ///
607    /// More information:
608    ///  - [ECMAScript reference][spec]
609    ///
610    /// [spec]: https://tc39.es/ecma262/#sec-testintegritylevel
611    pub fn test_integrity_level(
612        &self,
613        level: IntegrityLevel,
614        context: &mut Context,
615    ) -> JsResult<bool> {
616        // 1. Assert: Type(O) is Object.
617        // 2. Assert: level is either sealed or frozen.
618
619        // 3. Let extensible be ? IsExtensible(O).
620        let extensible = self.is_extensible(context)?;
621
622        // 4. If extensible is true, return false.
623        if extensible {
624            return Ok(false);
625        }
626
627        // 5. NOTE: If the object is extensible, none of its properties are examined.
628        // 6. Let keys be ? O.[[OwnPropertyKeys]]().
629        let keys = self.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;
630
631        // 7. For each element k of keys, do
632        for k in keys {
633            // a. Let currentDesc be ? O.[[GetOwnProperty]](k).
634            let current_desc =
635                self.__get_own_property__(&k, &mut InternalMethodPropertyContext::new(context))?;
636            // b. If currentDesc is not undefined, then
637            if let Some(current_desc) = current_desc {
638                // i. If currentDesc.[[Configurable]] is true, return false.
639                if current_desc.expect_configurable() {
640                    return Ok(false);
641                }
642                // ii. If level is frozen and IsDataDescriptor(currentDesc) is true, then
643                if level.is_frozen() && current_desc.is_data_descriptor() {
644                    // 1. If currentDesc.[[Writable]] is true, return false.
645                    if current_desc.expect_writable() {
646                        return Ok(false);
647                    }
648                }
649            }
650        }
651        // 8. Return true.
652        Ok(true)
653    }
654
655    /// Abstract operation [`LengthOfArrayLike ( obj )`][spec].
656    ///
657    /// Returns the value of the "length" property of an array-like object.
658    ///
659    /// [spec]: https://tc39.es/ecma262/#sec-lengthofarraylike
660    pub(crate) fn length_of_array_like(&self, context: &mut Context) -> JsResult<u64> {
661        // 1. Assert: Type(obj) is Object.
662
663        // NOTE: This is an optimization, most of the cases that `LengthOfArrayLike` will be called
664        //       is for arrays. The "length" property of an array is stored in the first index.
665        if self.is_array() {
666            let borrowed_object = self.borrow();
667            // NOTE: using `to_u32` instead of `to_length` is an optimization,
668            //       since arrays are limited to [0, 2^32 - 1] range.
669            return borrowed_object.properties().storage[0]
670                .to_u32(context)
671                .map(u64::from);
672        }
673
674        // 2. Return ℝ(? ToLength(? Get(obj, "length"))).
675        self.get(StaticJsStrings::LENGTH, context)?
676            .to_length(context)
677    }
678
679    /// `7.3.22 SpeciesConstructor ( O, defaultConstructor )`
680    ///
681    /// The abstract operation `SpeciesConstructor` takes arguments `O` (an Object) and
682    /// `defaultConstructor` (a constructor). It is used to retrieve the constructor that should be
683    /// used to create new objects that are derived from `O`. `defaultConstructor` is the
684    /// constructor to use if a constructor `@@species` property cannot be found starting from `O`.
685    ///
686    /// More information:
687    ///  - [ECMAScript reference][spec]
688    ///
689    /// [spec]: https://tc39.es/ecma262/#sec-speciesconstructor
690    pub(crate) fn species_constructor<F>(
691        &self,
692        default_constructor: F,
693        context: &mut Context,
694    ) -> JsResult<Self>
695    where
696        F: FnOnce(&StandardConstructors) -> &StandardConstructor,
697    {
698        // 1. Assert: Type(O) is Object.
699
700        // 2. Let C be ? Get(O, "constructor").
701        let c = self.get(CONSTRUCTOR, context)?;
702
703        // 3. If C is undefined, return defaultConstructor.
704        if c.is_undefined() {
705            return Ok(default_constructor(context.intrinsics().constructors()).constructor());
706        }
707
708        // 4. If Type(C) is not Object, throw a TypeError exception.
709        let c = c.as_object().ok_or_else(|| {
710            JsNativeError::typ().with_message("property 'constructor' is not an object")
711        })?;
712
713        // 5. Let S be ? Get(C, @@species).
714        let s = c.get(JsSymbol::species(), context)?;
715
716        // 6. If S is either undefined or null, return defaultConstructor.
717        if s.is_null_or_undefined() {
718            return Ok(default_constructor(context.intrinsics().constructors()).constructor());
719        }
720
721        // 7. If IsConstructor(S) is true, return S.
722        if let Some(s) = s.as_constructor() {
723            return Ok(s.clone());
724        }
725
726        // 8. Throw a TypeError exception.
727        Err(JsNativeError::typ()
728            .with_message("property 'constructor' is not a constructor")
729            .into())
730    }
731
732    /// It is used to iterate over names of object's keys.
733    ///
734    /// More information:
735    /// - [ECMAScript reference][spec]
736    ///
737    /// [spec]: https://tc39.es/ecma262/#sec-enumerableownpropertynames
738    pub(crate) fn enumerable_own_property_names(
739        &self,
740        kind: PropertyNameKind,
741        context: &mut Context,
742    ) -> JsResult<Vec<JsValue>> {
743        // 1. Assert: Type(O) is Object.
744        // 2. Let ownKeys be ? O.[[OwnPropertyKeys]]().
745        let own_keys =
746            self.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;
747        // 3. Let properties be a new empty List.
748        let mut properties = Vec::with_capacity(own_keys.len());
749
750        // 4. For each element key of ownKeys, do
751        for key in own_keys {
752            // a. If Type(key) is String, then
753            let key_str = match &key {
754                PropertyKey::String(s) => Some(s.clone()),
755                PropertyKey::Index(i) => Some(i.get().to_string().into()),
756                PropertyKey::Symbol(_) => None,
757            };
758
759            if let Some(key_str) = key_str {
760                // i. Let desc be ? O.[[GetOwnProperty]](key).
761                let desc = self
762                    .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;
763                // ii. If desc is not undefined and desc.[[Enumerable]] is true, then
764                if let Some(desc) = desc
765                    && desc.expect_enumerable()
766                {
767                    match kind {
768                        // 1. If kind is key, append key to properties.
769                        PropertyNameKind::Key => properties.push(key_str.into()),
770                        // 2. Else,
771                        // a. Let value be ? Get(O, key).
772                        // b. If kind is value, append value to properties.
773                        PropertyNameKind::Value => {
774                            properties.push(self.get(key.clone(), context)?);
775                        }
776                        // c. Else,
777                        // i. Assert: kind is key+value.
778                        // ii. Let entry be ! CreateArrayFromList(« key, value »).
779                        // iii. Append entry to properties.
780                        PropertyNameKind::KeyAndValue => properties.push(
781                            Array::create_array_from_list(
782                                [key_str.into(), self.get(key.clone(), context)?],
783                                context,
784                            )
785                            .into(),
786                        ),
787                    }
788                }
789            }
790        }
791
792        // 5. Return properties.
793        Ok(properties)
794    }
795
796    /// Abstract operation `GetMethod ( V, P )`
797    ///
798    /// Retrieves the value of a specific property, when the value of the property is expected to be a function.
799    ///
800    /// More information:
801    /// - [ECMAScript reference][spec]
802    ///
803    /// [spec]: https://tc39.es/ecma262/#sec-getmethod
804    pub(crate) fn get_method<K>(&self, key: K, context: &mut Context) -> JsResult<Option<Self>>
805    where
806        K: Into<PropertyKey>,
807    {
808        // Note: The spec specifies this function for JsValue.
809        // It is implemented for JsObject for convenience.
810
811        // 1. Assert: IsPropertyKey(P) is true.
812        // 2. Let func be ? GetV(V, P).
813
814        match self
815            .__get__(
816                &key.into(),
817                self.clone().into(),
818                &mut InternalMethodPropertyContext::new(context),
819            )?
820            .variant()
821        {
822            // 3. If func is either undefined or null, return undefined.
823            JsVariant::Undefined | JsVariant::Null => Ok(None),
824            // 5. Return func.
825            JsVariant::Object(obj) if obj.is_callable() => Ok(Some(obj.clone())),
826            // 4. If IsCallable(func) is false, throw a TypeError exception.
827            _ => Err(JsNativeError::typ()
828                .with_message("value returned for property of object is not a function")
829                .into()),
830        }
831    }
832
833    /// Abstract operation `IsArray ( argument )`
834    ///
835    /// Check if a value is an array.
836    ///
837    /// More information:
838    ///  - [ECMAScript reference][spec]
839    ///
840    /// [spec]: https://tc39.es/ecma262/#sec-isarray
841    pub(crate) fn is_array_abstract(&self) -> JsResult<bool> {
842        // Note: The spec specifies this function for JsValue.
843        // It is implemented for JsObject for convenience.
844
845        // 2. If argument is an Array exotic object, return true.
846        if self.is_array() {
847            return Ok(true);
848        }
849
850        // 3. If argument is a Proxy exotic object, then
851        if let Some(proxy) = self.downcast_ref::<Proxy>() {
852            // a. If argument.[[ProxyHandler]] is null, throw a TypeError exception.
853            // b. Let target be argument.[[ProxyTarget]].
854            let (target, _) = proxy.try_data()?;
855
856            // c. Return ? IsArray(target).
857            return target.is_array_abstract();
858        }
859
860        // 4. Return false.
861        Ok(false)
862    }
863
864    /// Abstract operation [`GetFunctionRealm`][spec].
865    ///
866    /// [spec]: https://tc39.es/ecma262/#sec-getfunctionrealm
867    pub(crate) fn get_function_realm(&self, context: &mut Context) -> JsResult<Realm> {
868        if let Some(fun) = self.downcast_ref::<OrdinaryFunction>() {
869            return Ok(fun.realm().clone());
870        }
871
872        if let Some(f) = self.downcast_ref::<NativeFunctionObject>() {
873            return Ok(f.realm.clone().unwrap_or_else(|| context.realm().clone()));
874        }
875
876        if let Some(bound) = self.downcast_ref::<BoundFunction>() {
877            let fun = bound.target_function().clone();
878            return fun.get_function_realm(context);
879        }
880
881        if let Some(proxy) = self.downcast_ref::<Proxy>() {
882            let (fun, _) = proxy.try_data()?;
883            return fun.get_function_realm(context);
884        }
885
886        Ok(context.realm().clone())
887    }
888
889    /// `7.3.26 CopyDataProperties ( target, source, excludedItems )`
890    ///
891    /// More information:
892    ///  - [ECMAScript reference][spec]
893    ///
894    /// [spec]: https://tc39.es/ecma262/#sec-copydataproperties
895    pub fn copy_data_properties<K>(
896        &self,
897        source: &JsValue,
898        excluded_keys: Vec<K>,
899        context: &mut Context,
900    ) -> JsResult<()>
901    where
902        K: Into<PropertyKey>,
903    {
904        let context = &mut InternalMethodPropertyContext::new(context);
905
906        // 1. Assert: Type(target) is Object.
907        // 2. Assert: excludedItems is a List of property keys.
908        // 3. If source is undefined or null, return target.
909        if source.is_null_or_undefined() {
910            return Ok(());
911        }
912
913        // 4. Let from be ! ToObject(source).
914        let from = source
915            .to_object(context)
916            .expect("function ToObject should never complete abruptly here");
917
918        // 5. Let keys be ? from.[[OwnPropertyKeys]]().
919        // 6. For each element nextKey of keys, do
920        let excluded_keys: Vec<PropertyKey> = excluded_keys.into_iter().map(Into::into).collect();
921        for key in from.__own_property_keys__(context)? {
922            // a. Let excluded be false.
923            let mut excluded = false;
924
925            // b. For each element e of excludedItems, do
926            for e in &excluded_keys {
927                // i. If SameValue(e, nextKey) is true, then
928                if *e == key {
929                    // 1. Set excluded to true.
930                    excluded = true;
931                    break;
932                }
933            }
934            // c. If excluded is false, then
935            if !excluded {
936                // i. Let desc be ? from.[[GetOwnProperty]](nextKey).
937                let desc = from.__get_own_property__(&key, context)?;
938
939                // ii. If desc is not undefined and desc.[[Enumerable]] is true, then
940                if let Some(desc) = desc
941                    && let Some(enumerable) = desc.enumerable()
942                    && enumerable
943                {
944                    // 1. Let propValue be ? Get(from, nextKey).
945                    let prop_value = from.__get__(&key, from.clone().into(), context)?;
946
947                    // 2. Perform ! CreateDataPropertyOrThrow(target, nextKey, propValue).
948                    self.create_data_property_or_throw(key, prop_value, context)
949                        .expect("CreateDataPropertyOrThrow should never complete abruptly here");
950                }
951            }
952        }
953
954        // 7. Return target.
955        Ok(())
956    }
957
958    /// Abstract operation `PrivateElementFind ( O, P )`
959    ///
960    /// Get the private element from an object.
961    ///
962    /// More information:
963    ///  - [ECMAScript specification][spec]
964    ///
965    /// [spec]: https://tc39.es/ecma262/#sec-privateelementfind
966    #[allow(clippy::similar_names)]
967    pub(crate) fn private_element_find(
968        &self,
969        name: &PrivateName,
970        is_getter: bool,
971        is_setter: bool,
972    ) -> Option<PrivateElement> {
973        // 1. If O.[[PrivateElements]] contains a PrivateElement whose [[Key]] is P, then
974        for (key, value) in &self.borrow().private_elements {
975            if key == name {
976                // a. Let entry be that PrivateElement.
977                // b. Return entry.
978                if let PrivateElement::Accessor { getter, setter } = value {
979                    if getter.is_some() && is_getter || setter.is_some() && is_setter {
980                        return Some(value.clone());
981                    }
982                } else {
983                    return Some(value.clone());
984                }
985            }
986        }
987
988        // 2. Return empty.
989        None
990    }
991
992    /// Abstract operation `PrivateFieldAdd ( O, P, value )`
993    ///
994    /// Add private field to an object.
995    ///
996    /// More information:
997    ///  - [ECMAScript specification][spec]
998    ///
999    /// [spec]: https://tc39.es/ecma262/#sec-privatefieldadd
1000    pub(crate) fn private_field_add(
1001        &self,
1002        name: &PrivateName,
1003        value: JsValue,
1004        context: &mut Context,
1005    ) -> JsResult<()> {
1006        // 1. If the host is a web browser, then
1007        //    a. Perform ? HostEnsureCanAddPrivateElement(O).
1008        context
1009            .host_hooks()
1010            .ensure_can_add_private_element(self, context)?;
1011
1012        // 2. If ? IsExtensible(O) is false, throw a TypeError exception.
1013        // NOTE: From <https://tc39.es/proposal-nonextensible-applies-to-private/#sec-privatefieldadd>
1014        if !self.is_extensible(context)? {
1015            return Err(js_error!(
1016                TypeError: "cannot add private field to non-extensible class instance"
1017            ));
1018        }
1019
1020        // 3. Let entry be PrivateElementFind(O, P).
1021        let entry = self.private_element_find(name, false, false);
1022
1023        // 4. If entry is not empty, throw a TypeError exception.
1024        if entry.is_some() {
1025            return Err(js_error!(TypeError: "private field already exists on prototype"));
1026        }
1027
1028        // 5. Append PrivateElement { [[Key]]: P, [[Kind]]: field, [[Value]]: value } to O.[[PrivateElements]].
1029        self.borrow_mut()
1030            .private_elements
1031            .push((name.clone(), PrivateElement::Field(value)));
1032
1033        // 5. Return unused.
1034        Ok(())
1035    }
1036
1037    /// Abstract operation `PrivateMethodOrAccessorAdd ( O, method )`
1038    ///
1039    /// Add private method or accessor to an object.
1040    ///
1041    /// More information:
1042    ///  - [ECMAScript specification][spec]
1043    ///
1044    /// [spec]: https://tc39.es/ecma262/#sec-privatemethodoraccessoradd
1045    pub(crate) fn private_method_or_accessor_add(
1046        &self,
1047        name: &PrivateName,
1048        method: &PrivateElement,
1049        context: &mut Context,
1050    ) -> JsResult<()> {
1051        // 1. Assert: method.[[Kind]] is either method or accessor.
1052        assert!(matches!(
1053            method,
1054            PrivateElement::Method(_) | PrivateElement::Accessor { .. }
1055        ));
1056
1057        let (getter, setter) = if let PrivateElement::Accessor { getter, setter } = method {
1058            (getter.is_some(), setter.is_some())
1059        } else {
1060            (false, false)
1061        };
1062
1063        // 2. If the host is a web browser, then
1064        // a. Perform ? HostEnsureCanAddPrivateElement(O).
1065        context
1066            .host_hooks()
1067            .ensure_can_add_private_element(self, context)?;
1068
1069        // 3. If ? IsExtensible(O) is false, throw a TypeError exception.
1070        // NOTE: From <https://tc39.es/proposal-nonextensible-applies-to-private/#sec-privatemethodoraccessoradd>
1071        if !self.is_extensible(context)? {
1072            return if getter || setter {
1073                Err(js_error!(
1074                    TypeError: "cannot add private accessor to non-extensible class instance"
1075                ))
1076            } else {
1077                Err(js_error!(
1078                    TypeError: "cannot add private method to non-extensible class instance"
1079                ))
1080            };
1081        }
1082
1083        // 3. Let entry be PrivateElementFind(O, method.[[Key]]).
1084        let entry = self.private_element_find(name, getter, setter);
1085
1086        // 4. If entry is not empty, throw a TypeError exception.
1087        if entry.is_some() {
1088            return if getter || setter {
1089                Err(js_error!(
1090                    TypeError: "private accessor already exists on class instance"
1091                ))
1092            } else {
1093                Err(js_error!(
1094                    TypeError: "private method already exists on class instance"
1095                ))
1096            };
1097        }
1098
1099        // 5. Append method to O.[[PrivateElements]].
1100        self.borrow_mut()
1101            .append_private_element(name.clone(), method.clone());
1102
1103        // 6. Return unused.
1104        Ok(())
1105    }
1106
1107    /// Abstract operation `PrivateGet ( O, P )`
1108    ///
1109    /// Get the value of a private element.
1110    ///
1111    /// More information:
1112    ///  - [ECMAScript specification][spec]
1113    ///
1114    /// [spec]: https://tc39.es/ecma262/#sec-privateget
1115    pub(crate) fn private_get(
1116        &self,
1117        name: &PrivateName,
1118        context: &mut Context,
1119    ) -> JsResult<JsValue> {
1120        // 1. Let entry be PrivateElementFind(O, P).
1121        let entry = self.private_element_find(name, true, true);
1122
1123        match &entry {
1124            // 2. If entry is empty, throw a TypeError exception.
1125            None => Err(JsNativeError::typ()
1126                .with_message("Private element does not exist on object")
1127                .into()),
1128
1129            // 3. If entry.[[Kind]] is field or method, then
1130            // a. Return entry.[[Value]].
1131            Some(PrivateElement::Field(value)) => Ok(value.clone()),
1132            Some(PrivateElement::Method(value)) => Ok(value.clone().into()),
1133
1134            // 4. Assert: entry.[[Kind]] is accessor.
1135            Some(PrivateElement::Accessor { getter, .. }) => {
1136                // 5. If entry.[[Get]] is undefined, throw a TypeError exception.
1137                // 6. Let getter be entry.[[Get]].
1138                let getter = getter.as_ref().ok_or_else(|| {
1139                    JsNativeError::typ()
1140                        .with_message("private property was defined without a getter")
1141                })?;
1142
1143                // 7. Return ? Call(getter, O).
1144                getter.call(&self.clone().into(), &[], context)
1145            }
1146        }
1147    }
1148
1149    /// Abstract operation `PrivateSet ( O, P, value )`
1150    ///
1151    /// Set the value of a private element.
1152    ///
1153    /// More information:
1154    ///  - [ECMAScript specification][spec]
1155    ///
1156    /// [spec]: https://tc39.es/ecma262/#sec-privateset
1157    pub(crate) fn private_set(
1158        &self,
1159        name: &PrivateName,
1160        value: JsValue,
1161        context: &mut Context,
1162    ) -> JsResult<()> {
1163        // 1. Let entry be PrivateElementFind(O, P).
1164        // Note: This function is inlined here for mutable access.
1165        let mut object_mut = self.borrow_mut();
1166        let entry = object_mut
1167            .private_elements
1168            .iter_mut()
1169            .find_map(|(key, value)| if key == name { Some(value) } else { None });
1170
1171        match entry {
1172            // 2. If entry is empty, throw a TypeError exception.
1173            None => {
1174                return Err(JsNativeError::typ()
1175                    .with_message("Private element does not exist on object")
1176                    .into());
1177            }
1178
1179            // 3. If entry.[[Kind]] is field, then
1180            // a. Set entry.[[Value]] to value.
1181            Some(PrivateElement::Field(field)) => {
1182                *field = value;
1183            }
1184
1185            // 4. Else if entry.[[Kind]] is method, then
1186            // a. Throw a TypeError exception.
1187            Some(PrivateElement::Method(_)) => {
1188                return Err(JsNativeError::typ()
1189                    .with_message("private method is not writable")
1190                    .into());
1191            }
1192
1193            // 5. Else,
1194            Some(PrivateElement::Accessor { setter, .. }) => {
1195                // a. Assert: entry.[[Kind]] is accessor.
1196                // b. If entry.[[Set]] is undefined, throw a TypeError exception.
1197                // c. Let setter be entry.[[Set]].
1198                let setter = setter.clone().ok_or_else(|| {
1199                    JsNativeError::typ()
1200                        .with_message("private property was defined without a setter")
1201                })?;
1202
1203                // d. Perform ? Call(setter, O, « value »).
1204                drop(object_mut);
1205                setter.call(&self.clone().into(), &[value], context)?;
1206            }
1207        }
1208
1209        // 6. Return unused.
1210        Ok(())
1211    }
1212
1213    /// Abstract operation `DefineField ( receiver, fieldRecord )`
1214    ///
1215    /// Define a field on an object.
1216    ///
1217    /// More information:
1218    ///  - [ECMAScript specification][spec]
1219    ///
1220    /// [spec]: https://tc39.es/ecma262/#sec-definefield
1221    pub(crate) fn define_field(
1222        &self,
1223        field_record: &ClassFieldDefinition,
1224        context: &mut Context,
1225    ) -> JsResult<()> {
1226        // 2. Let initializer be fieldRecord.[[Initializer]].
1227        let initializer = match field_record {
1228            ClassFieldDefinition::Public(_, function, _)
1229            | ClassFieldDefinition::Private(_, function) => function,
1230        };
1231
1232        // 3. If initializer is not empty, then
1233        // a. Let initValue be ? Call(initializer, receiver).
1234        // 4. Else, let initValue be undefined.
1235        let init_value = initializer.call(&self.clone().into(), &[], context)?;
1236
1237        match field_record {
1238            // 1. Let fieldName be fieldRecord.[[Name]].
1239            // 5. If fieldName is a Private Name, then
1240            ClassFieldDefinition::Private(field_name, _) => {
1241                // a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue).
1242                self.private_field_add(field_name, init_value, context)?;
1243            }
1244            // 1. Let fieldName be fieldRecord.[[Name]].
1245            // 6. Else,
1246            ClassFieldDefinition::Public(field_name, _, function_name) => {
1247                if let Some(function_name) = function_name {
1248                    set_function_name(
1249                        &init_value
1250                            .as_object()
1251                            .js_expect("init value must be a function object")?,
1252                        function_name,
1253                        None,
1254                        context,
1255                    )?;
1256                }
1257
1258                // a. Assert: IsPropertyKey(fieldName) is true.
1259                // b. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue).
1260                self.create_data_property_or_throw(field_name.clone(), init_value, context)?;
1261            }
1262        }
1263
1264        // 7. Return unused.
1265        Ok(())
1266    }
1267
1268    /// Abstract operation `InitializeInstanceElements ( O, constructor )`
1269    ///
1270    /// Add private methods and fields from a class constructor to an object.
1271    ///
1272    /// More information:
1273    ///  - [ECMAScript specification][spec]
1274    ///
1275    /// [spec]: https://tc39.es/ecma262/#sec-initializeinstanceelements
1276    pub(crate) fn initialize_instance_elements(
1277        &self,
1278        constructor: &Self,
1279        context: &mut Context,
1280    ) -> JsResult<()> {
1281        let constructor_function = constructor
1282            .downcast_ref::<OrdinaryFunction>()
1283            .js_expect("class constructor must be function object")?;
1284
1285        // 1. Let methods be the value of constructor.[[PrivateMethods]].
1286        // 2. For each PrivateElement method of methods, do
1287        for (name, method) in constructor_function.get_private_methods() {
1288            // a. Perform ? PrivateMethodOrAccessorAdd(O, method).
1289            self.private_method_or_accessor_add(name, method, context)?;
1290        }
1291
1292        // 3. Let fields be the value of constructor.[[Fields]].
1293        // 4. For each element fieldRecord of fields, do
1294        for field_record in constructor_function.get_fields() {
1295            // a. Perform ? DefineField(O, fieldRecord).
1296            self.define_field(field_record, context)?;
1297        }
1298
1299        // 5. Return unused.
1300        Ok(())
1301    }
1302
1303    /// Abstract operation `Invoke ( V, P [ , argumentsList ] )`
1304    ///
1305    /// Calls a method property of an ECMAScript object.
1306    ///
1307    /// Equivalent to the [`JsValue::invoke`] method, but specialized for objects.
1308    ///
1309    /// More information:
1310    /// - [ECMAScript reference][spec]
1311    ///
1312    /// [spec]: https://tc39.es/ecma262/#sec-invoke
1313    pub(crate) fn invoke<K>(
1314        &self,
1315        key: K,
1316        args: &[JsValue],
1317        context: &mut Context,
1318    ) -> JsResult<JsValue>
1319    where
1320        K: Into<PropertyKey>,
1321    {
1322        let this_value: JsValue = self.clone().into();
1323
1324        // 1. If argumentsList is not present, set argumentsList to a new empty List.
1325        // 2. Let func be ? GetV(V, P).
1326        let func = self.__get__(
1327            &key.into(),
1328            this_value.clone(),
1329            &mut InternalMethodPropertyContext::new(context),
1330        )?;
1331
1332        // 3. Return ? Call(func, V, argumentsList)
1333        func.call(&this_value, args, context)
1334    }
1335}
1336
1337impl JsValue {
1338    /// Abstract operation `GetV ( V, P )`.
1339    ///
1340    /// Retrieves the value of a specific property of an ECMAScript language value. If the value is
1341    /// not an object, the property lookup is performed using a wrapper object appropriate for the
1342    /// type of the value.
1343    ///
1344    /// More information:
1345    /// - [ECMAScript reference][spec]
1346    ///
1347    /// [spec]: https://tc39.es/ecma262/#sec-getv
1348    pub(crate) fn get_v<K>(&self, key: K, context: &mut Context) -> JsResult<Self>
1349    where
1350        K: Into<PropertyKey>,
1351    {
1352        // 1. Let O be ? ToObject(V).
1353        let o = self.to_object(context)?;
1354
1355        // 2. Return ? O.[[Get]](P, V).
1356
1357        o.__get__(
1358            &key.into(),
1359            self.clone(),
1360            &mut InternalMethodPropertyContext::new(context),
1361        )
1362    }
1363
1364    /// Abstract operation `GetMethod ( V, P )`
1365    ///
1366    /// Retrieves the value of a specific property, when the value of the property is expected to be a function.
1367    ///
1368    /// More information:
1369    /// - [ECMAScript reference][spec]
1370    ///
1371    /// [spec]: https://tc39.es/ecma262/#sec-getmethod
1372    pub(crate) fn get_method<K>(&self, key: K, context: &mut Context) -> JsResult<Option<JsObject>>
1373    where
1374        K: Into<PropertyKey>,
1375    {
1376        // Note: The spec specifies this function for JsValue.
1377        // The main part of the function is implemented for JsObject.
1378        // 1. Let func be ? GetV(V, P).
1379        match self.get_v(key, context)?.variant() {
1380            // 3. If func is either undefined or null, return undefined.
1381            JsVariant::Undefined | JsVariant::Null => Ok(None),
1382            // 5. Return func.
1383            JsVariant::Object(obj) if obj.is_callable() => Ok(Some(obj.clone())),
1384            // 4. If IsCallable(func) is false, throw a TypeError exception.
1385            _ => Err(JsNativeError::typ()
1386                .with_message("value returned for property of object is not a function")
1387                .into()),
1388        }
1389    }
1390
1391    /// It is used to create List value whose elements are provided by the indexed properties of
1392    /// self.
1393    ///
1394    /// More information:
1395    /// - [ECMAScript reference][spec]
1396    ///
1397    /// [spec]: https://tc39.es/ecma262/#sec-createlistfromarraylike
1398    pub(crate) fn create_list_from_array_like(
1399        &self,
1400        element_types: &[Type],
1401        context: &mut Context,
1402    ) -> JsResult<Vec<Self>> {
1403        // 1. If elementTypes is not present, set elementTypes to « Undefined, Null, Boolean, String, Symbol, Number, BigInt, Object ».
1404        let types = if element_types.is_empty() {
1405            &[
1406                Type::Undefined,
1407                Type::Null,
1408                Type::Boolean,
1409                Type::String,
1410                Type::Symbol,
1411                Type::Number,
1412                Type::BigInt,
1413                Type::Object,
1414            ]
1415        } else {
1416            element_types
1417        };
1418
1419        // 2. If Type(obj) is not Object, throw a TypeError exception.
1420        let obj = self.as_object().ok_or_else(|| {
1421            JsNativeError::typ().with_message("cannot create list from a primitive")
1422        })?;
1423
1424        // 3. Let len be ? LengthOfArrayLike(obj).
1425        let len = obj.length_of_array_like(context)?;
1426
1427        // 4. Let list be a new empty List.
1428        let mut list = Vec::with_capacity(len as usize);
1429
1430        // 5. Let index be 0.
1431        // 6. Repeat, while index < len,
1432        for index in 0..len {
1433            // a. Let indexName be ! ToString(𝔽(index)).
1434            // b. Let next be ? Get(obj, indexName).
1435            let next = obj.get(index, context)?;
1436            // c. If Type(next) is not an element of elementTypes, throw a TypeError exception.
1437            if !types.contains(&next.get_type()) {
1438                return Err(JsNativeError::typ().with_message("bad type").into());
1439            }
1440            // d. Append next as the last element of list.
1441            list.push(next.clone());
1442            // e. Set index to index + 1.
1443        }
1444
1445        // 7. Return list.
1446        Ok(list)
1447    }
1448
1449    /// Abstract operation [`Call ( F, V [ , argumentsList ] )`][call].
1450    ///
1451    /// Calls this value if the value is a callable object.
1452    ///
1453    /// # Note
1454    ///
1455    /// It is almost always better to try to obtain a callable object first with [`JsValue::as_callable`],
1456    /// then calling [`JsObject::call`], since that allows reusing the unwrapped function for other
1457    /// operations. This method is only an utility method for when the spec directly uses `Call`
1458    /// without using the value as a proper object.
1459    ///
1460    /// [call]: https://tc39.es/ecma262/#sec-call
1461    #[inline]
1462    #[cfg_attr(feature = "native-backtrace", track_caller)]
1463    pub(crate) fn call(&self, this: &Self, args: &[Self], context: &mut Context) -> JsResult<Self> {
1464        let Some(object) = self.as_object() else {
1465            return Err(JsNativeError::typ()
1466                .with_message(format!(
1467                    "value with type `{}` is not callable",
1468                    self.type_of()
1469                ))
1470                .into());
1471        };
1472
1473        object.call(this, args, context)
1474    }
1475
1476    /// Abstract operation `( V, P [ , argumentsList ] )`
1477    ///
1478    /// Calls a method property of an ECMAScript language value.
1479    ///
1480    /// More information:
1481    /// - [ECMAScript reference][spec]
1482    ///
1483    /// [spec]: https://tc39.es/ecma262/#sec-invoke
1484    #[cfg_attr(feature = "native-backtrace", track_caller)]
1485    pub(crate) fn invoke<K>(&self, key: K, args: &[Self], context: &mut Context) -> JsResult<Self>
1486    where
1487        K: Into<PropertyKey>,
1488    {
1489        // 1. If argumentsList is not present, set argumentsList to a new empty List.
1490        // 2. Let func be ? GetV(V, P).
1491        let func = self.get_v(key, context)?;
1492
1493        // 3. Return ? Call(func, V, argumentsList)
1494        func.call(self, args, context)
1495    }
1496
1497    /// Abstract operation `OrdinaryHasInstance ( C, O )`
1498    ///
1499    /// More information:
1500    /// - [ECMAScript reference][spec]
1501    ///
1502    /// [spec]: https://tc39.es/ecma262/#sec-ordinaryhasinstance
1503    pub fn ordinary_has_instance(
1504        function: &Self,
1505        object: &Self,
1506        context: &mut Context,
1507    ) -> JsResult<bool> {
1508        // 1. If IsCallable(C) is false, return false.
1509        let Some(function) = function.as_callable() else {
1510            return Ok(false);
1511        };
1512
1513        // 2. If C has a [[BoundTargetFunction]] internal slot, then
1514        if let Some(bound_function) = function.downcast_ref::<BoundFunction>() {
1515            // a. Let BC be C.[[BoundTargetFunction]].
1516            // b. Return ? InstanceofOperator(O, BC).
1517            return object.instance_of(&bound_function.target_function().clone().into(), context);
1518        }
1519
1520        let Some(mut object) = object.as_object() else {
1521            // 3. If Type(O) is not Object, return false.
1522            return Ok(false);
1523        };
1524
1525        // 4. Let P be ? Get(C, "prototype").
1526        let prototype = function.get(PROTOTYPE, context)?;
1527
1528        // 5. If Type(P) is not Object, throw a TypeError exception.
1529        let prototype = prototype.as_object().ok_or_else(|| {
1530            JsNativeError::typ()
1531                .with_message("function has non-object prototype in instanceof check")
1532        })?;
1533
1534        // 6. Repeat,
1535        loop {
1536            // a. Set O to ? O.[[GetPrototypeOf]]().
1537            object = match object
1538                .__get_prototype_of__(&mut InternalMethodPropertyContext::new(context))?
1539            {
1540                Some(obj) => obj,
1541                // b. If O is null, return false.
1542                None => return Ok(false),
1543            };
1544
1545            // c. If SameValue(P, O) is true, return true.
1546            if JsObject::equals(&object, &prototype) {
1547                return Ok(true);
1548            }
1549        }
1550    }
1551}