Browse Source

Move `object` module to root (#689)

pull/680/head
Halid Odat 4 years ago committed by GitHub
parent
commit
efd9ab87c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      boa/src/builtins/array/mod.rs
  2. 6
      boa/src/builtins/bigint/mod.rs
  3. 2
      boa/src/builtins/boolean/mod.rs
  4. 6
      boa/src/builtins/date/mod.rs
  5. 2
      boa/src/builtins/date/tests.rs
  6. 6
      boa/src/builtins/error/mod.rs
  7. 3
      boa/src/builtins/error/range.rs
  8. 3
      boa/src/builtins/error/reference.rs
  9. 3
      boa/src/builtins/error/syntax.rs
  10. 3
      boa/src/builtins/error/type.rs
  11. 6
      boa/src/builtins/function/mod.rs
  12. 2
      boa/src/builtins/json/tests.rs
  13. 2
      boa/src/builtins/map/mod.rs
  14. 3
      boa/src/builtins/mod.rs
  15. 6
      boa/src/builtins/number/mod.rs
  16. 766
      boa/src/builtins/object/mod.rs
  17. 2
      boa/src/builtins/regexp/mod.rs
  18. 6
      boa/src/builtins/string/mod.rs
  19. 6
      boa/src/class.rs
  20. 3
      boa/src/context.rs
  21. 2
      boa/src/environment/function_environment_record.rs
  22. 2
      boa/src/environment/lexical_environment.rs
  23. 1
      boa/src/lib.rs
  24. 0
      boa/src/object/gcobject.rs
  25. 3
      boa/src/object/internal_methods.rs
  26. 0
      boa/src/object/iter.rs
  27. 448
      boa/src/object/mod.rs
  28. 2
      boa/src/realm.rs
  29. 2
      boa/src/value/mod.rs

2
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,

6
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,
};

2
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)]

6
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,
};

2
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

6
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,
};

3
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,
};

3
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,
};

3
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,
};

3
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,
};

6
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,

2
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() {

2
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,
};

3
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,

6
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,
};

766
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<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>),
}
/// 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<Value> {
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<Value> {
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<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,
}
/// Uses the SameValue algorithm to check equality of objects
pub fn is(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
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<Value> {
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<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,
}
/// Set the `prototype` of an object.
pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
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<Value> {
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<Value> {
// 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<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,
}
}
}
/// Create a new object.
pub fn make_object(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
// 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<Value> {
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<Value> {
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)
}

2
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,

6
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,

6
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,
};

3
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::{

2
boa/src/environment/function_environment_record.rs

@ -9,12 +9,12 @@
//! More info: <https://tc39.es/ecma262/#sec-function-environment-records>
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};

2
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};

1
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;

0
boa/src/builtins/object/gcobject.rs → boa/src/object/gcobject.rs

3
boa/src/builtins/object/internal_methods.rs → 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.
///

0
boa/src/builtins/object/iter.rs → boa/src/object/iter.rs

448
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<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,
}
}
}

2
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());

2
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,
};

Loading…
Cancel
Save