From efd9ab87c58c2a0e3b77c66a43e359b7e957c3de Mon Sep 17 00:00:00 2001 From: Halid Odat Date: Tue, 8 Sep 2020 08:18:56 +0200 Subject: [PATCH] Move `object` module to root (#689) --- boa/src/builtins/array/mod.rs | 2 +- boa/src/builtins/bigint/mod.rs | 6 +- boa/src/builtins/boolean/mod.rs | 2 +- boa/src/builtins/date/mod.rs | 6 +- boa/src/builtins/date/tests.rs | 2 +- boa/src/builtins/error/mod.rs | 6 +- boa/src/builtins/error/range.rs | 3 +- boa/src/builtins/error/reference.rs | 3 +- boa/src/builtins/error/syntax.rs | 3 +- boa/src/builtins/error/type.rs | 3 +- boa/src/builtins/function/mod.rs | 6 +- boa/src/builtins/json/tests.rs | 2 +- boa/src/builtins/map/mod.rs | 2 +- boa/src/builtins/mod.rs | 3 +- boa/src/builtins/number/mod.rs | 6 +- boa/src/builtins/object/mod.rs | 766 ++++-------------- boa/src/builtins/regexp/mod.rs | 2 +- boa/src/builtins/string/mod.rs | 6 +- boa/src/class.rs | 6 +- boa/src/context.rs | 3 +- .../function_environment_record.rs | 2 +- boa/src/environment/lexical_environment.rs | 2 +- boa/src/lib.rs | 1 + boa/src/{builtins => }/object/gcobject.rs | 0 .../{builtins => }/object/internal_methods.rs | 3 +- boa/src/{builtins => }/object/iter.rs | 0 boa/src/object/mod.rs | 448 ++++++++++ boa/src/realm.rs | 2 +- boa/src/value/mod.rs | 2 +- 29 files changed, 666 insertions(+), 632 deletions(-) rename boa/src/{builtins => }/object/gcobject.rs (100%) rename boa/src/{builtins => }/object/internal_methods.rs (99%) rename boa/src/{builtins => }/object/iter.rs (100%) create mode 100644 boa/src/object/mod.rs diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 7a262acd45..a8476ec343 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -14,7 +14,7 @@ mod tests; use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ - builtins::object::{ObjectData, PROTOTYPE}, + object::{ObjectData, PROTOTYPE}, property::{Attribute, Property}, value::{same_value_zero, Value}, BoaProfiler, Context, Result, diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index aedc84363a..3fa59a5162 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/boa/src/builtins/bigint/mod.rs @@ -13,10 +13,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt use crate::{ - builtins::{ - function::{make_builtin_fn, make_constructor_fn}, - object::ObjectData, - }, + builtins::function::{make_builtin_fn, make_constructor_fn}, + object::ObjectData, value::{RcBigInt, Value}, BoaProfiler, Context, Result, }; diff --git a/boa/src/builtins/boolean/mod.rs b/boa/src/builtins/boolean/mod.rs index f4ea177a3f..7ccb02b72b 100644 --- a/boa/src/builtins/boolean/mod.rs +++ b/boa/src/builtins/boolean/mod.rs @@ -13,7 +13,7 @@ mod tests; use super::function::{make_builtin_fn, make_constructor_fn}; -use crate::{builtins::object::ObjectData, BoaProfiler, Context, Result, Value}; +use crate::{object::ObjectData, BoaProfiler, Context, Result, Value}; /// Boolean implementation. #[derive(Debug, Clone, Copy)] diff --git a/boa/src/builtins/date/mod.rs b/boa/src/builtins/date/mod.rs index 46485a8ff7..9a6779aa90 100644 --- a/boa/src/builtins/date/mod.rs +++ b/boa/src/builtins/date/mod.rs @@ -2,10 +2,8 @@ mod tests; use crate::{ - builtins::{ - function::{make_builtin_fn, make_constructor_fn}, - object::ObjectData, - }, + builtins::function::{make_builtin_fn, make_constructor_fn}, + object::ObjectData, value::{PreferredType, Value}, BoaProfiler, Context, Result, }; diff --git a/boa/src/builtins/date/tests.rs b/boa/src/builtins/date/tests.rs index 5dd9d01db4..0a563fb337 100644 --- a/boa/src/builtins/date/tests.rs +++ b/boa/src/builtins/date/tests.rs @@ -1,6 +1,6 @@ #![allow(clippy::zero_prefixed_literal)] -use crate::{builtins::object::ObjectData, forward, forward_val, Context, Value}; +use crate::{forward, forward_val, object::ObjectData, Context, Value}; use chrono::prelude::*; // NOTE: Javascript Uses 0-based months, where chrono uses 1-based months. Many of the assertions look wrong because of diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index 4357f55fc6..5321b62c7c 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -11,10 +11,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error use crate::{ - builtins::{ - function::{make_builtin_fn, make_constructor_fn}, - object::ObjectData, - }, + builtins::function::{make_builtin_fn, make_constructor_fn}, + object::ObjectData, profiler::BoaProfiler, Context, Result, Value, }; diff --git a/boa/src/builtins/error/range.rs b/boa/src/builtins/error/range.rs index 8bfe505fe3..9fea28d9a1 100644 --- a/boa/src/builtins/error/range.rs +++ b/boa/src/builtins/error/range.rs @@ -10,7 +10,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError use crate::{ - builtins::{function::make_builtin_fn, function::make_constructor_fn, object::ObjectData}, + builtins::{function::make_builtin_fn, function::make_constructor_fn}, + object::ObjectData, profiler::BoaProfiler, Context, Result, Value, }; diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs index a1310dbc3f..144e055fc0 100644 --- a/boa/src/builtins/error/reference.rs +++ b/boa/src/builtins/error/reference.rs @@ -10,7 +10,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError use crate::{ - builtins::{function::make_builtin_fn, function::make_constructor_fn, object::ObjectData}, + builtins::{function::make_builtin_fn, function::make_constructor_fn}, + object::ObjectData, profiler::BoaProfiler, Context, Result, Value, }; diff --git a/boa/src/builtins/error/syntax.rs b/boa/src/builtins/error/syntax.rs index 30e2dfc188..086b558def 100644 --- a/boa/src/builtins/error/syntax.rs +++ b/boa/src/builtins/error/syntax.rs @@ -12,7 +12,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError use crate::{ - builtins::{function::make_builtin_fn, function::make_constructor_fn, object::ObjectData}, + builtins::{function::make_builtin_fn, function::make_constructor_fn}, + object::ObjectData, profiler::BoaProfiler, Context, Result, Value, }; diff --git a/boa/src/builtins/error/type.rs b/boa/src/builtins/error/type.rs index 8377c3f54f..e2e301c12f 100644 --- a/boa/src/builtins/error/type.rs +++ b/boa/src/builtins/error/type.rs @@ -16,7 +16,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError use crate::{ - builtins::{function::make_builtin_fn, function::make_constructor_fn, object::ObjectData}, + builtins::{function::make_builtin_fn, function::make_constructor_fn}, + object::ObjectData, BoaProfiler, Context, Result, Value, }; diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 24624a115c..2b0d029fe7 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -12,11 +12,9 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function use crate::{ - builtins::{ - object::{Object, ObjectData, PROTOTYPE}, - Array, - }, + builtins::Array, environment::lexical_environment::Environment, + object::{Object, ObjectData, PROTOTYPE}, property::{Attribute, Property}, syntax::ast::node::{FormalParameter, RcStatementList}, BoaProfiler, Context, Result, Value, diff --git a/boa/src/builtins/json/tests.rs b/boa/src/builtins/json/tests.rs index 2c9776e7d9..d091cd423b 100644 --- a/boa/src/builtins/json/tests.rs +++ b/boa/src/builtins/json/tests.rs @@ -1,4 +1,4 @@ -use crate::{builtins::object::PROTOTYPE, forward, forward_val, value::same_value, Context}; +use crate::{forward, forward_val, object::PROTOTYPE, value::same_value, Context}; #[test] fn json_sanity() { diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 5fab3455bd..4437700c88 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -2,7 +2,7 @@ use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ - builtins::object::{ObjectData, PROTOTYPE}, + object::{ObjectData, PROTOTYPE}, property::{Attribute, Property}, BoaProfiler, Context, Result, Value, }; diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index afd3284de5..9e1241d84a 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -34,6 +34,7 @@ pub(crate) use self::{ math::Math, nan::NaN, number::Number, + object::Object, regexp::RegExp, string::String, symbol::Symbol, @@ -47,7 +48,7 @@ pub fn init(interpreter: &mut Context) { let globals = [ // The `Function` global must be initialized before other types. function::init, - object::init, + Object::init, Array::init, BigInt::init, Boolean::init, diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index 17a4131a9f..2a92f63d10 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -13,11 +13,9 @@ //! [spec]: https://tc39.es/ecma262/#sec-number-object //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number -use super::{ - function::{make_builtin_fn, make_constructor_fn}, - object::ObjectData, -}; +use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ + object::ObjectData, value::{AbstractRelation, Value}, BoaProfiler, Context, Result, }; diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 46e1b1e560..851e655ca8 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -14,628 +14,222 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object use crate::{ - builtins::{ - function::{make_builtin_fn, make_constructor_fn, Function}, - map::ordered_map::OrderedMap, - BigInt, Date, RegExp, - }, - property::{Property, PropertyKey}, - value::{same_value, RcBigInt, RcString, RcSymbol, Value}, + builtins::function::{make_builtin_fn, make_constructor_fn}, + object::ObjectData, + property::Property, + value::{same_value, Value}, BoaProfiler, Context, Result, }; -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::*; #[cfg(test)] mod tests; -/// 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 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, - /// Properties - string_properties: FxHashMap, - /// Symbol Properties - symbol_properties: FxHashMap, - /// 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), - RegExp(Box), - BigInt(RcBigInt), - Boolean(bool), - Function(Function), - String(RcString), - Number(f64), - Symbol(RcSymbol), - Error, - Ordinary, - Date(Date), - Global, - NativeObject(Box), -} +/// The global JavaScript object. +#[derive(Debug, Clone, Copy)] +pub struct Object; -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 Object { + /// Create a new object. + pub fn make_object(_: &Value, args: &[Value], ctx: &mut Context) -> Result { + if let Some(arg) = args.get(0) { + if !arg.is_null_or_undefined() { + return arg.to_object(ctx); } - ) - } -} - -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, } - } -} + let global = ctx.global_object(); -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, - } + Ok(Value::new_object(Some(global))) } - /// ObjectCreate is used to specify the runtime creation of new ordinary objects. + /// `Object.create( proto, [propertiesObject] )` + /// + /// Creates a new object from the provided prototype. /// /// More information: /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] /// - /// [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 - } + /// [spec]: https://tc39.es/ecma262/#sec-object.create + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create + pub fn create(_: &Value, args: &[Value], interpreter: &mut Context) -> Result { + let prototype = args.get(0).cloned().unwrap_or_else(Value::undefined); + let properties = args.get(1).cloned().unwrap_or_else(Value::undefined); - /// 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, + if properties != Value::Undefined { + unimplemented!("propertiesObject argument of Object.create") } - } - /// 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, + match prototype { + Value::Object(_) | Value::Null => Ok(Value::new_object_from_prototype( + prototype, + ObjectData::Ordinary, + )), + _ => interpreter.throw_type_error(format!( + "Object prototype may only be an Object or null: {}", + prototype.display() + )), } } - /// Return a new `String` object whose `[[StringData]]` internal slot is set to argument. - pub fn string(value: S) -> Self - where - S: Into, - { - Self { - data: ObjectData::String(value.into()), - indexed_properties: FxHashMap::default(), - string_properties: FxHashMap::default(), - symbol_properties: FxHashMap::default(), - prototype: Value::null(), - extensible: true, - } + /// Uses the SameValue algorithm to check equality of objects + pub fn is(_: &Value, args: &[Value], _: &mut Context) -> Result { + let x = args.get(0).cloned().unwrap_or_else(Value::undefined); + let y = args.get(1).cloned().unwrap_or_else(Value::undefined); + + Ok(same_value(&x, &y).into()) } - /// 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, - } + /// Get the `prototype` of an object. + pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result { + let obj = args.get(0).expect("Cannot get object"); + Ok(obj + .as_object() + .map_or_else(Value::undefined, |object| object.prototype().clone())) } - /// Create a new native object of type `T`. - pub fn native_object(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, - } + /// Set the `prototype` of an object. + pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result { + let obj = args.get(0).expect("Cannot get object").clone(); + let proto = args.get(1).expect("Cannot get object").clone(); + obj.as_object_mut().unwrap().set_prototype(proto); + Ok(obj) } - /// It determines if Object is a callable function with a [[Call]] internal method. + /// Define a property in an object + pub fn define_property(_: &Value, args: &[Value], ctx: &mut Context) -> Result { + let obj = args.get(0).expect("Cannot get object"); + let prop = args.get(1).expect("Cannot get object").to_string(ctx)?; + let desc = Property::from(args.get(2).expect("Cannot get object")); + obj.set_property(prop, desc); + Ok(Value::undefined()) + } + + /// `Object.prototype.toString()` + /// + /// This method returns a string representing the object. /// /// More information: - /// - [EcmaScript reference][spec] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] /// - /// [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()) + /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString + #[allow(clippy::wrong_self_convention)] + pub fn to_string(this: &Value, _: &[Value], _: &mut Context) -> Result { + // FIXME: it should not display the object. + Ok(this.display().to_string().into()) } - /// It determines if Object is a function object with a [[Construct]] internal method. + /// `Object.prototype.hasOwnPrototype( property )` + /// + /// The method returns a boolean indicating whether the object has the specified property + /// as its own property (as opposed to inheriting it). /// /// More information: - /// - [EcmaScript reference][spec] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] /// - /// [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> { - match self.data { - ObjectData::Map(ref map) => Some(map), - _ => None, - } - } - - #[inline] - pub fn as_map_mut(&mut self) -> Option<&mut OrderedMap> { - 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 { - 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 { - 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 { - 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 { - 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(&self) -> bool - where - T: NativeObject, - { - use std::ops::Deref; - match self.data { - ObjectData::NativeObject(ref object) => object.deref().as_any().is::(), - _ => false, - } - } - - /// Downcast a reference to the object, - /// if the object is type native object type `T`. - pub fn downcast_ref(&self) -> Option<&T> - where - T: NativeObject, - { - use std::ops::Deref; - match self.data { - ObjectData::NativeObject(ref object) => object.deref().as_any().downcast_ref::(), - _ => None, - } - } - - /// Downcast a mutable reference to the object, - /// if the object is type native object type `T`. - pub fn downcast_mut(&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::() - } - _ => None, - } - } -} - -/// Create a new object. -pub fn make_object(_: &Value, args: &[Value], ctx: &mut Context) -> Result { - if let Some(arg) = args.get(0) { - if !arg.is_null_or_undefined() { - return arg.to_object(ctx); - } - } - let global = ctx.global_object(); - - Ok(Value::new_object(Some(global))) -} - -/// `Object.create( proto, [propertiesObject] )` -/// -/// Creates a new object from the provided prototype. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#sec-object.create -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create -pub fn create(_: &Value, args: &[Value], interpreter: &mut Context) -> Result { - let prototype = args.get(0).cloned().unwrap_or_else(Value::undefined); - let properties = args.get(1).cloned().unwrap_or_else(Value::undefined); - - if properties != Value::Undefined { - unimplemented!("propertiesObject argument of Object.create") - } - - match prototype { - Value::Object(_) | Value::Null => Ok(Value::new_object_from_prototype( + /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.hasownproperty + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty + pub fn has_own_property(this: &Value, args: &[Value], ctx: &mut Context) -> Result { + let prop = if args.is_empty() { + None + } else { + Some(args.get(0).expect("Cannot get object").to_string(ctx)?) + }; + let own_property = this + .as_object() + .as_deref() + .expect("Cannot get THIS object") + .get_own_property(&prop.expect("cannot get prop").into()); + if own_property.is_none() { + Ok(Value::from(false)) + } else { + Ok(Value::from(true)) + } + } + + pub fn property_is_enumerable( + this: &Value, + args: &[Value], + ctx: &mut Context, + ) -> Result { + let key = match args.get(0) { + None => return Ok(Value::from(false)), + Some(key) => key, + }; + + let key = key.to_property_key(ctx)?; + let own_property = this.to_object(ctx).map(|obj| { + obj.as_object() + .expect("Unable to deref object") + .get_own_property(&key) + }); + + Ok(own_property.map_or(Value::from(false), |own_prop| { + Value::from(own_prop.enumerable_or(false)) + })) + } + + /// Initialise the `Object` object on the global object. + #[inline] + pub fn init(interpreter: &mut Context) -> (&'static str, Value) { + let global = interpreter.global_object(); + let _timer = BoaProfiler::global().start_event("object", "init"); + + let prototype = Value::new_object(None); + + make_builtin_fn( + Self::has_own_property, + "hasOwnProperty", + &prototype, + 0, + interpreter, + ); + make_builtin_fn( + Self::property_is_enumerable, + "propertyIsEnumerable", + &prototype, + 0, + interpreter, + ); + make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter); + + let object = make_constructor_fn( + "Object", + 1, + Self::make_object, + global, prototype, - ObjectData::Ordinary, - )), - _ => interpreter.throw_type_error(format!( - "Object prototype may only be an Object or null: {}", - prototype.display() - )), + true, + true, + ); + + // static methods of the builtin Object + make_builtin_fn(Self::create, "create", &object, 2, interpreter); + make_builtin_fn( + Self::set_prototype_of, + "setPrototypeOf", + &object, + 2, + interpreter, + ); + make_builtin_fn( + Self::get_prototype_of, + "getPrototypeOf", + &object, + 1, + interpreter, + ); + make_builtin_fn( + Self::define_property, + "defineProperty", + &object, + 3, + interpreter, + ); + make_builtin_fn(Self::is, "is", &object, 2, interpreter); + + ("Object", object) } } - -/// Uses the SameValue algorithm to check equality of objects -pub fn is(_: &Value, args: &[Value], _: &mut Context) -> Result { - let x = args.get(0).cloned().unwrap_or_else(Value::undefined); - let y = args.get(1).cloned().unwrap_or_else(Value::undefined); - - Ok(same_value(&x, &y).into()) -} - -/// Get the `prototype` of an object. -pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result { - let obj = args.get(0).expect("Cannot get object"); - Ok(obj - .as_object() - .map_or_else(Value::undefined, |object| object.prototype.clone())) -} - -/// Set the `prototype` of an object. -pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result { - let obj = args.get(0).expect("Cannot get object").clone(); - let proto = args.get(1).expect("Cannot get object").clone(); - obj.as_object_mut().unwrap().prototype = proto; - Ok(obj) -} - -/// Define a property in an object -pub fn define_property(_: &Value, args: &[Value], ctx: &mut Context) -> Result { - let obj = args.get(0).expect("Cannot get object"); - let prop = args.get(1).expect("Cannot get object").to_string(ctx)?; - let desc = Property::from(args.get(2).expect("Cannot get object")); - obj.set_property(prop, desc); - Ok(Value::undefined()) -} - -/// `Object.prototype.toString()` -/// -/// This method returns a string representing the object. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString -pub fn to_string(this: &Value, _: &[Value], _: &mut Context) -> Result { - // FIXME: it should not display the object. - Ok(this.display().to_string().into()) -} - -/// `Object.prototype.hasOwnPrototype( property )` -/// -/// The method returns a boolean indicating whether the object has the specified property -/// as its own property (as opposed to inheriting it). -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#sec-object.prototype.hasownproperty -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty -pub fn has_own_property(this: &Value, args: &[Value], ctx: &mut Context) -> Result { - let prop = if args.is_empty() { - None - } else { - Some(args.get(0).expect("Cannot get object").to_string(ctx)?) - }; - let own_property = this - .as_object() - .as_deref() - .expect("Cannot get THIS object") - .get_own_property(&prop.expect("cannot get prop").into()); - if own_property.is_none() { - Ok(Value::from(false)) - } else { - Ok(Value::from(true)) - } -} - -pub fn property_is_enumerable(this: &Value, args: &[Value], ctx: &mut Context) -> Result { - let key = match args.get(0) { - None => return Ok(Value::from(false)), - Some(key) => key, - }; - - let key = key.to_property_key(ctx)?; - let own_property = this.to_object(ctx).map(|obj| { - obj.as_object() - .expect("Unable to deref object") - .get_own_property(&key) - }); - - Ok(own_property.map_or(Value::from(false), |own_prop| { - Value::from(own_prop.enumerable_or(false)) - })) -} - -/// Initialise the `Object` object on the global object. -#[inline] -pub fn init(interpreter: &mut Context) -> (&'static str, Value) { - let global = interpreter.global_object(); - let _timer = BoaProfiler::global().start_event("object", "init"); - - let prototype = Value::new_object(None); - - make_builtin_fn( - has_own_property, - "hasOwnProperty", - &prototype, - 0, - interpreter, - ); - make_builtin_fn( - property_is_enumerable, - "propertyIsEnumerable", - &prototype, - 0, - interpreter, - ); - make_builtin_fn(to_string, "toString", &prototype, 0, interpreter); - - let object = make_constructor_fn("Object", 1, make_object, global, prototype, true, true); - - // static methods of the builtin Object - make_builtin_fn(create, "create", &object, 2, interpreter); - make_builtin_fn(set_prototype_of, "setPrototypeOf", &object, 2, interpreter); - make_builtin_fn(get_prototype_of, "getPrototypeOf", &object, 1, interpreter); - make_builtin_fn(define_property, "defineProperty", &object, 3, interpreter); - make_builtin_fn(is, "is", &object, 2, interpreter); - - ("Object", object) -} diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 04383c3292..e21e5e2cf6 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -13,7 +13,7 @@ use regex::Regex; use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ - builtins::object::ObjectData, + object::ObjectData, property::Property, value::{RcString, Value}, BoaProfiler, Context, Result, diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index d51c4d2411..e4520ee4db 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -14,10 +14,8 @@ mod tests; use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ - builtins::{ - object::{Object, ObjectData}, - RegExp, - }, + builtins::RegExp, + object::{Object, ObjectData}, property::Property, value::{RcString, Value}, BoaProfiler, Context, Result, diff --git a/boa/src/class.rs b/boa/src/class.rs index 6ce089a0d0..e754bddef9 100644 --- a/boa/src/class.rs +++ b/boa/src/class.rs @@ -60,10 +60,8 @@ //! [class-trait]: ./trait.Class.html use crate::{ - builtins::{ - function::{BuiltInFunction, Function, FunctionFlags, NativeFunction}, - object::{GcObject, NativeObject, Object, ObjectData, PROTOTYPE}, - }, + builtins::function::{BuiltInFunction, Function, FunctionFlags, NativeFunction}, + object::{GcObject, NativeObject, Object, ObjectData, PROTOTYPE}, property::{Attribute, Property, PropertyKey}, Context, Result, Value, }; diff --git a/boa/src/context.rs b/boa/src/context.rs index 8330fae6b0..6839b21c6c 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -4,12 +4,11 @@ use crate::{ builtins::{ self, function::{Function, FunctionFlags, NativeFunction}, - object::ObjectData, - object::{GcObject, Object, PROTOTYPE}, Console, Symbol, }, class::{Class, ClassBuilder}, exec::Interpreter, + object::{GcObject, Object, ObjectData, PROTOTYPE}, property::{Property, PropertyKey}, realm::Realm, syntax::{ diff --git a/boa/src/environment/function_environment_record.rs b/boa/src/environment/function_environment_record.rs index 0ea6e04ea6..1681735d7b 100644 --- a/boa/src/environment/function_environment_record.rs +++ b/boa/src/environment/function_environment_record.rs @@ -9,12 +9,12 @@ //! More info: use crate::{ - builtins::object::GcObject, environment::{ declarative_environment_record::DeclarativeEnvironmentRecordBinding, environment_record_trait::EnvironmentRecordTrait, lexical_environment::{Environment, EnvironmentType}, }, + object::GcObject, Value, }; use gc::{unsafe_empty_trace, Finalize, Trace}; diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index 4faf63035d..698acbd2dd 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -6,7 +6,6 @@ //! This is the entrypoint to lexical environments. use crate::{ - builtins::object::GcObject, environment::{ declarative_environment_record::DeclarativeEnvironmentRecord, environment_record_trait::EnvironmentRecordTrait, @@ -14,6 +13,7 @@ use crate::{ global_environment_record::GlobalEnvironmentRecord, object_environment_record::ObjectEnvironmentRecord, }, + object::GcObject, BoaProfiler, Value, }; use gc::{Gc, GcCell}; diff --git a/boa/src/lib.rs b/boa/src/lib.rs index f0d2a9b45c..591634780d 100644 --- a/boa/src/lib.rs +++ b/boa/src/lib.rs @@ -38,6 +38,7 @@ pub mod builtins; pub mod class; pub mod environment; pub mod exec; +pub mod object; pub mod profiler; pub mod property; pub mod realm; diff --git a/boa/src/builtins/object/gcobject.rs b/boa/src/object/gcobject.rs similarity index 100% rename from boa/src/builtins/object/gcobject.rs rename to boa/src/object/gcobject.rs diff --git a/boa/src/builtins/object/internal_methods.rs b/boa/src/object/internal_methods.rs similarity index 99% rename from boa/src/builtins/object/internal_methods.rs rename to boa/src/object/internal_methods.rs index 8a1e52977b..7401fac90b 100644 --- a/boa/src/builtins/object/internal_methods.rs +++ b/boa/src/object/internal_methods.rs @@ -6,11 +6,12 @@ //! [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots use crate::{ - builtins::object::Object, + object::Object, property::{Attribute, Property, PropertyKey}, value::{same_value, Value}, BoaProfiler, }; + impl Object { /// Check if object has property. /// diff --git a/boa/src/builtins/object/iter.rs b/boa/src/object/iter.rs similarity index 100% rename from boa/src/builtins/object/iter.rs rename to boa/src/object/iter.rs diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs new file mode 100644 index 0000000000..8c47b4ca44 --- /dev/null +++ b/boa/src/object/mod.rs @@ -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 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, + /// Properties + string_properties: FxHashMap, + /// Symbol Properties + symbol_properties: FxHashMap, + /// 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), + RegExp(Box), + BigInt(RcBigInt), + Boolean(bool), + Function(Function), + String(RcString), + Number(f64), + Symbol(RcSymbol), + Error, + Ordinary, + Date(Date), + Global, + NativeObject(Box), +} + +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(value: S) -> Self + where + S: Into, + { + 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(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> { + match self.data { + ObjectData::Map(ref map) => Some(map), + _ => None, + } + } + + #[inline] + pub fn as_map_mut(&mut self) -> Option<&mut OrderedMap> { + 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 { + 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 { + 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 { + 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 { + 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(&self) -> bool + where + T: NativeObject, + { + use std::ops::Deref; + match self.data { + ObjectData::NativeObject(ref object) => object.deref().as_any().is::(), + _ => false, + } + } + + /// Downcast a reference to the object, + /// if the object is type native object type `T`. + pub fn downcast_ref(&self) -> Option<&T> + where + T: NativeObject, + { + use std::ops::Deref; + match self.data { + ObjectData::NativeObject(ref object) => object.deref().as_any().downcast_ref::(), + _ => None, + } + } + + /// Downcast a mutable reference to the object, + /// if the object is type native object type `T`. + pub fn downcast_mut(&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::() + } + _ => None, + } + } +} diff --git a/boa/src/realm.rs b/boa/src/realm.rs index 81fc0e1a46..b6785dde13 100644 --- a/boa/src/realm.rs +++ b/boa/src/realm.rs @@ -34,7 +34,7 @@ impl Realm { let global = Value::new_object(None); // Allow identification of the global object easily - global.set_data(crate::builtins::object::ObjectData::Global); + global.set_data(crate::object::ObjectData::Global); // We need to clone the global here because its referenced from separate places (only pointer is cloned) let global_env = new_global_environment(global.clone(), global.clone()); diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index ec1688cc08..bbee9fd2df 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -8,9 +8,9 @@ mod tests; use crate::{ builtins::{ number::{f64_to_int32, f64_to_uint32}, - object::{GcObject, Object, ObjectData, PROTOTYPE}, BigInt, Number, }, + object::{GcObject, Object, ObjectData, PROTOTYPE}, property::{Attribute, Property, PropertyKey}, BoaProfiler, Context, Result, };