Rust编写的JavaScript引擎,该项目是一个试验性质的项目。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

1063 lines
31 KiB

//! Boa's representation of a JavaScript object and builtin object wrappers
//!
//! For the builtin object wrappers, please see [`object::builtins`][builtins] for implementors.
pub use jsobject::{RecursionLimiter, Ref, RefMut};
pub use operations::IntegrityLevel;
pub use property_map::*;
use thin_vec::ThinVec;
use self::{internal_methods::ORDINARY_INTERNAL_METHODS, shape::Shape};
use crate::{
builtins::{
function::{
arguments::{MappedArguments, UnmappedArguments},
ConstructorKind,
},
typed_array::{TypedArray, TypedArrayKind},
OrdinaryObject,
},
context::intrinsics::StandardConstructor,
js_string,
native_function::{NativeFunction, NativeFunctionObject},
property::{Attribute, PropertyDescriptor, PropertyKey},
realm::Realm,
string::{common::StaticJsStrings, utf16},
Context, JsString, JsSymbol, JsValue,
};
use boa_gc::{Finalize, Trace};
use std::{
any::{Any, TypeId},
fmt::Debug,
};
#[cfg(test)]
mod tests;
pub(crate) mod internal_methods;
pub mod builtins;
mod datatypes;
mod jsobject;
mod operations;
mod property_map;
pub mod shape;
pub(crate) use builtins::*;
pub use datatypes::JsData;
pub use jsobject::*;
pub(crate) trait JsObjectType: Into<JsValue> + Into<JsObject> {}
/// Const `constructor`, usually set on prototypes as a key to point to their respective constructor object.
pub const CONSTRUCTOR: &[u16] = utf16!("constructor");
/// Const `prototype`, usually set on constructors as a key to point to their respective prototype object.
pub const PROTOTYPE: &[u16] = utf16!("prototype");
/// Common field names.
/// A type alias for an object prototype.
///
/// A `None` values means that the prototype is the `null` value.
pub type JsPrototype = Option<JsObject>;
/// The internal storage of an object's property values.
///
/// The [`shape::Shape`] contains the property names and attributes.
pub(crate) type ObjectStorage = Vec<JsValue>;
/// This trait allows Rust types to be passed around as objects.
///
/// This is automatically implemented when a type implements `Any`, `Trace`, and `JsData`.
pub trait NativeObject: Any + Trace + JsData {
/// Convert the Rust type which implements `NativeObject` to a `&dyn Any`.
fn as_any(&self) -> &dyn Any;
/// Convert the Rust type which implements `NativeObject` to a `&mut dyn Any`.
fn as_mut_any(&mut self) -> &mut dyn Any;
/// Gets the type name of the value.
fn type_name_of_value(&self) -> &'static str;
}
// TODO: Use super trait casting in Rust 1.75
impl<T: Any + Trace + JsData> NativeObject for T {
fn as_any(&self) -> &dyn Any {
self
}
fn as_mut_any(&mut self) -> &mut dyn Any {
self
}
fn type_name_of_value(&self) -> &'static str {
fn name_of_val<T: ?Sized>(_val: &T) -> &'static str {
std::any::type_name::<T>()
}
name_of_val(self)
}
}
// TODO: Use super trait casting in Rust 1.75
impl dyn NativeObject {
/// Returns `true` if the inner type is the same as `T`.
#[inline]
pub fn is<T: NativeObject>(&self) -> bool {
// Get `TypeId` of the type this function is instantiated with.
let t = TypeId::of::<T>();
// Get `TypeId` of the type in the trait object (`self`).
let concrete = self.type_id();
// Compare both `TypeId`s on equality.
t == concrete
}
/// Returns some reference to the inner value if it is of type `T`, or
/// `None` if it isn't.
#[inline]
pub fn downcast_ref<T: NativeObject>(&self) -> Option<&T> {
if self.is::<T>() {
// SAFETY: just checked whether we are pointing to the correct type, and we can rely on
// that check for memory safety because we have implemented NativeObject for all types; no other
// impls can exist as they would conflict with our impl.
unsafe { Some(self.downcast_ref_unchecked()) }
} else {
None
}
}
/// Returns some mutable reference to the inner value if it is of type `T`, or
/// `None` if it isn't.
#[inline]
pub fn downcast_mut<T: NativeObject>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
// SAFETY: Already checked if inner type is T, so this is safe.
unsafe { Some(self.downcast_mut_unchecked()) }
} else {
None
}
}
/// Returns a reference to the inner value as type `dyn T`.
///
/// # Safety
///
/// The contained value must be of type `T`. Calling this method
/// with the incorrect type is *undefined behavior*.
#[inline]
pub unsafe fn downcast_ref_unchecked<T: NativeObject>(&self) -> &T {
debug_assert!(self.is::<T>());
let ptr: *const dyn NativeObject = self;
// SAFETY: caller guarantees that T is the correct type
unsafe { &*ptr.cast::<T>() }
}
/// Returns a mutable reference to the inner value as type `dyn T`.
///
/// # Safety
///
/// The contained value must be of type `T`. Calling this method
/// with the incorrect type is *undefined behavior*.
#[inline]
pub unsafe fn downcast_mut_unchecked<T: NativeObject>(&mut self) -> &mut T {
debug_assert!(self.is::<T>());
// SAFETY: caller guarantees that T is the correct type
let ptr: *mut dyn NativeObject = self;
unsafe { &mut *ptr.cast::<T>() }
}
}
/// The internal representation of a JavaScript object.
#[derive(Debug, Finalize, Trace)]
// SAFETY: This does not implement drop, so this is safe.
#[boa_gc(unsafe_no_drop)]
pub struct Object<T: ?Sized> {
/// The collection of properties contained in the object
pub(crate) properties: PropertyMap,
/// Whether it can have new properties added to it.
pub(crate) extensible: bool,
/// The `[[PrivateElements]]` internal slot.
private_elements: ThinVec<(PrivateName, PrivateElement)>,
/// The inner object data
pub(crate) data: T,
}
impl<T: Default> Default for Object<T> {
fn default() -> Self {
Self {
properties: PropertyMap::default(),
extensible: true,
private_elements: ThinVec::new(),
data: T::default(),
}
}
}
/// A Private Name.
#[derive(Clone, Debug, PartialEq, Eq, Trace, Finalize)]
pub struct PrivateName {
/// The `[[Description]]` internal slot of the private name.
description: JsString,
/// The unique identifier of the private name.
id: usize,
}
impl PrivateName {
/// Create a new private name.
pub(crate) const fn new(description: JsString, id: usize) -> Self {
Self { description, id }
}
}
/// The representation of private object elements.
#[derive(Clone, Debug, Trace, Finalize)]
pub enum PrivateElement {
/// A private field.
Field(JsValue),
/// A private method.
Method(JsObject),
/// A private element accessor.
Accessor {
/// A getter function.
getter: Option<JsObject>,
/// A setter function.
setter: Option<JsObject>,
},
}
impl<T: ?Sized> Object<T> {
/// Returns the shape of the object.
#[must_use]
pub const fn shape(&self) -> &Shape {
&self.properties.shape
}
/// Returns the data of the object.
#[inline]
#[must_use]
pub const fn data(&self) -> &T {
&self.data
}
/// Gets the prototype instance of this object.
#[inline]
#[must_use]
pub fn prototype(&self) -> JsPrototype {
self.properties.shape.prototype()
}
/// Sets the prototype instance of the object.
///
/// [More information][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-invariants-of-the-essential-internal-methods
#[track_caller]
pub fn set_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> bool {
let prototype = prototype.into();
if self.extensible {
self.properties.shape = self.properties.shape.change_prototype_transition(prototype);
true
} else {
// If target is non-extensible, [[SetPrototypeOf]] must return false
// unless V is the SameValue as the target's observed [[GetPrototypeOf]] value.
self.prototype() == prototype
}
}
/// Returns the properties of the object.
#[inline]
#[must_use]
pub const fn properties(&self) -> &PropertyMap {
&self.properties
}
#[inline]
pub(crate) fn properties_mut(&mut self) -> &mut PropertyMap {
&mut self.properties
}
/// Inserts a field in the object `properties` without checking if it's writable.
///
/// If a field was already in the object with the same name, then `true` is returned
/// otherwise, `false` is returned.
pub(crate) fn insert<K, P>(&mut self, key: K, property: P) -> bool
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
self.properties.insert(&key.into(), property.into())
}
/// Helper function for property removal without checking if it's configurable.
///
/// Returns `true` if the property was removed, `false` otherwise.
#[inline]
pub(crate) fn remove(&mut self, key: &PropertyKey) -> bool {
self.properties.remove(key)
}
/// Append a private element to an object.
pub(crate) fn append_private_element(&mut self, name: PrivateName, element: PrivateElement) {
if let PrivateElement::Accessor { getter, setter } = &element {
for (key, value) in &mut self.private_elements {
if name == *key {
if let PrivateElement::Accessor {
getter: existing_getter,
setter: existing_setter,
} = value
{
if existing_getter.is_none() {
*existing_getter = getter.clone();
}
if existing_setter.is_none() {
*existing_setter = setter.clone();
}
return;
}
}
}
}
self.private_elements.push((name, element));
}
}
impl Object<dyn NativeObject> {
/// Return `true` if it is a native object and the native type is `T`.
#[must_use]
pub fn is<T: NativeObject>(&self) -> bool {
self.data.is::<T>()
}
/// Downcast a reference to the object,
/// if the object is type native object type `T`.
#[must_use]
pub fn downcast_ref<T: NativeObject>(&self) -> Option<&T> {
self.data.downcast_ref::<T>()
}
/// Downcast a mutable reference to the object,
/// if the object is type native object type `T`.
pub fn downcast_mut<T: NativeObject>(&mut self) -> Option<&mut T> {
self.data.downcast_mut::<T>()
}
/// Checks if this object is an `Arguments` object.
pub(crate) fn is_arguments(&self) -> bool {
self.is::<UnmappedArguments>() || self.is::<MappedArguments>()
}
/// Checks if it a `Uint8Array` object.
#[inline]
#[must_use]
pub fn is_typed_uint8_array(&self) -> bool {
if let Some(int) = self.downcast_ref::<TypedArray>() {
matches!(int.kind(), TypedArrayKind::Uint8)
} else {
false
}
}
/// Checks if it a `Int8Array` object.
#[inline]
#[must_use]
pub fn is_typed_int8_array(&self) -> bool {
if let Some(int) = self.downcast_ref::<TypedArray>() {
matches!(int.kind(), TypedArrayKind::Int8)
} else {
false
}
}
/// Checks if it a `Uint16Array` object.
#[inline]
#[must_use]
pub fn is_typed_uint16_array(&self) -> bool {
if let Some(int) = self.downcast_ref::<TypedArray>() {
matches!(int.kind(), TypedArrayKind::Uint16)
} else {
false
}
}
/// Checks if it a `Int16Array` object.
#[inline]
#[must_use]
pub fn is_typed_int16_array(&self) -> bool {
if let Some(int) = self.downcast_ref::<TypedArray>() {
matches!(int.kind(), TypedArrayKind::Int16)
} else {
false
}
}
/// Checks if it a `Uint32Array` object.
#[inline]
#[must_use]
pub fn is_typed_uint32_array(&self) -> bool {
if let Some(int) = self.downcast_ref::<TypedArray>() {
matches!(int.kind(), TypedArrayKind::Uint32)
} else {
false
}
}
/// Checks if it a `Int32Array` object.
#[inline]
#[must_use]
pub fn is_typed_int32_array(&self) -> bool {
if let Some(int) = self.downcast_ref::<TypedArray>() {
matches!(int.kind(), TypedArrayKind::Int32)
} else {
false
}
}
/// Checks if it a `Float32Array` object.
#[inline]
#[must_use]
pub fn is_typed_float32_array(&self) -> bool {
if let Some(int) = self.downcast_ref::<TypedArray>() {
matches!(int.kind(), TypedArrayKind::Float32)
} else {
false
}
}
/// Checks if it a `Float64Array` object.
#[inline]
#[must_use]
pub fn is_typed_float64_array(&self) -> bool {
if let Some(int) = self.downcast_ref::<TypedArray>() {
matches!(int.kind(), TypedArrayKind::Float64)
} else {
false
}
}
}
/// The functions binding.
///
/// Specifies what is the name of the function object (`name` property),
/// and the binding name of the function object which can be different
/// from the function name.
///
/// The only way to construct this is with the `From` trait.
///
/// There are two implementations:
/// - From a single type `T` which implements `Into<FunctionBinding>` which sets the binding
/// name and the function name to the same value.
/// - From a tuple `(B: Into<PropertyKey>, N: Into<JsString>)`, where the `B` is the binding name
/// and the `N` is the function name.
#[derive(Debug, Clone)]
pub struct FunctionBinding {
pub(crate) binding: PropertyKey,
pub(crate) name: JsString,
}
impl From<JsString> for FunctionBinding {
#[inline]
fn from(name: JsString) -> Self {
Self {
binding: name.clone().into(),
name,
}
}
}
impl From<JsSymbol> for FunctionBinding {
#[inline]
fn from(binding: JsSymbol) -> Self {
Self {
name: binding.fn_name(),
binding: binding.into(),
}
}
}
impl<B, N> From<(B, N)> for FunctionBinding
where
B: Into<PropertyKey>,
N: Into<JsString>,
{
fn from((binding, name): (B, N)) -> Self {
Self {
binding: binding.into(),
name: name.into(),
}
}
}
/// Builder for creating native function objects
#[derive(Debug)]
pub struct FunctionObjectBuilder<'realm> {
realm: &'realm Realm,
function: NativeFunction,
constructor: Option<ConstructorKind>,
name: JsString,
length: usize,
}
impl<'realm> FunctionObjectBuilder<'realm> {
/// Create a new `FunctionBuilder` for creating a native function.
#[inline]
#[must_use]
pub fn new(realm: &'realm Realm, function: NativeFunction) -> Self {
Self {
realm,
function,
constructor: None,
name: js_string!(),
length: 0,
}
}
/// Specify the name property of object function object.
///
/// The default is `""` (empty string).
#[must_use]
pub fn name<N>(mut self, name: N) -> Self
where
N: Into<JsString>,
{
self.name = name.into();
self
}
/// Specify the length property of object function object.
///
/// How many arguments this function takes.
///
/// The default is `0`.
#[inline]
#[must_use]
pub const fn length(mut self, length: usize) -> Self {
self.length = length;
self
}
/// Specify whether the object function object can be called with `new` keyword.
///
/// The default is `false`.
#[must_use]
pub fn constructor(mut self, yes: bool) -> Self {
self.constructor = yes.then_some(ConstructorKind::Base);
self
}
/// Build the function object.
#[must_use]
pub fn build(self) -> JsFunction {
let object = self.realm.intrinsics().templates().function().create(
NativeFunctionObject {
f: self.function,
constructor: self.constructor,
realm: Some(self.realm.clone()),
},
vec![self.length.into(), self.name.into()],
);
JsFunction::from_object_unchecked(object)
}
}
/// Builder for creating objects with properties.
///
/// # Examples
///
/// ```
/// # use boa_engine::{
/// # Context,
/// # JsValue,
/// # NativeFunction,
/// # object::ObjectInitializer,
/// # property::Attribute,
/// # js_string,
/// # };
/// let mut context = Context::default();
/// let object = ObjectInitializer::new(&mut context)
/// .property(js_string!("hello"), js_string!("world"), Attribute::all())
/// .property(1, 1, Attribute::all())
/// .function(
/// NativeFunction::from_fn_ptr(|_, _, _| Ok(JsValue::undefined())),
/// js_string!("func"),
/// 0,
/// )
/// .build();
/// ```
///
/// The equivalent in JavaScript would be:
/// ```text
/// let object = {
/// hello: "world",
/// "1": 1,
/// func: function() {}
/// }
/// ```
#[derive(Debug)]
pub struct ObjectInitializer<'ctx> {
context: &'ctx mut Context,
object: JsObject,
}
impl<'ctx> ObjectInitializer<'ctx> {
/// Create a new `ObjectBuilder`.
#[inline]
pub fn new(context: &'ctx mut Context) -> Self {
let object = JsObject::with_object_proto(context.intrinsics());
Self { context, object }
}
/// Create a new `ObjectBuilder` with custom [`NativeObject`] data.
pub fn with_native_data<T: NativeObject>(data: T, context: &'ctx mut Context) -> Self {
let object = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
context.intrinsics().constructors().object().prototype(),
data,
);
Self { context, object }
}
/// Create a new `ObjectBuilder` with custom [`NativeObject`] data and custom prototype.
pub fn with_native_data_and_proto<T: NativeObject>(
data: T,
proto: JsObject,
context: &'ctx mut Context,
) -> Self {
let object =
JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), proto, data);
Self { context, object }
}
/// Add a function to the object.
pub fn function<B>(&mut self, function: NativeFunction, binding: B, length: usize) -> &mut Self
where
B: Into<FunctionBinding>,
{
let binding = binding.into();
let function = FunctionObjectBuilder::new(self.context.realm(), function)
.name(binding.name)
.length(length)
.constructor(false)
.build();
self.object.borrow_mut().insert(
binding.binding,
PropertyDescriptor::builder()
.value(function)
.writable(true)
.enumerable(false)
.configurable(true),
);
self
}
/// Add a property to the object.
pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where
K: Into<PropertyKey>,
V: Into<JsValue>,
{
let property = PropertyDescriptor::builder()
.value(value)
.writable(attribute.writable())
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.object.borrow_mut().insert(key, property);
self
}
/// Add new accessor property to the object.
///
/// # Panics
///
/// If both getter or setter are [`None`].
pub fn accessor<K>(
&mut self,
key: K,
get: Option<JsFunction>,
set: Option<JsFunction>,
attribute: Attribute,
) -> &mut Self
where
K: Into<PropertyKey>,
{
// Accessors should have at least one function.
assert!(set.is_some() || get.is_some());
let property = PropertyDescriptor::builder()
.maybe_get(get)
.maybe_set(set)
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.object.borrow_mut().insert(key, property);
self
}
/// Build the object.
#[inline]
pub fn build(&mut self) -> JsObject {
self.object.clone()
}
/// Gets the context used to create the object.
#[inline]
pub fn context(&mut self) -> &mut Context {
self.context
}
}
/// Builder for creating constructors objects, like `Array`.
#[derive(Debug)]
pub struct ConstructorBuilder<'ctx> {
context: &'ctx mut Context,
function: NativeFunction,
constructor_object: Object<OrdinaryObject>,
has_prototype_property: bool,
prototype: Object<OrdinaryObject>,
name: JsString,
length: usize,
callable: bool,
kind: Option<ConstructorKind>,
inherit: Option<JsPrototype>,
custom_prototype: Option<JsPrototype>,
}
impl<'ctx> ConstructorBuilder<'ctx> {
/// Create a new `ConstructorBuilder`.
#[inline]
pub fn new(context: &'ctx mut Context, function: NativeFunction) -> ConstructorBuilder<'ctx> {
Self {
context,
function,
constructor_object: Object {
data: OrdinaryObject,
properties: PropertyMap::default(),
extensible: true,
private_elements: ThinVec::new(),
},
prototype: Object {
data: OrdinaryObject,
properties: PropertyMap::default(),
extensible: true,
private_elements: ThinVec::new(),
},
length: 0,
name: js_string!(),
callable: true,
kind: Some(ConstructorKind::Base),
inherit: None,
custom_prototype: None,
has_prototype_property: true,
}
}
/// Add new method to the constructors prototype.
pub fn method<B>(&mut self, function: NativeFunction, binding: B, length: usize) -> &mut Self
where
B: Into<FunctionBinding>,
{
let binding = binding.into();
let function = FunctionObjectBuilder::new(self.context.realm(), function)
.name(binding.name)
.length(length)
.constructor(false)
.build();
self.prototype.insert(
binding.binding,
PropertyDescriptor::builder()
.value(function)
.writable(true)
.enumerable(false)
.configurable(true),
);
self
}
/// Add new static method to the constructors object itself.
pub fn static_method<B>(
&mut self,
function: NativeFunction,
binding: B,
length: usize,
) -> &mut Self
where
B: Into<FunctionBinding>,
{
let binding = binding.into();
let function = FunctionObjectBuilder::new(self.context.realm(), function)
.name(binding.name)
.length(length)
.constructor(false)
.build();
self.constructor_object.insert(
binding.binding,
PropertyDescriptor::builder()
.value(function)
.writable(true)
.enumerable(false)
.configurable(true),
);
self
}
/// Add new data property to the constructor's prototype.
pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where
K: Into<PropertyKey>,
V: Into<JsValue>,
{
let property = PropertyDescriptor::builder()
.value(value)
.writable(attribute.writable())
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.prototype.insert(key, property);
self
}
/// Add new static data property to the constructor object itself.
pub fn static_property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where
K: Into<PropertyKey>,
V: Into<JsValue>,
{
let property = PropertyDescriptor::builder()
.value(value)
.writable(attribute.writable())
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.constructor_object.insert(key, property);
self
}
/// Add new accessor property to the constructor's prototype.
pub fn accessor<K>(
&mut self,
key: K,
get: Option<JsFunction>,
set: Option<JsFunction>,
attribute: Attribute,
) -> &mut Self
where
K: Into<PropertyKey>,
{
let property = PropertyDescriptor::builder()
.maybe_get(get)
.maybe_set(set)
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.prototype.insert(key, property);
self
}
/// Add new static accessor property to the constructor object itself.
pub fn static_accessor<K>(
&mut self,
key: K,
get: Option<JsFunction>,
set: Option<JsFunction>,
attribute: Attribute,
) -> &mut Self
where
K: Into<PropertyKey>,
{
let property = PropertyDescriptor::builder()
.maybe_get(get)
.maybe_set(set)
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.constructor_object.insert(key, property);
self
}
/// Add new property to the constructor's prototype.
pub fn property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
let property = property.into();
self.prototype.insert(key, property);
self
}
/// Add new static property to the constructor object itself.
pub fn static_property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
let property = property.into();
self.constructor_object.insert(key, property);
self
}
/// Specify how many arguments the constructor function takes.
///
/// Default is `0`.
#[inline]
pub fn length(&mut self, length: usize) -> &mut Self {
self.length = length;
self
}
/// Specify the name of the constructor function.
///
/// Default is `"[object]"`
pub fn name<N>(&mut self, name: N) -> &mut Self
where
N: AsRef<str>,
{
self.name = name.as_ref().into();
self
}
/// Specify whether the constructor function can be called.
///
/// Default is `true`
#[inline]
pub fn callable(&mut self, callable: bool) -> &mut Self {
self.callable = callable;
self
}
/// Specify whether the constructor function can be called with `new` keyword.
///
/// Default is `true`
#[inline]
pub fn constructor(&mut self, constructor: bool) -> &mut Self {
self.kind = constructor.then_some(ConstructorKind::Base);
self
}
/// Specify the parent prototype which objects created by this constructor
/// inherit from.
///
/// Default is `Object.prototype`
pub fn inherit<O: Into<JsPrototype>>(&mut self, prototype: O) -> &mut Self {
self.inherit = Some(prototype.into());
self
}
/// Specify the `[[Prototype]]` internal field of this constructor.
///
/// Default is `Function.prototype`
pub fn custom_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> &mut Self {
self.custom_prototype = Some(prototype.into());
self
}
/// Specify whether the constructor function has a 'prototype' property.
///
/// Default is `true`
#[inline]
pub fn has_prototype_property(&mut self, has_prototype_property: bool) -> &mut Self {
self.has_prototype_property = has_prototype_property;
self
}
/// Return the current context.
#[inline]
pub fn context(&mut self) -> &mut Context {
self.context
}
/// Build the constructor function object.
#[must_use]
pub fn build(mut self) -> StandardConstructor {
let length = PropertyDescriptor::builder()
.value(self.length)
.writable(false)
.enumerable(false)
.configurable(true);
let name = PropertyDescriptor::builder()
.value(self.name.clone())
.writable(false)
.enumerable(false)
.configurable(true);
let prototype = {
if let Some(proto) = self.inherit.take() {
self.prototype.set_prototype(proto);
} else {
self.prototype.set_prototype(
self.context
.intrinsics()
.constructors()
.object()
.prototype(),
);
}
JsObject::from_object_and_vtable(self.prototype, &ORDINARY_INTERNAL_METHODS)
};
let constructor = {
let mut constructor = Object {
properties: self.constructor_object.properties,
extensible: self.constructor_object.extensible,
private_elements: self.constructor_object.private_elements,
data: NativeFunctionObject {
f: self.function,
constructor: self.kind,
realm: Some(self.context.realm().clone()),
},
};
constructor.insert(StaticJsStrings::LENGTH, length);
constructor.insert(utf16!("name"), name);
if let Some(proto) = self.custom_prototype.take() {
constructor.set_prototype(proto);
} else {
constructor.set_prototype(
self.context
.intrinsics()
.constructors()
.function()
.prototype(),
);
}
if self.has_prototype_property {
constructor.insert(
PROTOTYPE,
PropertyDescriptor::builder()
.value(prototype.clone())
.writable(false)
.enumerable(false)
.configurable(false),
);
}
let internal_methods = constructor.data.internal_methods();
JsObject::from_object_and_vtable(constructor, internal_methods)
};
{
let mut prototype = prototype.borrow_mut();
prototype.insert(
CONSTRUCTOR,
PropertyDescriptor::builder()
.value(constructor.clone())
.writable(true)
.enumerable(false)
.configurable(true),
);
}
StandardConstructor::new(JsFunction::from_object_unchecked(constructor), prototype)
}
}