|
|
|
@ -17,24 +17,23 @@ use crate::{
|
|
|
|
|
builtins::{ |
|
|
|
|
function::Function, |
|
|
|
|
property::Property, |
|
|
|
|
value::{same_value, ResultValue, Value, ValueData}, |
|
|
|
|
value::{ResultValue, Value, ValueData}, |
|
|
|
|
BigInt, Symbol, |
|
|
|
|
}, |
|
|
|
|
exec::Interpreter, |
|
|
|
|
BoaProfiler, |
|
|
|
|
}; |
|
|
|
|
use gc::{unsafe_empty_trace, Finalize, Trace}; |
|
|
|
|
use gc::{Finalize, Trace}; |
|
|
|
|
use rustc_hash::FxHashMap; |
|
|
|
|
use std::{ |
|
|
|
|
borrow::Borrow, |
|
|
|
|
fmt::{self, Debug, Display, Error, Formatter}, |
|
|
|
|
fmt::{Debug, Display, Error, Formatter}, |
|
|
|
|
ops::Deref, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
use super::function::{make_builtin_fn, make_constructor_fn}; |
|
|
|
|
pub use internal_methods_trait::ObjectInternalMethods; |
|
|
|
|
pub use internal_state::{InternalState, InternalStateCell}; |
|
|
|
|
|
|
|
|
|
pub mod internal_methods_trait; |
|
|
|
|
pub mod internal_methods; |
|
|
|
|
mod internal_state; |
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
@ -47,322 +46,87 @@ pub static PROTOTYPE: &str = "prototype";
|
|
|
|
|
pub static INSTANCE_PROTOTYPE: &str = "__proto__"; |
|
|
|
|
|
|
|
|
|
/// The internal representation of an JavaScript object.
|
|
|
|
|
#[derive(Trace, Finalize, Clone)] |
|
|
|
|
#[derive(Debug, Trace, Finalize, Clone)] |
|
|
|
|
pub struct Object { |
|
|
|
|
/// The type of the object.
|
|
|
|
|
pub kind: ObjectKind, |
|
|
|
|
pub data: ObjectData, |
|
|
|
|
/// Internal Slots
|
|
|
|
|
pub internal_slots: FxHashMap<String, Value>, |
|
|
|
|
internal_slots: FxHashMap<String, Value>, |
|
|
|
|
/// Properties
|
|
|
|
|
pub properties: FxHashMap<String, Property>, |
|
|
|
|
properties: FxHashMap<String, Property>, |
|
|
|
|
/// Symbol Properties
|
|
|
|
|
pub sym_properties: FxHashMap<i32, Property>, |
|
|
|
|
symbol_properties: FxHashMap<u32, Property>, |
|
|
|
|
/// Some rust object that stores internal state
|
|
|
|
|
pub state: Option<InternalStateCell>, |
|
|
|
|
/// Function
|
|
|
|
|
pub func: Option<Function>, |
|
|
|
|
state: Option<InternalStateCell>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Debug for Object { |
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
|
|
|
|
writeln!(f, "{{")?; |
|
|
|
|
writeln!(f, "\tkind: {}", self.kind)?; |
|
|
|
|
writeln!(f, "\tstate: {:?}", self.state)?; |
|
|
|
|
writeln!(f, "\tfunc: {:?}", self.func)?; |
|
|
|
|
writeln!(f, "\tproperties: {{")?; |
|
|
|
|
for (key, _) in self.properties.iter() { |
|
|
|
|
writeln!(f, "\t\t{}", key)?; |
|
|
|
|
} |
|
|
|
|
writeln!(f, "\t }}")?; |
|
|
|
|
write!(f, "}}") |
|
|
|
|
} |
|
|
|
|
/// Defines the different types of objects.
|
|
|
|
|
#[derive(Debug, Trace, Finalize, Clone)] |
|
|
|
|
pub enum ObjectData { |
|
|
|
|
Array, |
|
|
|
|
BigInt(BigInt), |
|
|
|
|
Boolean(bool), |
|
|
|
|
Function(Function), |
|
|
|
|
String(String), |
|
|
|
|
Number(f64), |
|
|
|
|
Symbol(Symbol), |
|
|
|
|
Error, |
|
|
|
|
Ordinary, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl ObjectInternalMethods for Object { |
|
|
|
|
/// `Object.setPropertyOf(obj, prototype)`
|
|
|
|
|
///
|
|
|
|
|
/// This method sets the prototype (i.e., the internal `[[Prototype]]` property)
|
|
|
|
|
/// of a specified object to another object or `null`.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf
|
|
|
|
|
fn set_prototype_of(&mut self, val: Value) -> bool { |
|
|
|
|
debug_assert!(val.is_object() || val.is_null()); |
|
|
|
|
let current = self.get_internal_slot(PROTOTYPE); |
|
|
|
|
if same_value(¤t, &val, false) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
let extensible = self.get_internal_slot("extensible"); |
|
|
|
|
if extensible.is_null() { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
let mut p = val.clone(); |
|
|
|
|
let mut done = false; |
|
|
|
|
while !done { |
|
|
|
|
if p.is_null() { |
|
|
|
|
done = true |
|
|
|
|
} else if same_value(&Value::from(self.clone()), &p, false) { |
|
|
|
|
return false; |
|
|
|
|
} else { |
|
|
|
|
p = p.get_internal_slot(PROTOTYPE); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
self.set_internal_slot(PROTOTYPE, val); |
|
|
|
|
true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Helper function for property insertion.
|
|
|
|
|
fn insert_property(&mut self, name: String, p: Property) { |
|
|
|
|
self.properties.insert(name, p); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Helper function for property removal.
|
|
|
|
|
fn remove_property(&mut self, name: &str) { |
|
|
|
|
self.properties.remove(name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Helper function to set an internal slot
|
|
|
|
|
fn set_internal_slot(&mut self, name: &str, val: Value) { |
|
|
|
|
self.internal_slots.insert(name.to_string(), val); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Helper function to get an immutable internal slot or Null
|
|
|
|
|
fn get_internal_slot(&self, name: &str) -> Value { |
|
|
|
|
let _timer = BoaProfiler::global().start_event("Object::get_internal_slot", "object"); |
|
|
|
|
match self.internal_slots.get(name) { |
|
|
|
|
Some(v) => v.clone(), |
|
|
|
|
None => Value::null(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// The specification returns a Property Descriptor or Undefined.
|
|
|
|
|
///
|
|
|
|
|
/// These are 2 separate types and we can't do that here.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p
|
|
|
|
|
fn get_own_property(&self, prop: &Value) -> Property { |
|
|
|
|
let _timer = BoaProfiler::global().start_event("Object::get_own_property", "object"); |
|
|
|
|
debug_assert!(Property::is_property_key(prop)); |
|
|
|
|
// Prop could either be a String or Symbol
|
|
|
|
|
match *(*prop) { |
|
|
|
|
ValueData::String(ref st) => { |
|
|
|
|
match self.properties.get(st) { |
|
|
|
|
// If O does not have an own property with key P, return undefined.
|
|
|
|
|
// In this case we return a new empty Property
|
|
|
|
|
None => Property::default(), |
|
|
|
|
Some(ref v) => { |
|
|
|
|
let mut d = Property::default(); |
|
|
|
|
if v.is_data_descriptor() { |
|
|
|
|
d.value = v.value.clone(); |
|
|
|
|
d.writable = v.writable; |
|
|
|
|
} else { |
|
|
|
|
debug_assert!(v.is_accessor_descriptor()); |
|
|
|
|
d.get = v.get.clone(); |
|
|
|
|
d.set = v.set.clone(); |
|
|
|
|
} |
|
|
|
|
d.enumerable = v.enumerable; |
|
|
|
|
d.configurable = v.configurable; |
|
|
|
|
d |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
ValueData::Symbol(ref sym) => { |
|
|
|
|
let sym_id = (**sym) |
|
|
|
|
.borrow() |
|
|
|
|
.get_internal_slot("SymbolData") |
|
|
|
|
.to_string() |
|
|
|
|
.parse::<i32>() |
|
|
|
|
.expect("Could not get Symbol ID"); |
|
|
|
|
match self.sym_properties.get(&sym_id) { |
|
|
|
|
// If O does not have an own property with key P, return undefined.
|
|
|
|
|
// In this case we return a new empty Property
|
|
|
|
|
None => Property::default(), |
|
|
|
|
Some(ref v) => { |
|
|
|
|
let mut d = Property::default(); |
|
|
|
|
if v.is_data_descriptor() { |
|
|
|
|
d.value = v.value.clone(); |
|
|
|
|
d.writable = v.writable; |
|
|
|
|
} else { |
|
|
|
|
debug_assert!(v.is_accessor_descriptor()); |
|
|
|
|
d.get = v.get.clone(); |
|
|
|
|
d.set = v.set.clone(); |
|
|
|
|
} |
|
|
|
|
d.enumerable = v.enumerable; |
|
|
|
|
d.configurable = v.configurable; |
|
|
|
|
d |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
_ => Property::default(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Define an own property.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc
|
|
|
|
|
#[allow(clippy::option_unwrap_used)] |
|
|
|
|
fn define_own_property(&mut self, property_key: String, desc: Property) -> bool { |
|
|
|
|
let mut current = self.get_own_property(&Value::from(property_key.to_string())); |
|
|
|
|
let extensible = self.is_extensible(); |
|
|
|
|
|
|
|
|
|
// https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
|
|
|
|
|
// There currently isn't a property, lets create a new one
|
|
|
|
|
if current.value.is_none() || current.value.as_ref().expect("failed").is_undefined() { |
|
|
|
|
if !extensible { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
if desc.value.is_some() && desc.value.clone().unwrap().is_symbol() { |
|
|
|
|
let sym_id = desc |
|
|
|
|
.value |
|
|
|
|
.clone() |
|
|
|
|
.unwrap() |
|
|
|
|
.to_string() |
|
|
|
|
.parse::<i32>() |
|
|
|
|
.expect("parsing failed"); |
|
|
|
|
self.sym_properties.insert(sym_id, desc); |
|
|
|
|
} else { |
|
|
|
|
self.properties.insert(property_key, desc); |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
// If every field is absent we don't need to set anything
|
|
|
|
|
if desc.is_none() { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 4
|
|
|
|
|
if !current.configurable.unwrap_or(false) { |
|
|
|
|
if desc.configurable.is_some() && desc.configurable.unwrap() { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if desc.enumerable.is_some() |
|
|
|
|
&& (desc.enumerable.as_ref().unwrap() != current.enumerable.as_ref().unwrap()) |
|
|
|
|
{ |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 5
|
|
|
|
|
if desc.is_generic_descriptor() { |
|
|
|
|
// 6
|
|
|
|
|
} else if current.is_data_descriptor() != desc.is_data_descriptor() { |
|
|
|
|
// a
|
|
|
|
|
if !current.configurable.unwrap() { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
// b
|
|
|
|
|
if current.is_data_descriptor() { |
|
|
|
|
// Convert to accessor
|
|
|
|
|
current.value = None; |
|
|
|
|
current.writable = None; |
|
|
|
|
} else { |
|
|
|
|
// c
|
|
|
|
|
// convert to data
|
|
|
|
|
current.get = None; |
|
|
|
|
current.set = None; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if current.value.is_some() && current.value.clone().unwrap().is_symbol() { |
|
|
|
|
let sym_id = current |
|
|
|
|
.value |
|
|
|
|
.clone() |
|
|
|
|
.unwrap() |
|
|
|
|
.to_string() |
|
|
|
|
.parse::<i32>() |
|
|
|
|
.expect("parsing failed"); |
|
|
|
|
self.sym_properties.insert(sym_id, current); |
|
|
|
|
} else { |
|
|
|
|
self.properties.insert(property_key.clone(), current); |
|
|
|
|
} |
|
|
|
|
// 7
|
|
|
|
|
} else if current.is_data_descriptor() && desc.is_data_descriptor() { |
|
|
|
|
// a
|
|
|
|
|
if !current.configurable.unwrap() && !current.writable.unwrap() { |
|
|
|
|
if desc.writable.is_some() && desc.writable.unwrap() { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if desc.value.is_some() |
|
|
|
|
&& !same_value( |
|
|
|
|
&desc.value.clone().unwrap(), |
|
|
|
|
¤t.value.clone().unwrap(), |
|
|
|
|
false, |
|
|
|
|
) |
|
|
|
|
{ |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
// 8
|
|
|
|
|
} else { |
|
|
|
|
if !current.configurable.unwrap() { |
|
|
|
|
if desc.set.is_some() |
|
|
|
|
&& !same_value( |
|
|
|
|
&desc.set.clone().unwrap(), |
|
|
|
|
¤t.set.clone().unwrap(), |
|
|
|
|
false, |
|
|
|
|
) |
|
|
|
|
{ |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if desc.get.is_some() |
|
|
|
|
&& !same_value( |
|
|
|
|
&desc.get.clone().unwrap(), |
|
|
|
|
¤t.get.clone().unwrap(), |
|
|
|
|
false, |
|
|
|
|
) |
|
|
|
|
{ |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
impl Display for ObjectData { |
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { |
|
|
|
|
write!( |
|
|
|
|
f, |
|
|
|
|
"{}", |
|
|
|
|
match self { |
|
|
|
|
Self::Function(_) => "Function", |
|
|
|
|
Self::Array => "Array", |
|
|
|
|
Self::String(_) => "String", |
|
|
|
|
Self::Symbol(_) => "Symbol", |
|
|
|
|
Self::Error => "Error", |
|
|
|
|
Self::Ordinary => "Ordinary", |
|
|
|
|
Self::Boolean(_) => "Boolean", |
|
|
|
|
Self::Number(_) => "Number", |
|
|
|
|
Self::BigInt(_) => "BigInt", |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
// 9
|
|
|
|
|
self.properties.insert(property_key, desc); |
|
|
|
|
true |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Object { |
|
|
|
|
impl Default for Object { |
|
|
|
|
/// Return a new ObjectData struct, with `kind` set to Ordinary
|
|
|
|
|
pub fn default() -> Self { |
|
|
|
|
#[inline] |
|
|
|
|
fn default() -> Self { |
|
|
|
|
let mut object = Self { |
|
|
|
|
kind: ObjectKind::Ordinary, |
|
|
|
|
data: ObjectData::Ordinary, |
|
|
|
|
internal_slots: FxHashMap::default(), |
|
|
|
|
properties: FxHashMap::default(), |
|
|
|
|
sym_properties: FxHashMap::default(), |
|
|
|
|
symbol_properties: FxHashMap::default(), |
|
|
|
|
state: None, |
|
|
|
|
func: None, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
object.set_internal_slot("extensible", Value::from(true)); |
|
|
|
|
object |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Object { |
|
|
|
|
#[inline] |
|
|
|
|
pub fn new() -> Self { |
|
|
|
|
Default::default() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Return a new ObjectData struct, with `kind` set to Ordinary
|
|
|
|
|
pub fn function() -> Self { |
|
|
|
|
pub fn function(function: Function) -> Self { |
|
|
|
|
let _timer = BoaProfiler::global().start_event("Object::Function", "object"); |
|
|
|
|
|
|
|
|
|
let mut object = Self { |
|
|
|
|
kind: ObjectKind::Function, |
|
|
|
|
data: ObjectData::Function(function), |
|
|
|
|
internal_slots: FxHashMap::default(), |
|
|
|
|
properties: FxHashMap::default(), |
|
|
|
|
sym_properties: FxHashMap::default(), |
|
|
|
|
symbol_properties: FxHashMap::default(), |
|
|
|
|
state: None, |
|
|
|
|
func: None, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
object.set_internal_slot("extensible", Value::from(true)); |
|
|
|
@ -385,73 +149,48 @@ impl Object {
|
|
|
|
|
obj |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Set the function this object wraps
|
|
|
|
|
pub fn set_func(&mut self, val: Function) { |
|
|
|
|
self.func = Some(val); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Return a new Boolean object whose `[[BooleanData]]` internal slot is set to argument.
|
|
|
|
|
fn from_boolean(argument: &Value) -> Self { |
|
|
|
|
let mut obj = Self { |
|
|
|
|
kind: ObjectKind::Boolean, |
|
|
|
|
pub fn boolean(value: bool) -> Self { |
|
|
|
|
Self { |
|
|
|
|
data: ObjectData::Boolean(value), |
|
|
|
|
internal_slots: FxHashMap::default(), |
|
|
|
|
properties: FxHashMap::default(), |
|
|
|
|
sym_properties: FxHashMap::default(), |
|
|
|
|
symbol_properties: FxHashMap::default(), |
|
|
|
|
state: None, |
|
|
|
|
func: None, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
obj.internal_slots |
|
|
|
|
.insert("BooleanData".to_string(), argument.clone()); |
|
|
|
|
obj |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Return a new `Number` object whose `[[NumberData]]` internal slot is set to argument.
|
|
|
|
|
fn from_number(argument: &Value) -> Self { |
|
|
|
|
let mut obj = Self { |
|
|
|
|
kind: ObjectKind::Number, |
|
|
|
|
pub fn number(value: f64) -> Self { |
|
|
|
|
Self { |
|
|
|
|
data: ObjectData::Number(value), |
|
|
|
|
internal_slots: FxHashMap::default(), |
|
|
|
|
properties: FxHashMap::default(), |
|
|
|
|
sym_properties: FxHashMap::default(), |
|
|
|
|
symbol_properties: FxHashMap::default(), |
|
|
|
|
state: None, |
|
|
|
|
func: None, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
obj.internal_slots |
|
|
|
|
.insert("NumberData".to_string(), argument.clone()); |
|
|
|
|
obj |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Return a new `String` object whose `[[StringData]]` internal slot is set to argument.
|
|
|
|
|
fn from_string(argument: &Value) -> Self { |
|
|
|
|
let mut obj = Self { |
|
|
|
|
kind: ObjectKind::String, |
|
|
|
|
pub fn string(value: String) -> Self { |
|
|
|
|
Self { |
|
|
|
|
data: ObjectData::String(value), |
|
|
|
|
internal_slots: FxHashMap::default(), |
|
|
|
|
properties: FxHashMap::default(), |
|
|
|
|
sym_properties: FxHashMap::default(), |
|
|
|
|
symbol_properties: FxHashMap::default(), |
|
|
|
|
state: None, |
|
|
|
|
func: None, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
obj.internal_slots |
|
|
|
|
.insert("StringData".to_string(), argument.clone()); |
|
|
|
|
obj |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument.
|
|
|
|
|
fn from_bigint(argument: &Value) -> Self { |
|
|
|
|
let mut obj = Self { |
|
|
|
|
kind: ObjectKind::BigInt, |
|
|
|
|
pub fn bigint(value: BigInt) -> Self { |
|
|
|
|
Self { |
|
|
|
|
data: ObjectData::BigInt(value), |
|
|
|
|
internal_slots: FxHashMap::default(), |
|
|
|
|
properties: FxHashMap::default(), |
|
|
|
|
sym_properties: FxHashMap::default(), |
|
|
|
|
symbol_properties: FxHashMap::default(), |
|
|
|
|
state: None, |
|
|
|
|
func: None, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
obj.internal_slots |
|
|
|
|
.insert("BigIntData".to_string(), argument.clone()); |
|
|
|
|
obj |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Converts the `Value` to an `Object` type.
|
|
|
|
@ -461,11 +200,12 @@ impl Object {
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-toobject
|
|
|
|
|
pub fn from(value: &Value) -> Result<Self, ()> { |
|
|
|
|
match *value.deref().borrow() { |
|
|
|
|
ValueData::Boolean(_) => Ok(Self::from_boolean(value)), |
|
|
|
|
ValueData::Rational(_) => Ok(Self::from_number(value)), |
|
|
|
|
ValueData::String(_) => Ok(Self::from_string(value)), |
|
|
|
|
ValueData::BigInt(_) => Ok(Self::from_bigint(value)), |
|
|
|
|
match *value.data() { |
|
|
|
|
ValueData::Boolean(a) => Ok(Self::boolean(a)), |
|
|
|
|
ValueData::Rational(a) => Ok(Self::number(a)), |
|
|
|
|
ValueData::Integer(a) => Ok(Self::number(f64::from(a))), |
|
|
|
|
ValueData::String(ref a) => Ok(Self::string(a.clone())), |
|
|
|
|
ValueData::BigInt(ref bigint) => Ok(Self::bigint(bigint.clone())), |
|
|
|
|
ValueData::Object(ref obj) => Ok((*obj).deref().borrow().clone()), |
|
|
|
|
_ => Err(()), |
|
|
|
|
} |
|
|
|
@ -477,11 +217,9 @@ impl Object {
|
|
|
|
|
/// - [EcmaScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-iscallable
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn is_callable(&self) -> bool { |
|
|
|
|
match self.func { |
|
|
|
|
Some(ref function) => function.is_callable(), |
|
|
|
|
None => false, |
|
|
|
|
} |
|
|
|
|
matches!(self.data, ObjectData::Function(ref f) if f.is_callable()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// It determines if Object is a function object with a [[Construct]] internal method.
|
|
|
|
@ -490,58 +228,168 @@ impl Object {
|
|
|
|
|
/// - [EcmaScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-isconstructor
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn is_constructable(&self) -> bool { |
|
|
|
|
match self.func { |
|
|
|
|
Some(ref function) => function.is_constructable(), |
|
|
|
|
None => false, |
|
|
|
|
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, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Defines the different types of objects.
|
|
|
|
|
#[derive(Finalize, Debug, Copy, Clone, Eq, PartialEq)] |
|
|
|
|
pub enum ObjectKind { |
|
|
|
|
Function, |
|
|
|
|
Array, |
|
|
|
|
String, |
|
|
|
|
Symbol, |
|
|
|
|
Error, |
|
|
|
|
Ordinary, |
|
|
|
|
Boolean, |
|
|
|
|
Number, |
|
|
|
|
BigInt, |
|
|
|
|
} |
|
|
|
|
/// Checks if it a `String` object.
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn is_string(&self) -> bool { |
|
|
|
|
matches!(self.data, ObjectData::String(_)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Display for ObjectKind { |
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { |
|
|
|
|
write!( |
|
|
|
|
f, |
|
|
|
|
"{}", |
|
|
|
|
match self { |
|
|
|
|
Self::Function => "Function", |
|
|
|
|
Self::Array => "Array", |
|
|
|
|
Self::String => "String", |
|
|
|
|
Self::Symbol => "Symbol", |
|
|
|
|
Self::Error => "Error", |
|
|
|
|
Self::Ordinary => "Ordinary", |
|
|
|
|
Self::Boolean => "Boolean", |
|
|
|
|
Self::Number => "Number", |
|
|
|
|
Self::BigInt => "BigInt", |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
#[inline] |
|
|
|
|
pub fn as_string(&self) -> Option<&str> { |
|
|
|
|
match self.data { |
|
|
|
|
ObjectData::String(ref string) => Some(string.as_str()), |
|
|
|
|
_ => None, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Trace` implementation for `ObjectKind`.
|
|
|
|
|
///
|
|
|
|
|
/// This is indeed safe, but we need to mark this as an empty trace because neither
|
|
|
|
|
// `NativeFunctionData` nor Node hold any GC'd objects, but Gc doesn't know that. So we need to
|
|
|
|
|
/// signal it manually. `rust-gc` does not have a `Trace` implementation for `fn(_, _, _)`.
|
|
|
|
|
///
|
|
|
|
|
/// <https://github.com/Manishearth/rust-gc/blob/master/gc/src/trace.rs>
|
|
|
|
|
/// Waiting on <https://github.com/Manishearth/rust-gc/issues/87> until we can derive Copy
|
|
|
|
|
unsafe impl Trace for ObjectKind { |
|
|
|
|
unsafe_empty_trace!(); |
|
|
|
|
/// 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<&Symbol> { |
|
|
|
|
match self.data { |
|
|
|
|
ObjectData::Symbol(ref symbol) => Some(symbol), |
|
|
|
|
_ => 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 an ordinary object.
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn is_ordinary(&self) -> bool { |
|
|
|
|
matches!(self.data, ObjectData::Ordinary) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn internal_slots(&self) -> &FxHashMap<String, Value> { |
|
|
|
|
&self.internal_slots |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn internal_slots_mut(&mut self) -> &mut FxHashMap<String, Value> { |
|
|
|
|
&mut self.internal_slots |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn properties(&self) -> &FxHashMap<String, Property> { |
|
|
|
|
&self.properties |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn properties_mut(&mut self) -> &mut FxHashMap<String, Property> { |
|
|
|
|
&mut self.properties |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn symbol_properties(&self) -> &FxHashMap<u32, Property> { |
|
|
|
|
&self.symbol_properties |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn symbol_properties_mut(&mut self) -> &mut FxHashMap<u32, Property> { |
|
|
|
|
&mut self.symbol_properties |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn state(&self) -> &Option<InternalStateCell> { |
|
|
|
|
&self.state |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn state_mut(&mut self) -> &mut Option<InternalStateCell> { |
|
|
|
|
&mut self.state |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Create a new object.
|
|
|
|
@ -633,7 +481,6 @@ pub fn create(global: &Value) -> Value {
|
|
|
|
|
|
|
|
|
|
let object = make_constructor_fn("Object", 1, make_object, global, prototype, true); |
|
|
|
|
|
|
|
|
|
object.set_field("length", Value::from(1)); |
|
|
|
|
make_builtin_fn(set_prototype_of, "setPrototypeOf", &object, 2); |
|
|
|
|
make_builtin_fn(get_prototype_of, "getPrototypeOf", &object, 1); |
|
|
|
|
make_builtin_fn(define_property, "defineProperty", &object, 3); |
|
|
|
@ -643,7 +490,8 @@ pub fn create(global: &Value) -> Value {
|
|
|
|
|
|
|
|
|
|
/// Initialise the `Object` object on the global object.
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn init(global: &Value) { |
|
|
|
|
pub fn init(global: &Value) -> (&str, Value) { |
|
|
|
|
let _timer = BoaProfiler::global().start_event("object", "init"); |
|
|
|
|
global.set_field("Object", create(global)); |
|
|
|
|
|
|
|
|
|
("Object", create(global)) |
|
|
|
|
} |
|
|
|
|