Skip to main content

boa_engine/object/
mod.rs

1//! Boa's representation of a JavaScript object and builtin object wrappers
2//!
3//! For the builtin object wrappers, please see [`object::builtins`][builtins] for implementors.
4
5use datatypes::ObjectData;
6pub use jsobject::{RecursionLimiter, Ref, RefMut};
7pub use operations::IntegrityLevel;
8pub use property_map::*;
9use thin_vec::ThinVec;
10
11use self::{internal_methods::ORDINARY_INTERNAL_METHODS, shape::Shape};
12use crate::{
13    Context, JsString, JsSymbol, JsValue,
14    builtins::{OrdinaryObject, function::ConstructorKind},
15    context::intrinsics::StandardConstructor,
16    js_string,
17    native_function::{NativeFunction, NativeFunctionObject},
18    property::{Attribute, PropertyDescriptor, PropertyKey},
19    realm::Realm,
20    string::StaticJsStrings,
21};
22
23use boa_gc::{Finalize, Trace};
24use std::{
25    any::{Any, TypeId},
26    fmt::Debug,
27};
28
29#[cfg(test)]
30mod tests;
31
32pub(crate) mod internal_methods;
33
34pub mod builtins;
35mod datatypes;
36mod jsobject;
37mod operations;
38mod property_map;
39
40pub mod shape;
41
42pub(crate) use builtins::*;
43
44pub use datatypes::JsData;
45pub use jsobject::*;
46
47/// Const `constructor`, usually set on prototypes as a key to point to their respective constructor object.
48pub const CONSTRUCTOR: JsString = js_string!("constructor");
49
50/// Const `prototype`, usually set on constructors as a key to point to their respective prototype object.
51pub const PROTOTYPE: JsString = js_string!("prototype");
52
53/// A type alias for an object prototype.
54///
55/// A `None` values means that the prototype is the `null` value.
56pub type JsPrototype = Option<JsObject>;
57
58/// The internal storage of an object's property values.
59///
60/// The [`shape::Shape`] contains the property names and attributes.
61pub(crate) type ObjectStorage = Vec<JsValue>;
62
63/// This trait allows Rust types to be passed around as objects.
64///
65/// This is automatically implemented when a type implements `Any`, `Trace`, and `JsData`.
66pub trait NativeObject: Any + Trace + JsData {
67    /// Convert the Rust type which implements `NativeObject` to a `&dyn Any`.
68    fn as_any(&self) -> &dyn Any;
69
70    /// Convert the Rust type which implements `NativeObject` to a `&mut dyn Any`.
71    fn as_mut_any(&mut self) -> &mut dyn Any;
72
73    /// Gets the type name of the value.
74    fn type_name_of_value(&self) -> &'static str;
75}
76
77// TODO: Use super trait casting in Rust 1.75
78impl<T: Any + Trace + JsData> NativeObject for T {
79    fn as_any(&self) -> &dyn Any {
80        self
81    }
82
83    fn as_mut_any(&mut self) -> &mut dyn Any {
84        self
85    }
86
87    fn type_name_of_value(&self) -> &'static str {
88        fn name_of_val<T: ?Sized>(_val: &T) -> &'static str {
89            std::any::type_name::<T>()
90        }
91
92        name_of_val(self)
93    }
94}
95
96// TODO: Use super trait casting in Rust 1.75
97impl dyn NativeObject {
98    /// Returns `true` if the inner type is the same as `T`.
99    #[inline]
100    pub fn is<T: NativeObject>(&self) -> bool {
101        // Get `TypeId` of the type this function is instantiated with.
102        let t = TypeId::of::<T>();
103
104        // Get `TypeId` of the type in the trait object (`self`).
105        let concrete = self.type_id();
106
107        // Compare both `TypeId`s on equality.
108        t == concrete
109    }
110
111    /// Returns some reference to the inner value if it is of type `T`, or
112    /// `None` if it isn't.
113    #[inline]
114    pub fn downcast_ref<T: NativeObject>(&self) -> Option<&T> {
115        if self.is::<T>() {
116            // SAFETY: just checked whether we are pointing to the correct type, and we can rely on
117            // that check for memory safety because we have implemented NativeObject for all types; no other
118            // impls can exist as they would conflict with our impl.
119            unsafe { Some(self.downcast_ref_unchecked()) }
120        } else {
121            None
122        }
123    }
124
125    /// Returns some mutable reference to the inner value if it is of type `T`, or
126    /// `None` if it isn't.
127    #[inline]
128    pub fn downcast_mut<T: NativeObject>(&mut self) -> Option<&mut T> {
129        if self.is::<T>() {
130            // SAFETY: Already checked if inner type is T, so this is safe.
131            unsafe { Some(self.downcast_mut_unchecked()) }
132        } else {
133            None
134        }
135    }
136
137    /// Returns a reference to the inner value as type `dyn T`.
138    ///
139    /// # Safety
140    ///
141    /// The contained value must be of type `T`. Calling this method
142    /// with the incorrect type is *undefined behavior*.
143    #[inline]
144    pub unsafe fn downcast_ref_unchecked<T: NativeObject>(&self) -> &T {
145        debug_assert!(self.is::<T>());
146        let ptr: *const dyn NativeObject = self;
147        // SAFETY: caller guarantees that T is the correct type
148        unsafe { &*ptr.cast::<T>() }
149    }
150
151    /// Returns a mutable reference to the inner value as type `dyn T`.
152    ///
153    /// # Safety
154    ///
155    /// The contained value must be of type `T`. Calling this method
156    /// with the incorrect type is *undefined behavior*.
157    #[inline]
158    pub unsafe fn downcast_mut_unchecked<T: NativeObject>(&mut self) -> &mut T {
159        debug_assert!(self.is::<T>());
160        // SAFETY: caller guarantees that T is the correct type
161        let ptr: *mut dyn NativeObject = self;
162        unsafe { &mut *ptr.cast::<T>() }
163    }
164}
165
166/// The internal representation of a JavaScript object.
167#[derive(Debug, Finalize, Trace)]
168// SAFETY: This does not implement drop, so this is safe.
169#[boa_gc(unsafe_no_drop)]
170// SAFETY: This type must use `#[repr(C)]` to prevent the compiler from reordering fields,
171//         as it is used for casting between types.
172#[repr(C)]
173pub struct Object<T: ?Sized> {
174    /// The collection of properties contained in the object
175    pub(crate) properties: PropertyMap,
176    /// Whether it can have new properties added to it.
177    pub(crate) extensible: bool,
178    /// The `[[PrivateElements]]` internal slot.
179    private_elements: ThinVec<(PrivateName, PrivateElement)>,
180    /// The inner object data
181    data: ObjectData<T>,
182}
183
184impl<T: Default> Default for Object<T> {
185    fn default() -> Self {
186        Self {
187            properties: PropertyMap::default(),
188            extensible: true,
189            private_elements: ThinVec::new(),
190            data: ObjectData::default(),
191        }
192    }
193}
194
195/// A Private Name.
196#[derive(Clone, Debug, PartialEq, Eq, Trace, Finalize)]
197pub struct PrivateName {
198    /// The `[[Description]]` internal slot of the private name.
199    description: JsString,
200
201    /// The unique identifier of the private name.
202    id: usize,
203}
204
205impl PrivateName {
206    /// Create a new private name.
207    pub(crate) const fn new(description: JsString, id: usize) -> Self {
208        Self { description, id }
209    }
210}
211
212/// The representation of private object elements.
213#[derive(Clone, Debug, Trace, Finalize)]
214pub enum PrivateElement {
215    /// A private field.
216    Field(JsValue),
217
218    /// A private method.
219    Method(JsObject),
220
221    /// A private element accessor.
222    Accessor {
223        /// A getter function.
224        getter: Option<JsObject>,
225
226        /// A setter function.
227        setter: Option<JsObject>,
228    },
229}
230
231impl<T: ?Sized> Object<T> {
232    /// Returns the shape of the object.
233    #[must_use]
234    pub const fn shape(&self) -> &Shape {
235        &self.properties.shape
236    }
237
238    /// Returns the data of the object.
239    #[inline]
240    #[must_use]
241    pub fn data(&self) -> &T {
242        self.data.as_ref()
243    }
244
245    /// Returns the data of the object.
246    #[inline]
247    #[must_use]
248    pub fn data_mut(&mut self) -> &mut T {
249        self.data.as_mut()
250    }
251
252    /// Gets the prototype instance of this object.
253    #[inline]
254    #[must_use]
255    pub fn prototype(&self) -> JsPrototype {
256        self.properties.shape.prototype()
257    }
258
259    /// Sets the prototype instance of the object.
260    ///
261    /// [More information][spec]
262    ///
263    /// [spec]: https://tc39.es/ecma262/#sec-invariants-of-the-essential-internal-methods
264    #[track_caller]
265    pub fn set_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> bool {
266        let prototype = prototype.into();
267        if self.extensible {
268            self.properties.shape = self.properties.shape.change_prototype_transition(prototype);
269            true
270        } else {
271            // If target is non-extensible, [[SetPrototypeOf]] must return false
272            // unless V is the SameValue as the target's observed [[GetPrototypeOf]] value.
273            self.prototype() == prototype
274        }
275    }
276
277    /// Returns the properties of the object.
278    #[inline]
279    #[must_use]
280    pub const fn properties(&self) -> &PropertyMap {
281        &self.properties
282    }
283
284    #[inline]
285    pub(crate) fn properties_mut(&mut self) -> &mut PropertyMap {
286        &mut self.properties
287    }
288
289    /// Inserts a field in the object `properties` without checking if it's writable.
290    ///
291    /// If a field was already in the object with the same name, then `true` is returned
292    /// otherwise, `false` is returned.
293    pub(crate) fn insert<K, P>(&mut self, key: K, property: P) -> bool
294    where
295        K: Into<PropertyKey>,
296        P: Into<PropertyDescriptor>,
297    {
298        self.properties.insert(&key.into(), property.into())
299    }
300
301    /// Helper function for property removal without checking if it's configurable.
302    ///
303    /// Returns `true` if the property was removed, `false` otherwise.
304    #[inline]
305    pub(crate) fn remove(&mut self, key: &PropertyKey) -> bool {
306        self.properties.remove(key)
307    }
308
309    /// Append a private element to an object.
310    pub(crate) fn append_private_element(&mut self, name: PrivateName, element: PrivateElement) {
311        if let PrivateElement::Accessor { getter, setter } = &element {
312            for (key, value) in &mut self.private_elements {
313                if name == *key
314                    && let PrivateElement::Accessor {
315                        getter: existing_getter,
316                        setter: existing_setter,
317                    } = value
318                {
319                    if existing_getter.is_none() {
320                        existing_getter.clone_from(getter);
321                    }
322                    if existing_setter.is_none() {
323                        existing_setter.clone_from(setter);
324                    }
325                    return;
326                }
327            }
328        }
329
330        self.private_elements.push((name, element));
331    }
332}
333
334/// The functions binding.
335///
336/// Specifies what is the name of the function object (`name` property),
337/// and the binding name of the function object which can be different
338/// from the function name.
339///
340/// The only way to construct this is with the `From` trait.
341///
342/// There are two implementations:
343///  - From a single type `T` which implements `Into<FunctionBinding>` which sets the binding
344///    name and the function name to the same value.
345///  - From a tuple `(B: Into<PropertyKey>, N: Into<JsString>)`, where the `B` is the binding name
346///    and the `N` is the function name.
347#[derive(Debug, Clone)]
348pub struct FunctionBinding {
349    pub(crate) binding: PropertyKey,
350    pub(crate) name: JsString,
351}
352
353impl From<JsString> for FunctionBinding {
354    #[inline]
355    fn from(name: JsString) -> Self {
356        Self {
357            binding: name.clone().into(),
358            name,
359        }
360    }
361}
362
363impl From<JsSymbol> for FunctionBinding {
364    #[inline]
365    fn from(binding: JsSymbol) -> Self {
366        Self {
367            name: binding.fn_name(),
368            binding: binding.into(),
369        }
370    }
371}
372
373impl<B, N> From<(B, N)> for FunctionBinding
374where
375    B: Into<PropertyKey>,
376    N: Into<JsString>,
377{
378    fn from((binding, name): (B, N)) -> Self {
379        Self {
380            binding: binding.into(),
381            name: name.into(),
382        }
383    }
384}
385
386/// Builder for creating native function objects
387#[derive(Debug)]
388pub struct FunctionObjectBuilder<'realm> {
389    realm: &'realm Realm,
390    function: NativeFunction,
391    constructor: Option<ConstructorKind>,
392    name: JsString,
393    length: usize,
394}
395
396impl<'realm> FunctionObjectBuilder<'realm> {
397    /// Create a new `FunctionBuilder` for creating a native function.
398    #[inline]
399    #[must_use]
400    pub fn new(realm: &'realm Realm, function: NativeFunction) -> Self {
401        Self {
402            realm,
403            function,
404            constructor: None,
405            name: js_string!(),
406            length: 0,
407        }
408    }
409
410    /// Specify the name property of object function object.
411    ///
412    /// The default is `""` (empty string).
413    #[must_use]
414    pub fn name<N>(mut self, name: N) -> Self
415    where
416        N: Into<JsString>,
417    {
418        self.name = name.into();
419        self
420    }
421
422    /// Specify the length property of object function object.
423    ///
424    /// How many arguments this function takes.
425    ///
426    /// The default is `0`.
427    #[inline]
428    #[must_use]
429    pub const fn length(mut self, length: usize) -> Self {
430        self.length = length;
431        self
432    }
433
434    /// Specify whether the object function object can be called with `new` keyword.
435    ///
436    /// The default is `false`.
437    #[must_use]
438    pub fn constructor(mut self, yes: bool) -> Self {
439        self.constructor = yes.then_some(ConstructorKind::Base);
440        self
441    }
442
443    /// Build the function object.
444    #[must_use]
445    pub fn build(self) -> JsFunction {
446        let object = self.realm.intrinsics().templates().function().create(
447            NativeFunctionObject {
448                f: self.function,
449                name: self.name.clone(),
450                constructor: self.constructor,
451                realm: Some(self.realm.clone()),
452            },
453            vec![self.length.into(), self.name.into()],
454        );
455
456        JsFunction::from_object_unchecked(object)
457    }
458}
459
460/// Builder for creating objects with properties.
461///
462/// # Examples
463///
464/// ```
465/// # use boa_engine::{
466/// #     Context,
467/// #     JsValue,
468/// #     NativeFunction,
469/// #     object::ObjectInitializer,
470/// #     property::Attribute,
471/// #     js_string,
472/// # };
473/// let mut context = Context::default();
474/// let object = ObjectInitializer::new(&mut context)
475///     .property(js_string!("hello"), js_string!("world"), Attribute::all())
476///     .property(1, 1, Attribute::all())
477///     .function(
478///         NativeFunction::from_fn_ptr(|_, _, _| Ok(JsValue::undefined())),
479///         js_string!("func"),
480///         0,
481///     )
482///     .build();
483/// ```
484///
485/// The equivalent in JavaScript would be:
486/// ```text
487/// let object = {
488///     hello: "world",
489///     "1": 1,
490///     func: function() {}
491/// }
492/// ```
493#[derive(Debug)]
494pub struct ObjectInitializer<'ctx> {
495    context: &'ctx mut Context,
496    object: JsObject,
497}
498
499impl<'ctx> ObjectInitializer<'ctx> {
500    /// Create a new `ObjectBuilder`.
501    #[inline]
502    pub fn new(context: &'ctx mut Context) -> Self {
503        let object = JsObject::with_object_proto(context.intrinsics());
504        Self { context, object }
505    }
506
507    /// Create a new `ObjectBuilder` with custom [`NativeObject`] data.
508    pub fn with_native_data<T: NativeObject>(data: T, context: &'ctx mut Context) -> Self {
509        let object = JsObject::from_proto_and_data_with_shared_shape(
510            context.root_shape(),
511            context.intrinsics().constructors().object().prototype(),
512            data,
513        )
514        .upcast();
515        Self { context, object }
516    }
517
518    /// Create a new `ObjectBuilder` with custom [`NativeObject`] data and custom prototype.
519    pub fn with_native_data_and_proto<T: NativeObject>(
520        data: T,
521        proto: JsObject,
522        context: &'ctx mut Context,
523    ) -> Self {
524        let object =
525            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), proto, data)
526                .upcast();
527        Self { context, object }
528    }
529
530    /// Add a function to the object.
531    pub fn function<B>(&mut self, function: NativeFunction, binding: B, length: usize) -> &mut Self
532    where
533        B: Into<FunctionBinding>,
534    {
535        let binding = binding.into();
536        let function = FunctionObjectBuilder::new(self.context.realm(), function)
537            .name(binding.name)
538            .length(length)
539            .constructor(false)
540            .build();
541
542        self.object.borrow_mut().insert(
543            binding.binding,
544            PropertyDescriptor::builder()
545                .value(function)
546                .writable(true)
547                .enumerable(false)
548                .configurable(true),
549        );
550        self
551    }
552
553    /// Add a property to the object.
554    pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
555    where
556        K: Into<PropertyKey>,
557        V: Into<JsValue>,
558    {
559        let property = PropertyDescriptor::builder()
560            .value(value)
561            .writable(attribute.writable())
562            .enumerable(attribute.enumerable())
563            .configurable(attribute.configurable());
564        self.object.borrow_mut().insert(key, property);
565        self
566    }
567
568    /// Add new accessor property to the object.
569    ///
570    /// # Panics
571    ///
572    /// If both getter or setter are [`None`].
573    pub fn accessor<K>(
574        &mut self,
575        key: K,
576        get: Option<JsFunction>,
577        set: Option<JsFunction>,
578        attribute: Attribute,
579    ) -> &mut Self
580    where
581        K: Into<PropertyKey>,
582    {
583        // Accessors should have at least one function.
584        assert!(set.is_some() || get.is_some());
585
586        let property = PropertyDescriptor::builder()
587            .maybe_get(get)
588            .maybe_set(set)
589            .enumerable(attribute.enumerable())
590            .configurable(attribute.configurable());
591        self.object.borrow_mut().insert(key, property);
592        self
593    }
594
595    /// Build the object.
596    #[inline]
597    pub fn build(&mut self) -> JsObject {
598        self.object.clone()
599    }
600
601    /// Gets the context used to create the object.
602    #[inline]
603    pub fn context(&mut self) -> &mut Context {
604        self.context
605    }
606}
607
608/// Builder for creating constructors objects, like `Array`.
609#[derive(Debug)]
610pub struct ConstructorBuilder<'ctx> {
611    context: &'ctx mut Context,
612    function: NativeFunction,
613    constructor_object: Object<OrdinaryObject>,
614    has_prototype_property: bool,
615    prototype: Object<OrdinaryObject>,
616    name: JsString,
617    length: usize,
618    callable: bool,
619    kind: Option<ConstructorKind>,
620    inherit: Option<JsPrototype>,
621    custom_prototype: Option<JsPrototype>,
622}
623
624impl<'ctx> ConstructorBuilder<'ctx> {
625    /// Create a new `ConstructorBuilder`.
626    #[inline]
627    pub fn new(context: &'ctx mut Context, function: NativeFunction) -> ConstructorBuilder<'ctx> {
628        Self {
629            context,
630            function,
631            constructor_object: Object {
632                data: ObjectData::new(OrdinaryObject),
633                properties: PropertyMap::default(),
634                extensible: true,
635                private_elements: ThinVec::new(),
636            },
637            prototype: Object {
638                data: ObjectData::new(OrdinaryObject),
639                properties: PropertyMap::default(),
640                extensible: true,
641                private_elements: ThinVec::new(),
642            },
643            length: 0,
644            name: js_string!(),
645            callable: true,
646            kind: Some(ConstructorKind::Base),
647            inherit: None,
648            custom_prototype: None,
649            has_prototype_property: true,
650        }
651    }
652
653    /// Add new method to the constructors prototype.
654    pub fn method<B>(&mut self, function: NativeFunction, binding: B, length: usize) -> &mut Self
655    where
656        B: Into<FunctionBinding>,
657    {
658        let binding = binding.into();
659        let function = FunctionObjectBuilder::new(self.context.realm(), function)
660            .name(binding.name)
661            .length(length)
662            .constructor(false)
663            .build();
664
665        self.prototype.insert(
666            binding.binding,
667            PropertyDescriptor::builder()
668                .value(function)
669                .writable(true)
670                .enumerable(false)
671                .configurable(true),
672        );
673        self
674    }
675
676    /// Add new static method to the constructors object itself.
677    pub fn static_method<B>(
678        &mut self,
679        function: NativeFunction,
680        binding: B,
681        length: usize,
682    ) -> &mut Self
683    where
684        B: Into<FunctionBinding>,
685    {
686        let binding = binding.into();
687        let function = FunctionObjectBuilder::new(self.context.realm(), function)
688            .name(binding.name)
689            .length(length)
690            .constructor(false)
691            .build();
692
693        self.constructor_object.insert(
694            binding.binding,
695            PropertyDescriptor::builder()
696                .value(function)
697                .writable(true)
698                .enumerable(false)
699                .configurable(true),
700        );
701        self
702    }
703
704    /// Add new data property to the constructor's prototype.
705    pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
706    where
707        K: Into<PropertyKey>,
708        V: Into<JsValue>,
709    {
710        let property = PropertyDescriptor::builder()
711            .value(value)
712            .writable(attribute.writable())
713            .enumerable(attribute.enumerable())
714            .configurable(attribute.configurable());
715        self.prototype.insert(key, property);
716        self
717    }
718
719    /// Add new static data property to the constructor object itself.
720    pub fn static_property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
721    where
722        K: Into<PropertyKey>,
723        V: Into<JsValue>,
724    {
725        let property = PropertyDescriptor::builder()
726            .value(value)
727            .writable(attribute.writable())
728            .enumerable(attribute.enumerable())
729            .configurable(attribute.configurable());
730        self.constructor_object.insert(key, property);
731        self
732    }
733
734    /// Add new accessor property to the constructor's prototype.
735    pub fn accessor<K>(
736        &mut self,
737        key: K,
738        get: Option<JsFunction>,
739        set: Option<JsFunction>,
740        attribute: Attribute,
741    ) -> &mut Self
742    where
743        K: Into<PropertyKey>,
744    {
745        let property = PropertyDescriptor::builder()
746            .maybe_get(get)
747            .maybe_set(set)
748            .enumerable(attribute.enumerable())
749            .configurable(attribute.configurable());
750        self.prototype.insert(key, property);
751        self
752    }
753
754    /// Add new static accessor property to the constructor object itself.
755    pub fn static_accessor<K>(
756        &mut self,
757        key: K,
758        get: Option<JsFunction>,
759        set: Option<JsFunction>,
760        attribute: Attribute,
761    ) -> &mut Self
762    where
763        K: Into<PropertyKey>,
764    {
765        let property = PropertyDescriptor::builder()
766            .maybe_get(get)
767            .maybe_set(set)
768            .enumerable(attribute.enumerable())
769            .configurable(attribute.configurable());
770        self.constructor_object.insert(key, property);
771        self
772    }
773
774    /// Add new property to the constructor's prototype.
775    pub fn property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self
776    where
777        K: Into<PropertyKey>,
778        P: Into<PropertyDescriptor>,
779    {
780        let property = property.into();
781        self.prototype.insert(key, property);
782        self
783    }
784
785    /// Add new static property to the constructor object itself.
786    pub fn static_property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self
787    where
788        K: Into<PropertyKey>,
789        P: Into<PropertyDescriptor>,
790    {
791        let property = property.into();
792        self.constructor_object.insert(key, property);
793        self
794    }
795
796    /// Specify how many arguments the constructor function takes.
797    ///
798    /// Default is `0`.
799    #[inline]
800    pub fn length(&mut self, length: usize) -> &mut Self {
801        self.length = length;
802        self
803    }
804
805    /// Specify the name of the constructor function.
806    ///
807    /// Default is `"[object]"`
808    pub fn name<N>(&mut self, name: N) -> &mut Self
809    where
810        N: AsRef<str>,
811    {
812        self.name = name.as_ref().into();
813        self
814    }
815
816    /// Specify whether the constructor function can be called.
817    ///
818    /// Default is `true`
819    #[inline]
820    pub fn callable(&mut self, callable: bool) -> &mut Self {
821        self.callable = callable;
822        self
823    }
824
825    /// Specify whether the constructor function can be called with `new` keyword.
826    ///
827    /// Default is `true`
828    #[inline]
829    pub fn constructor(&mut self, constructor: bool) -> &mut Self {
830        self.kind = constructor.then_some(ConstructorKind::Base);
831        self
832    }
833
834    /// Specify the parent prototype which objects created by this constructor
835    /// inherit from.
836    ///
837    /// Default is `Object.prototype`
838    pub fn inherit<O: Into<JsPrototype>>(&mut self, prototype: O) -> &mut Self {
839        self.inherit = Some(prototype.into());
840        self
841    }
842
843    /// Specify the `[[Prototype]]` internal field of this constructor.
844    ///
845    /// Default is `Function.prototype`
846    pub fn custom_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> &mut Self {
847        self.custom_prototype = Some(prototype.into());
848        self
849    }
850
851    /// Specify whether the constructor function has a 'prototype' property.
852    ///
853    /// Default is `true`
854    #[inline]
855    pub fn has_prototype_property(&mut self, has_prototype_property: bool) -> &mut Self {
856        self.has_prototype_property = has_prototype_property;
857        self
858    }
859
860    /// Return the current context.
861    #[inline]
862    pub fn context(&mut self) -> &mut Context {
863        self.context
864    }
865
866    /// Build the constructor function object.
867    #[must_use]
868    pub fn build(mut self) -> StandardConstructor {
869        let length = PropertyDescriptor::builder()
870            .value(self.length)
871            .writable(false)
872            .enumerable(false)
873            .configurable(true);
874        let name = PropertyDescriptor::builder()
875            .value(self.name.clone())
876            .writable(false)
877            .enumerable(false)
878            .configurable(true);
879
880        let prototype = {
881            if let Some(proto) = self.inherit.take() {
882                self.prototype.set_prototype(proto);
883            } else {
884                self.prototype.set_prototype(
885                    self.context
886                        .intrinsics()
887                        .constructors()
888                        .object()
889                        .prototype(),
890                );
891            }
892
893            JsObject::from_object_and_vtable(self.prototype, &ORDINARY_INTERNAL_METHODS)
894        };
895
896        let constructor = {
897            let data = NativeFunctionObject {
898                f: self.function,
899                name: self.name.clone(),
900                constructor: self.kind,
901                realm: Some(self.context.realm().clone()),
902            };
903            let internal_methods = data.internal_methods();
904            let mut constructor = Object {
905                properties: self.constructor_object.properties,
906                extensible: self.constructor_object.extensible,
907                private_elements: self.constructor_object.private_elements,
908                data: ObjectData::new(data),
909            };
910
911            constructor.insert(StaticJsStrings::LENGTH, length);
912            constructor.insert(js_string!("name"), name);
913
914            if let Some(proto) = self.custom_prototype.take() {
915                constructor.set_prototype(proto);
916            } else {
917                constructor.set_prototype(
918                    self.context
919                        .intrinsics()
920                        .constructors()
921                        .function()
922                        .prototype(),
923                );
924            }
925
926            if self.has_prototype_property {
927                constructor.insert(
928                    PROTOTYPE,
929                    PropertyDescriptor::builder()
930                        .value(prototype.clone())
931                        .writable(false)
932                        .enumerable(false)
933                        .configurable(false),
934                );
935            }
936
937            JsObject::from_object_and_vtable(constructor, internal_methods)
938        };
939
940        {
941            let mut prototype = prototype.borrow_mut();
942            prototype.insert(
943                CONSTRUCTOR,
944                PropertyDescriptor::builder()
945                    .value(constructor.clone())
946                    .writable(true)
947                    .enumerable(false)
948                    .configurable(true),
949            );
950        }
951
952        StandardConstructor::new(JsFunction::from_object_unchecked(constructor), prototype)
953    }
954}