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