use crate::{
ecmascript::{
Agent, BUILTIN_STRING_MEMORY, Object, ObjectRecord, OrdinaryObject, PropertyKey, Realm,
Value,
builders::{NoDefinition, NoKey},
builtins::{
Builtin, BuiltinFunction, BuiltinGetter, BuiltinIntrinsic, BuiltinSetter,
ObjectShapeRecord,
},
},
engine::Bindable,
heap::{ArenaAccessMut, CreateHeapData, ElementDescriptor, ElementsVector},
};
use super::{builtin_function_builder::BuiltinFunctionBuilder, property_builder::PropertyBuilder};
#[doc(hidden)]
#[derive(Default, Clone, Copy)]
pub struct NoPrototype;
#[doc(hidden)]
#[derive(Clone, Copy)]
pub struct CreatorPrototype<T: Into<Object<'static>>>(pub(crate) T);
#[doc(hidden)]
#[derive(Default, Clone, Copy)]
pub struct NoProperties;
#[doc(hidden)]
pub type PropertyDefinition = (
PropertyKey<'static>,
Option<ElementDescriptor<'static>>,
Option<Value<'static>>,
);
#[doc(hidden)]
#[derive(Clone)]
pub struct CreatorProperties(pub(crate) Vec<PropertyDefinition>);
pub struct OrdinaryObjectBuilder<'agent, P, Pr> {
pub(crate) agent: &'agent mut Agent,
this: OrdinaryObject<'static>,
realm: Realm<'static>,
prototype: P,
extensible: bool,
properties: Pr,
}
impl<'agent> OrdinaryObjectBuilder<'agent, NoPrototype, NoProperties> {
#[must_use]
pub fn new(agent: &'agent mut Agent, realm: Realm<'static>) -> Self {
let this = OrdinaryObject::new_uninitialised(agent);
Self {
agent,
this,
realm,
prototype: NoPrototype,
extensible: true,
properties: NoProperties,
}
}
#[must_use]
pub(crate) fn new_intrinsic_object(
agent: &'agent mut Agent,
realm: Realm<'static>,
this: OrdinaryObject<'static>,
) -> Self {
Self {
agent,
this,
realm,
prototype: NoPrototype,
extensible: true,
properties: NoProperties,
}
}
}
impl<P, Pr> OrdinaryObjectBuilder<'_, P, Pr> {
#[must_use]
pub fn with_extensible(self, extensible: bool) -> Self {
Self {
agent: self.agent,
this: self.this,
realm: self.realm,
prototype: self.prototype,
extensible,
properties: self.properties,
}
}
}
impl<'agent, Pr> OrdinaryObjectBuilder<'agent, NoPrototype, Pr> {
#[must_use]
pub fn with_prototype<T: Into<Object<'static>>>(
self,
prototype: T,
) -> OrdinaryObjectBuilder<'agent, CreatorPrototype<T>, Pr> {
OrdinaryObjectBuilder {
agent: self.agent,
this: self.this,
realm: self.realm,
prototype: CreatorPrototype(prototype),
extensible: self.extensible,
properties: self.properties,
}
}
}
impl<'agent, P> OrdinaryObjectBuilder<'agent, P, NoProperties> {
#[must_use]
pub fn with_property_capacity(
self,
cap: usize,
) -> OrdinaryObjectBuilder<'agent, P, CreatorProperties> {
OrdinaryObjectBuilder {
agent: self.agent,
this: self.this,
realm: self.realm,
prototype: self.prototype,
extensible: self.extensible,
properties: CreatorProperties(Vec::with_capacity(cap)),
}
}
}
impl<P> OrdinaryObjectBuilder<'_, P, CreatorProperties> {
#[must_use]
pub fn with_data_property(mut self, key: PropertyKey<'static>, value: Value<'static>) -> Self {
self.properties.0.push((key, None, Some(value)));
OrdinaryObjectBuilder {
agent: self.agent,
this: self.this,
realm: self.realm,
prototype: self.prototype,
extensible: self.extensible,
properties: self.properties,
}
}
#[must_use]
pub fn with_property(
mut self,
creator: impl FnOnce(
PropertyBuilder<'_, NoKey, NoDefinition>,
) -> (
PropertyKey<'static>,
Option<ElementDescriptor<'static>>,
Option<Value<'static>>,
),
) -> Self {
let builder = PropertyBuilder::new(self.agent);
let property = creator(builder);
self.properties.0.push(property);
OrdinaryObjectBuilder {
agent: self.agent,
this: self.this,
realm: self.realm,
prototype: self.prototype,
extensible: self.extensible,
properties: self.properties,
}
}
#[must_use]
pub fn with_constructor_property(mut self, constructor: BuiltinFunction<'static>) -> Self {
let property = PropertyBuilder::new(self.agent)
.with_enumerable(false)
.with_key(BUILTIN_STRING_MEMORY.constructor.into())
.with_value(constructor.into())
.build();
self.properties.0.push(property);
OrdinaryObjectBuilder {
agent: self.agent,
this: self.this,
realm: self.realm,
prototype: self.prototype,
extensible: self.extensible,
properties: self.properties,
}
}
#[must_use]
pub fn with_builtin_function_property<T: Builtin>(mut self) -> Self {
let (value, key) = {
let mut builder = BuiltinFunctionBuilder::new::<T>(self.agent, self.realm);
let name = T::KEY.unwrap_or_else(|| PropertyKey::from(builder.get_name()));
(builder.build().into(), name)
};
let builder = PropertyBuilder::new(self.agent)
.with_key(key)
.with_configurable(T::CONFIGURABLE)
.with_enumerable(T::ENUMERABLE);
let property = if T::WRITABLE {
builder.with_value(value).build()
} else {
builder.with_value_readonly(value).build()
};
self.properties.0.push(property);
OrdinaryObjectBuilder {
agent: self.agent,
this: self.this,
realm: self.realm,
prototype: self.prototype,
extensible: self.extensible,
properties: self.properties,
}
}
#[must_use]
pub(crate) fn with_builtin_intrinsic_function_property<T: BuiltinIntrinsic>(mut self) -> Self {
let (value, key) = {
let mut builder =
BuiltinFunctionBuilder::new_intrinsic_function::<T>(self.agent, self.realm);
let name = T::KEY.unwrap_or_else(|| PropertyKey::from(builder.get_name()));
(builder.build().into(), name)
};
let builder = PropertyBuilder::new(self.agent)
.with_key(key)
.with_configurable(T::CONFIGURABLE)
.with_enumerable(T::ENUMERABLE);
let property = if T::WRITABLE {
builder.with_value(value).build()
} else {
builder.with_value_readonly(value).build()
};
self.properties.0.push(property);
OrdinaryObjectBuilder {
agent: self.agent,
this: self.this,
realm: self.realm,
prototype: self.prototype,
extensible: self.extensible,
properties: self.properties,
}
}
#[must_use]
pub(crate) fn with_builtin_function_getter_property<T: BuiltinGetter>(mut self) -> Self {
let getter_function = BuiltinFunctionBuilder::new_getter::<T>(self.agent, self.realm)
.build()
.into();
let property = PropertyBuilder::new(self.agent)
.with_key(T::KEY.unwrap())
.with_getter_function(getter_function)
.with_configurable(T::CONFIGURABLE)
.with_enumerable(T::ENUMERABLE)
.build();
self.properties.0.push(property);
OrdinaryObjectBuilder {
agent: self.agent,
this: self.this,
realm: self.realm,
prototype: self.prototype,
extensible: self.extensible,
properties: self.properties,
}
}
pub(crate) fn with_builtin_function_getter_setter_property<T: BuiltinGetter + BuiltinSetter>(
mut self,
) -> Self {
let getter_function = BuiltinFunctionBuilder::new_getter::<T>(self.agent, self.realm)
.build()
.into();
let setter_function = BuiltinFunctionBuilder::new_setter::<T>(self.agent, self.realm)
.build()
.into();
let property = PropertyBuilder::new(self.agent)
.with_key(T::KEY.unwrap())
.with_getter_and_setter_functions(getter_function, setter_function)
.with_configurable(T::CONFIGURABLE)
.with_enumerable(T::ENUMERABLE)
.build();
self.properties.0.push(property);
OrdinaryObjectBuilder {
agent: self.agent,
this: self.this,
realm: self.realm,
prototype: self.prototype,
extensible: self.extensible,
properties: self.properties,
}
}
}
impl OrdinaryObjectBuilder<'_, NoPrototype, NoProperties> {
pub fn build(self) -> OrdinaryObject<'static> {
create_intrinsic_backing_object(self.agent, self.this, None, vec![], self.extensible);
self.this
}
}
impl<T: Into<Object<'static>>> OrdinaryObjectBuilder<'_, CreatorPrototype<T>, NoProperties> {
pub fn build(self) -> OrdinaryObject<'static> {
create_intrinsic_backing_object(
self.agent,
self.this,
Some(self.prototype.0.into()),
vec![],
self.extensible,
);
self.this
}
}
impl OrdinaryObjectBuilder<'_, NoPrototype, CreatorProperties> {
pub fn build(self) -> OrdinaryObject<'static> {
create_intrinsic_backing_object(
self.agent,
self.this,
None,
self.properties.0,
self.extensible,
);
self.this
}
}
impl<T: Into<Object<'static>>> OrdinaryObjectBuilder<'_, CreatorPrototype<T>, CreatorProperties> {
pub fn build(self) -> OrdinaryObject<'static> {
create_intrinsic_backing_object(
self.agent,
self.this,
Some(self.prototype.0.into()),
self.properties.0,
self.extensible,
);
self.this
}
}
pub(super) fn create_intrinsic_backing_object(
agent: &mut Agent,
backing_object: OrdinaryObject,
prototype: Option<Object>,
properties: Vec<PropertyDefinition>,
extensible: bool,
) {
assert_eq!(properties.len(), properties.capacity());
{
let slice = properties.as_slice();
let duplicate = (1..slice.len()).find(|first_index| {
slice[*first_index..]
.iter()
.any(|(key, _, _)| *key == slice[first_index - 1].0)
});
if let Some(index) = duplicate {
panic!("Duplicate key found: {:?}", slice[index].0);
}
}
let properties_count = properties.len();
let (cap, index) = agent
.heap
.elements
.allocate_keys_with_capacity(properties_count)
.expect("Intrinsic object's property allocation failed");
let cap = cap.make_intrinsic();
let keys_memory = agent.heap.elements.get_keys_uninit_raw(cap, index);
for (slot, key) in keys_memory.iter_mut().zip(properties.iter().map(|e| e.0)) {
*slot = Some(key.unbind());
}
let mut shape = agent.heap.create(ObjectShapeRecord::create(
prototype,
index,
cap,
properties_count,
));
if !extensible {
shape.set_extensible(extensible);
}
let ElementsVector {
elements_index: values,
cap: object_cap,
len: object_len,
len_writable: _,
} = agent
.heap
.elements
.allocate_object_property_storage_from_entries_vec(properties)
.expect("Failed to create intrinsic backing object");
assert_eq!(object_cap, cap);
assert_eq!(object_len as usize, properties_count);
let slot = backing_object.get_mut(agent);
assert_eq!(slot, &ObjectRecord::BLANK);
*slot = ObjectRecord::new(shape, values);
}