mirror of https://github.com/boa-dev/boa.git
Halid Odat
4 years ago
committed by
GitHub
29 changed files with 666 additions and 632 deletions
@ -0,0 +1,448 @@ |
|||||||
|
//! This module implements the Rust representation of a JavaScript object.
|
||||||
|
|
||||||
|
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::*; |
||||||
|
|
||||||
|
/// Static `prototype`, usually set on constructors as a key to point to their respective prototype object.
|
||||||
|
pub static PROTOTYPE: &str = "prototype"; |
||||||
|
|
||||||
|
/// This trait allows Rust types to be passed around as objects.
|
||||||
|
///
|
||||||
|
/// This is automatically implemented, when a type implements `Debug`, `Any` and `Trace`.
|
||||||
|
pub trait NativeObject: Debug + Any + Trace { |
||||||
|
/// 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; |
||||||
|
} |
||||||
|
|
||||||
|
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 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// The internal representation of an JavaScript object.
|
||||||
|
#[derive(Debug, Trace, Finalize)] |
||||||
|
pub struct Object { |
||||||
|
/// The type of the object.
|
||||||
|
pub data: ObjectData, |
||||||
|
indexed_properties: FxHashMap<u32, Property>, |
||||||
|
/// Properties
|
||||||
|
string_properties: FxHashMap<RcString, Property>, |
||||||
|
/// Symbol Properties
|
||||||
|
symbol_properties: FxHashMap<RcSymbol, Property>, |
||||||
|
/// Instance prototype `__proto__`.
|
||||||
|
prototype: Value, |
||||||
|
/// Whether it can have new properties added to it.
|
||||||
|
extensible: bool, |
||||||
|
} |
||||||
|
|
||||||
|
/// Defines the different types of objects.
|
||||||
|
#[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 { |
||||||
|
/// Return a new ObjectData struct, with `kind` set to Ordinary
|
||||||
|
#[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() |
||||||
|
} |
||||||
|
|
||||||
|
/// Return a new ObjectData struct, with `kind` set to Ordinary
|
||||||
|
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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// ObjectCreate is used to specify the runtime creation of new ordinary objects.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript reference][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-objectcreate
|
||||||
|
// TODO: proto should be a &Value here
|
||||||
|
pub fn create(proto: Value) -> Self { |
||||||
|
let mut obj = Self::default(); |
||||||
|
obj.prototype = proto; |
||||||
|
obj |
||||||
|
} |
||||||
|
|
||||||
|
/// Return a new Boolean object whose `[[BooleanData]]` internal slot is set to argument.
|
||||||
|
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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Return a new `Number` object whose `[[NumberData]]` internal slot is set to argument.
|
||||||
|
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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Return a new `String` object whose `[[StringData]]` internal slot is set to argument.
|
||||||
|
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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument.
|
||||||
|
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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Create a new native object of type `T`.
|
||||||
|
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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// It determines if Object is a callable function with a [[Call]] internal method.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [EcmaScript reference][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-iscallable
|
||||||
|
#[inline] |
||||||
|
pub fn is_callable(&self) -> bool { |
||||||
|
matches!(self.data, ObjectData::Function(ref f) if f.is_callable()) |
||||||
|
} |
||||||
|
|
||||||
|
/// It determines if Object is a function object with a [[Construct]] internal method.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [EcmaScript reference][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-isconstructor
|
||||||
|
#[inline] |
||||||
|
pub fn is_constructable(&self) -> bool { |
||||||
|
matches!(self.data, ObjectData::Function(ref f) if f.is_constructable()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks if it an `Array` object.
|
||||||
|
#[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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks if it is a `Map` object.pub
|
||||||
|
#[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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks if it a `String` object.
|
||||||
|
#[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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks if it a `Function` object.
|
||||||
|
#[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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks if it a Symbol object.
|
||||||
|
#[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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks if it an Error object.
|
||||||
|
#[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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks if it a Boolean object.
|
||||||
|
#[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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks if it a `Number` object.
|
||||||
|
#[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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks if it a `BigInt` object.
|
||||||
|
#[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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks if it a `RegExp` object.
|
||||||
|
#[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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks if it an ordinary object.
|
||||||
|
#[inline] |
||||||
|
pub fn is_ordinary(&self) -> bool { |
||||||
|
matches!(self.data, ObjectData::Ordinary) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn prototype(&self) -> &Value { |
||||||
|
&self.prototype |
||||||
|
} |
||||||
|
|
||||||
|
pub fn set_prototype(&mut self, prototype: Value) { |
||||||
|
assert!(prototype.is_null() || prototype.is_object()); |
||||||
|
self.prototype = prototype |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns `true` if it holds an Rust type that implements `NativeObject`.
|
||||||
|
pub fn is_native_object(&self) -> bool { |
||||||
|
matches!(self.data, ObjectData::NativeObject(_)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Reeturn `true` if it is a native object and the native type is `T`.
|
||||||
|
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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Downcast a reference to the object,
|
||||||
|
/// if the object is type native object type `T`.
|
||||||
|
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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Downcast a mutable reference to the object,
|
||||||
|
/// if the object is type native object type `T`.
|
||||||
|
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, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue