use crate::{
Context, JsResult, JsValue,
context::intrinsics::StandardConstructor,
error::JsNativeError,
native_function::NativeFunction,
object::{ConstructorBuilder, FunctionBinding, JsFunction, JsObject, NativeObject, PROTOTYPE},
property::{Attribute, PropertyDescriptor, PropertyKey},
};
pub trait Class: NativeObject + Sized {
const NAME: &'static str;
const LENGTH: usize = 0;
const ATTRIBUTES: Attribute = Attribute::all();
fn init(class: &mut ClassBuilder<'_>) -> JsResult<()>;
fn data_constructor(
new_target: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<Self>;
#[allow(unused_variables)] fn object_constructor(
instance: &JsObject,
args: &[JsValue],
context: &mut Context,
) -> JsResult<()> {
Ok(())
}
fn construct(
new_target: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsObject> {
if new_target.is_undefined() {
return Err(JsNativeError::typ()
.with_message(format!(
"cannot call constructor of native class `{}` without new",
Self::NAME
))
.into());
}
let prototype = 'proto: {
let realm = if let Some(constructor) = new_target.as_object() {
if let Some(proto) = constructor.get(PROTOTYPE, context)?.as_object() {
break 'proto proto.clone();
}
constructor.get_function_realm(context)?
} else {
context.realm().clone()
};
realm
.get_class::<Self>()
.ok_or_else(|| {
JsNativeError::typ().with_message(format!(
"could not find native class `{}` in the map of registered classes",
Self::NAME
))
})?
.prototype()
};
let data = Self::data_constructor(new_target, args, context)?;
let object =
JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, data);
Self::object_constructor(&object, args, context)?;
Ok(object)
}
fn from_data(data: Self, context: &mut Context) -> JsResult<JsObject> {
let prototype = context
.get_global_class::<Self>()
.ok_or_else(|| {
JsNativeError::typ().with_message(format!(
"could not find native class `{}` in the map of registered classes",
Self::NAME
))
})?
.prototype();
let object =
JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, data);
Self::object_constructor(&object, &[], context)?;
Ok(object)
}
}
#[derive(Debug)]
pub struct ClassBuilder<'ctx> {
builder: ConstructorBuilder<'ctx>,
}
impl<'ctx> ClassBuilder<'ctx> {
pub fn new<T>(context: &'ctx mut Context) -> Self
where
T: Class,
{
let mut builder = ConstructorBuilder::new(
context,
NativeFunction::from_fn_ptr(|t, a, c| T::construct(t, a, c).map(JsValue::from)),
);
builder.name(T::NAME);
builder.length(T::LENGTH);
Self { builder }
}
#[must_use]
pub fn build(self) -> StandardConstructor {
self.builder.build()
}
pub fn method<N>(&mut self, name: N, length: usize, function: NativeFunction) -> &mut Self
where
N: Into<FunctionBinding>,
{
self.builder.method(function, name, length);
self
}
pub fn static_method<N>(
&mut self,
name: N,
length: usize,
function: NativeFunction,
) -> &mut Self
where
N: Into<FunctionBinding>,
{
self.builder.static_method(function, name, length);
self
}
pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where
K: Into<PropertyKey>,
V: Into<JsValue>,
{
self.builder.property(key, value, attribute);
self
}
pub fn static_property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where
K: Into<PropertyKey>,
V: Into<JsValue>,
{
self.builder.static_property(key, value, attribute);
self
}
pub fn accessor<K>(
&mut self,
key: K,
get: Option<JsFunction>,
set: Option<JsFunction>,
attribute: Attribute,
) -> &mut Self
where
K: Into<PropertyKey>,
{
self.builder.accessor(key, get, set, attribute);
self
}
pub fn static_accessor<K>(
&mut self,
key: K,
get: Option<JsFunction>,
set: Option<JsFunction>,
attribute: Attribute,
) -> &mut Self
where
K: Into<PropertyKey>,
{
self.builder.static_accessor(key, get, set, attribute);
self
}
pub fn property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
self.builder.property_descriptor(key, property);
self
}
pub fn static_property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
self.builder.static_property_descriptor(key, property);
self
}
#[inline]
pub fn context(&mut self) -> &mut Context {
self.builder.context()
}
}