use crate::{
builtins::{function::Function, map::ordered_map::OrderedMap, BigInt, Date, RegExp},
property::{Property, PropertyKey},
value::{RcBigInt, RcString, RcSymbol, Value},
BoaProfiler,
};
use gc::{Finalize, Trace};
use rustc_hash::FxHashMap;
use std::fmt::{Debug, Display, Error, Formatter};
use std::{any::Any, result::Result as StdResult};
mod gcobject;
mod internal_methods;
mod iter;
pub use gcobject::{GcObject, Ref, RefMut};
pub use iter::*;
pub static PROTOTYPE: &str = "prototype";
pub trait NativeObject: Debug + Any + Trace {
fn as_any(&self) -> &dyn Any;
fn as_mut_any(&mut self) -> &mut dyn Any;
}
impl<T: Any + Debug + Trace> NativeObject for T {
fn as_any(&self) -> &dyn Any {
self as &dyn Any
}
fn as_mut_any(&mut self) -> &mut dyn Any {
self as &mut dyn Any
}
}
#[derive(Debug, Trace, Finalize)]
pub struct Object {
pub data: ObjectData,
indexed_properties: FxHashMap<u32, Property>,
string_properties: FxHashMap<RcString, Property>,
symbol_properties: FxHashMap<RcSymbol, Property>,
prototype: Value,
extensible: bool,
}
#[derive(Debug, Trace, Finalize)]
pub enum ObjectData {
Array,
Map(OrderedMap<Value, Value>),
RegExp(Box<RegExp>),
BigInt(RcBigInt),
Boolean(bool),
Function(Function),
String(RcString),
Number(f64),
Symbol(RcSymbol),
Error,
Ordinary,
Date(Date),
Global,
NativeObject(Box<dyn NativeObject>),
}
impl Display for ObjectData {
fn fmt(&self, f: &mut Formatter<'_>) -> StdResult<(), Error> {
write!(
f,
"{}",
match self {
Self::Array => "Array",
Self::Function(_) => "Function",
Self::RegExp(_) => "RegExp",
Self::Map(_) => "Map",
Self::String(_) => "String",
Self::Symbol(_) => "Symbol",
Self::Error => "Error",
Self::Ordinary => "Ordinary",
Self::Boolean(_) => "Boolean",
Self::Number(_) => "Number",
Self::BigInt(_) => "BigInt",
Self::Date(_) => "Date",
Self::Global => "Global",
Self::NativeObject(_) => "NativeObject",
}
)
}
}
impl Default for Object {
#[inline]
fn default() -> Self {
Self {
data: ObjectData::Ordinary,
indexed_properties: FxHashMap::default(),
string_properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
extensible: true,
}
}
}
impl Object {
#[inline]
pub fn new() -> Self {
Default::default()
}
pub fn function(function: Function, prototype: Value) -> Self {
let _timer = BoaProfiler::global().start_event("Object::Function", "object");
Self {
data: ObjectData::Function(function),
indexed_properties: FxHashMap::default(),
string_properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype,
extensible: true,
}
}
pub fn create(proto: Value) -> Self {
let mut obj = Self::default();
obj.prototype = proto;
obj
}
pub fn boolean(value: bool) -> Self {
Self {
data: ObjectData::Boolean(value),
indexed_properties: FxHashMap::default(),
string_properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
extensible: true,
}
}
pub fn number(value: f64) -> Self {
Self {
data: ObjectData::Number(value),
indexed_properties: FxHashMap::default(),
string_properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
extensible: true,
}
}
pub fn string<S>(value: S) -> Self
where
S: Into<RcString>,
{
Self {
data: ObjectData::String(value.into()),
indexed_properties: FxHashMap::default(),
string_properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
extensible: true,
}
}
pub fn bigint(value: RcBigInt) -> Self {
Self {
data: ObjectData::BigInt(value),
indexed_properties: FxHashMap::default(),
string_properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
extensible: true,
}
}
pub fn native_object<T>(value: T) -> Self
where
T: NativeObject,
{
Self {
data: ObjectData::NativeObject(Box::new(value)),
indexed_properties: FxHashMap::default(),
string_properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
extensible: true,
}
}
#[inline]
pub fn is_callable(&self) -> bool {
matches!(self.data, ObjectData::Function(ref f) if f.is_callable())
}
#[inline]
pub fn is_constructable(&self) -> bool {
matches!(self.data, ObjectData::Function(ref f) if f.is_constructable())
}
#[inline]
pub fn is_array(&self) -> bool {
matches!(self.data, ObjectData::Array)
}
#[inline]
pub fn as_array(&self) -> Option<()> {
match self.data {
ObjectData::Array => Some(()),
_ => None,
}
}
#[inline]
pub fn is_map(&self) -> bool {
matches!(self.data, ObjectData::Map(_))
}
#[inline]
pub fn as_map_ref(&self) -> Option<&OrderedMap<Value, Value>> {
match self.data {
ObjectData::Map(ref map) => Some(map),
_ => None,
}
}
#[inline]
pub fn as_map_mut(&mut self) -> Option<&mut OrderedMap<Value, Value>> {
match &mut self.data {
ObjectData::Map(map) => Some(map),
_ => None,
}
}
#[inline]
pub fn is_string(&self) -> bool {
matches!(self.data, ObjectData::String(_))
}
#[inline]
pub fn as_string(&self) -> Option<RcString> {
match self.data {
ObjectData::String(ref string) => Some(string.clone()),
_ => None,
}
}
#[inline]
pub fn is_function(&self) -> bool {
matches!(self.data, ObjectData::Function(_))
}
#[inline]
pub fn as_function(&self) -> Option<&Function> {
match self.data {
ObjectData::Function(ref function) => Some(function),
_ => None,
}
}
#[inline]
pub fn is_symbol(&self) -> bool {
matches!(self.data, ObjectData::Symbol(_))
}
#[inline]
pub fn as_symbol(&self) -> Option<RcSymbol> {
match self.data {
ObjectData::Symbol(ref symbol) => Some(symbol.clone()),
_ => None,
}
}
#[inline]
pub fn is_error(&self) -> bool {
matches!(self.data, ObjectData::Error)
}
#[inline]
pub fn as_error(&self) -> Option<()> {
match self.data {
ObjectData::Error => Some(()),
_ => None,
}
}
#[inline]
pub fn is_boolean(&self) -> bool {
matches!(self.data, ObjectData::Boolean(_))
}
#[inline]
pub fn as_boolean(&self) -> Option<bool> {
match self.data {
ObjectData::Boolean(boolean) => Some(boolean),
_ => None,
}
}
#[inline]
pub fn is_number(&self) -> bool {
matches!(self.data, ObjectData::Number(_))
}
#[inline]
pub fn as_number(&self) -> Option<f64> {
match self.data {
ObjectData::Number(number) => Some(number),
_ => None,
}
}
#[inline]
pub fn is_bigint(&self) -> bool {
matches!(self.data, ObjectData::BigInt(_))
}
#[inline]
pub fn as_bigint(&self) -> Option<&BigInt> {
match self.data {
ObjectData::BigInt(ref bigint) => Some(bigint),
_ => None,
}
}
#[inline]
pub fn is_regexp(&self) -> bool {
matches!(self.data, ObjectData::RegExp(_))
}
#[inline]
pub fn as_regexp(&self) -> Option<&RegExp> {
match self.data {
ObjectData::RegExp(ref regexp) => Some(regexp),
_ => None,
}
}
#[inline]
pub fn is_ordinary(&self) -> bool {
matches!(self.data, ObjectData::Ordinary)
}
pub fn prototype_instance(&self) -> &Value {
&self.prototype
}
pub fn set_prototype_instance(&mut self, prototype: Value) {
assert!(prototype.is_null() || prototype.is_object());
self.prototype = prototype
}
#[inline]
pub fn with_prototype(proto: Value, data: ObjectData) -> Object {
let mut object = Object::default();
object.data = data;
object.set_prototype_instance(proto);
object
}
pub fn is_native_object(&self) -> bool {
matches!(self.data, ObjectData::NativeObject(_))
}
pub fn is<T>(&self) -> bool
where
T: NativeObject,
{
use std::ops::Deref;
match self.data {
ObjectData::NativeObject(ref object) => object.deref().as_any().is::<T>(),
_ => false,
}
}
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: NativeObject,
{
use std::ops::Deref;
match self.data {
ObjectData::NativeObject(ref object) => object.deref().as_any().downcast_ref::<T>(),
_ => None,
}
}
pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: NativeObject,
{
use std::ops::DerefMut;
match self.data {
ObjectData::NativeObject(ref mut object) => {
object.deref_mut().as_mut_any().downcast_mut::<T>()
}
_ => None,
}
}
}