use crate::{
builtins::function::{BuiltInFunction, Function, FunctionFlags, NativeFunction},
object::{GcObject, NativeObject, Object, ObjectData, PROTOTYPE},
property::{Attribute, Property, PropertyKey},
Context, Result, Value,
};
use std::fmt::Debug;
pub trait Class: NativeObject + Sized {
const NAME: &'static str;
const LENGTH: usize = 0;
const ATTRIBUTE: Attribute = Attribute::all();
fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Self>;
fn init(class: &mut ClassBuilder<'_>) -> Result<()>;
}
pub trait ClassConstructor: Class {
fn raw_constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value>
where
Self: Sized;
}
impl<T: Class> ClassConstructor for T {
fn raw_constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value>
where
Self: Sized,
{
let object_instance = Self::constructor(this, args, ctx)?;
this.set_data(ObjectData::NativeObject(Box::new(object_instance)));
Ok(this.clone())
}
}
#[derive(Debug)]
pub struct ClassBuilder<'context> {
context: &'context mut Context,
object: GcObject,
prototype: GcObject,
}
impl<'context> ClassBuilder<'context> {
pub(crate) fn new<T>(context: &'context mut Context) -> Self
where
T: ClassConstructor,
{
let global = context.global_object();
let prototype = {
let object_prototype = global.get_field("Object").get_field(PROTOTYPE);
let object = Object::create(object_prototype);
GcObject::new(object)
};
let function = Function::BuiltIn(
BuiltInFunction(T::raw_constructor),
FunctionFlags::CONSTRUCTABLE,
);
let mut constructor =
Object::function(function, global.get_field("Function").get_field(PROTOTYPE));
let length = Property::data_descriptor(
T::LENGTH.into(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
constructor.insert_property("length", length);
let name = Property::data_descriptor(
T::NAME.into(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
constructor.insert_property("name", name);
let constructor = GcObject::new(constructor);
prototype
.borrow_mut()
.insert_field("constructor", constructor.clone().into());
constructor
.borrow_mut()
.insert_field(PROTOTYPE, prototype.clone().into());
Self {
context,
object: constructor,
prototype,
}
}
pub(crate) fn build(self) -> GcObject {
self.object
}
pub fn method<N>(&mut self, name: N, length: usize, function: NativeFunction)
where
N: Into<String>,
{
let name = name.into();
let mut function = Object::function(
Function::BuiltIn(function.into(), FunctionFlags::CALLABLE),
self.context
.global_object()
.get_field("Function")
.get_field("prototype"),
);
function.insert_field("length", Value::from(length));
function.insert_field("name", Value::from(name.as_str()));
self.prototype
.borrow_mut()
.insert_field(name, Value::from(function));
}
pub fn static_method<N>(&mut self, name: N, length: usize, function: NativeFunction)
where
N: Into<String>,
{
let name = name.into();
let mut function = Object::function(
Function::BuiltIn(function.into(), FunctionFlags::CALLABLE),
self.context
.global_object()
.get_field("Function")
.get_field("prototype"),
);
function.insert_field("length", Value::from(length));
function.insert_field("name", Value::from(name.as_str()));
self.object
.borrow_mut()
.insert_field(name, Value::from(function));
}
#[inline]
pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute)
where
K: Into<PropertyKey>,
V: Into<Value>,
{
let property = Property::data_descriptor(value.into(), attribute | Attribute::default());
self.prototype
.borrow_mut()
.insert_property(key.into(), property);
}
#[inline]
pub fn static_property<K, V>(&mut self, key: K, value: V, attribute: Attribute)
where
K: Into<PropertyKey>,
V: Into<Value>,
{
let property = Property::data_descriptor(value.into(), attribute | Attribute::default());
self.object
.borrow_mut()
.insert_property(key.into(), property);
}
pub fn context(&mut self) -> &'_ mut Context {
self.context
}
}