|
|
@ -10,17 +10,14 @@ pub mod val_type; |
|
|
|
pub use crate::builtins::value::val_type::Type; |
|
|
|
pub use crate::builtins::value::val_type::Type; |
|
|
|
|
|
|
|
|
|
|
|
use crate::builtins::{ |
|
|
|
use crate::builtins::{ |
|
|
|
object::{ |
|
|
|
function::Function, |
|
|
|
internal_methods_trait::ObjectInternalMethods, InternalState, InternalStateCell, Object, |
|
|
|
object::{InternalState, InternalStateCell, Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, |
|
|
|
ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
property::Property, |
|
|
|
property::Property, |
|
|
|
BigInt, Function, |
|
|
|
BigInt, Symbol, |
|
|
|
}; |
|
|
|
}; |
|
|
|
use crate::BoaProfiler; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use crate::exec::Interpreter; |
|
|
|
use crate::exec::Interpreter; |
|
|
|
use gc::{Finalize, Gc, GcCell, GcCellRef, Trace}; |
|
|
|
use crate::BoaProfiler; |
|
|
|
|
|
|
|
use gc::{Finalize, Gc, GcCell, GcCellRef, GcCellRefMut, Trace}; |
|
|
|
use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue}; |
|
|
|
use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue}; |
|
|
|
use std::{ |
|
|
|
use std::{ |
|
|
|
any::Any, |
|
|
|
any::Any, |
|
|
@ -28,18 +25,20 @@ use std::{ |
|
|
|
convert::TryFrom, |
|
|
|
convert::TryFrom, |
|
|
|
f64::NAN, |
|
|
|
f64::NAN, |
|
|
|
fmt::{self, Display}, |
|
|
|
fmt::{self, Display}, |
|
|
|
ops::{Add, BitAnd, BitOr, BitXor, Deref, DerefMut, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub}, |
|
|
|
ops::{Add, BitAnd, BitOr, BitXor, Deref, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub}, |
|
|
|
str::FromStr, |
|
|
|
str::FromStr, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
pub mod conversions; |
|
|
|
pub mod conversions; |
|
|
|
pub mod display; |
|
|
|
pub mod display; |
|
|
|
pub mod equality; |
|
|
|
pub mod equality; |
|
|
|
|
|
|
|
pub mod hash; |
|
|
|
pub mod operations; |
|
|
|
pub mod operations; |
|
|
|
|
|
|
|
|
|
|
|
pub use conversions::*; |
|
|
|
pub use conversions::*; |
|
|
|
pub(crate) use display::display_obj; |
|
|
|
pub(crate) use display::display_obj; |
|
|
|
pub use equality::*; |
|
|
|
pub use equality::*; |
|
|
|
|
|
|
|
pub use hash::*; |
|
|
|
pub use operations::*; |
|
|
|
pub use operations::*; |
|
|
|
|
|
|
|
|
|
|
|
/// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`)
|
|
|
|
/// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`)
|
|
|
@ -48,7 +47,7 @@ pub type ResultValue = Result<Value, Value>; |
|
|
|
|
|
|
|
|
|
|
|
/// A Garbage-collected Javascript value as represented in the interpreter.
|
|
|
|
/// A Garbage-collected Javascript value as represented in the interpreter.
|
|
|
|
#[derive(Debug, Clone, Trace, Finalize, Default)] |
|
|
|
#[derive(Debug, Clone, Trace, Finalize, Default)] |
|
|
|
pub struct Value(pub(crate) Gc<ValueData>); |
|
|
|
pub struct Value(Gc<ValueData>); |
|
|
|
|
|
|
|
|
|
|
|
impl Value { |
|
|
|
impl Value { |
|
|
|
/// Creates a new `undefined` value.
|
|
|
|
/// Creates a new `undefined` value.
|
|
|
@ -63,6 +62,12 @@ impl Value { |
|
|
|
Self(Gc::new(ValueData::Null)) |
|
|
|
Self(Gc::new(ValueData::Null)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Creates a new number with `NaN` value.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
|
|
|
pub fn nan() -> Self { |
|
|
|
|
|
|
|
Self::number(NAN) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Creates a new string value.
|
|
|
|
/// Creates a new string value.
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn string<S>(value: S) -> Self |
|
|
|
pub fn string<S>(value: S) -> Self |
|
|
@ -117,6 +122,12 @@ impl Value { |
|
|
|
Self(Gc::new(ValueData::Object(Box::new(GcCell::new(object))))) |
|
|
|
Self(Gc::new(ValueData::Object(Box::new(GcCell::new(object))))) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Creates a new symbol value.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
|
|
|
pub fn symbol(symbol: Symbol) -> Self { |
|
|
|
|
|
|
|
Self(Gc::new(ValueData::Symbol(symbol))) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Gets the underlying `ValueData` structure.
|
|
|
|
/// Gets the underlying `ValueData` structure.
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn data(&self) -> &ValueData { |
|
|
|
pub fn data(&self) -> &ValueData { |
|
|
@ -145,12 +156,12 @@ impl Value { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Similar to `new_object`, but you can pass a prototype to create from, plus a kind
|
|
|
|
/// Similar to `new_object`, but you can pass a prototype to create from, plus a kind
|
|
|
|
pub fn new_object_from_prototype(proto: Value, kind: ObjectKind) -> Self { |
|
|
|
pub fn new_object_from_prototype(proto: Value, data: ObjectData) -> Self { |
|
|
|
let mut object = Object::default(); |
|
|
|
let mut object = Object::default(); |
|
|
|
object.kind = kind; |
|
|
|
object.data = data; |
|
|
|
|
|
|
|
|
|
|
|
object |
|
|
|
object |
|
|
|
.internal_slots |
|
|
|
.internal_slots_mut() |
|
|
|
.insert(INSTANCE_PROTOTYPE.to_string(), proto); |
|
|
|
.insert(INSTANCE_PROTOTYPE.to_string(), proto); |
|
|
|
|
|
|
|
|
|
|
|
Self::object(object) |
|
|
|
Self::object(object) |
|
|
@ -175,7 +186,7 @@ impl Value { |
|
|
|
.get_field("Array") |
|
|
|
.get_field("Array") |
|
|
|
.get_field(PROTOTYPE); |
|
|
|
.get_field(PROTOTYPE); |
|
|
|
let new_obj = |
|
|
|
let new_obj = |
|
|
|
Value::new_object_from_prototype(global_array_prototype, ObjectKind::Array); |
|
|
|
Value::new_object_from_prototype(global_array_prototype, ObjectData::Array); |
|
|
|
let length = vs.len(); |
|
|
|
let length = vs.len(); |
|
|
|
for (idx, json) in vs.into_iter().enumerate() { |
|
|
|
for (idx, json) in vs.into_iter().enumerate() { |
|
|
|
new_obj.set_property( |
|
|
|
new_obj.set_property( |
|
|
@ -216,9 +227,9 @@ impl Value { |
|
|
|
ValueData::Null => Ok(JSONValue::Null), |
|
|
|
ValueData::Null => Ok(JSONValue::Null), |
|
|
|
ValueData::Boolean(b) => Ok(JSONValue::Bool(b)), |
|
|
|
ValueData::Boolean(b) => Ok(JSONValue::Bool(b)), |
|
|
|
ValueData::Object(ref obj) => { |
|
|
|
ValueData::Object(ref obj) => { |
|
|
|
if obj.borrow().kind == ObjectKind::Array { |
|
|
|
if obj.borrow().is_array() { |
|
|
|
let mut arr: Vec<JSONValue> = Vec::new(); |
|
|
|
let mut arr: Vec<JSONValue> = Vec::new(); |
|
|
|
for k in obj.borrow().properties.keys() { |
|
|
|
for k in obj.borrow().properties().keys() { |
|
|
|
if k != "length" { |
|
|
|
if k != "length" { |
|
|
|
let value = self.get_field(k.to_string()); |
|
|
|
let value = self.get_field(k.to_string()); |
|
|
|
if value.is_undefined() || value.is_function() { |
|
|
|
if value.is_undefined() || value.is_function() { |
|
|
@ -231,7 +242,7 @@ impl Value { |
|
|
|
Ok(JSONValue::Array(arr)) |
|
|
|
Ok(JSONValue::Array(arr)) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
let mut new_obj = Map::new(); |
|
|
|
let mut new_obj = Map::new(); |
|
|
|
for k in obj.borrow().properties.keys() { |
|
|
|
for k in obj.borrow().properties().keys() { |
|
|
|
let key = k.clone(); |
|
|
|
let key = k.clone(); |
|
|
|
let value = self.get_field(k.to_string()); |
|
|
|
let value = self.get_field(k.to_string()); |
|
|
|
if !value.is_undefined() && !value.is_function() { |
|
|
|
if !value.is_undefined() && !value.is_function() { |
|
|
@ -246,9 +257,9 @@ impl Value { |
|
|
|
.map(JSONValue::Number) |
|
|
|
.map(JSONValue::Number) |
|
|
|
.unwrap_or(JSONValue::Null)), |
|
|
|
.unwrap_or(JSONValue::Null)), |
|
|
|
ValueData::Integer(val) => Ok(JSONValue::Number(JSONNumber::from(val))), |
|
|
|
ValueData::Integer(val) => Ok(JSONValue::Number(JSONNumber::from(val))), |
|
|
|
ValueData::BigInt(_) => Err(interpreter |
|
|
|
ValueData::BigInt(_) => { |
|
|
|
.throw_type_error("BigInt value can't be serialized in JSON") |
|
|
|
Err(interpreter.construct_type_error("BigInt value can't be serialized in JSON")) |
|
|
|
.expect_err("throw_type_error should always return an error")), |
|
|
|
} |
|
|
|
ValueData::Symbol(_) | ValueData::Undefined => { |
|
|
|
ValueData::Symbol(_) | ValueData::Undefined => { |
|
|
|
unreachable!("Symbols and Undefined JSON Values depend on parent type"); |
|
|
|
unreachable!("Symbols and Undefined JSON Values depend on parent type"); |
|
|
|
} |
|
|
|
} |
|
|
@ -283,8 +294,8 @@ pub enum ValueData { |
|
|
|
BigInt(BigInt), |
|
|
|
BigInt(BigInt), |
|
|
|
/// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values.
|
|
|
|
/// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values.
|
|
|
|
Object(Box<GcCell<Object>>), |
|
|
|
Object(Box<GcCell<Object>>), |
|
|
|
/// `Symbol` - A Symbol Type - Internally Symbols are similar to objects, except there are no properties, only internal slots.
|
|
|
|
/// `Symbol` - A Symbol Primitive type.
|
|
|
|
Symbol(Box<GcCell<Object>>), |
|
|
|
Symbol(Symbol), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl ValueData { |
|
|
|
impl ValueData { |
|
|
@ -302,65 +313,65 @@ impl ValueData { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is an object
|
|
|
|
/// Returns true if the value is an object
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn is_object(&self) -> bool { |
|
|
|
pub fn is_object(&self) -> bool { |
|
|
|
|
|
|
|
matches!(self, Self::Object(_)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
|
|
|
pub fn as_object(&self) -> Option<GcCellRef<'_, Object>> { |
|
|
|
match *self { |
|
|
|
match *self { |
|
|
|
Self::Object(_) => true, |
|
|
|
Self::Object(ref o) => Some(o.borrow()), |
|
|
|
_ => false, |
|
|
|
_ => None, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is a symbol
|
|
|
|
#[inline] |
|
|
|
pub fn is_symbol(&self) -> bool { |
|
|
|
pub fn as_object_mut(&self) -> Option<GcCellRefMut<'_, Object>> { |
|
|
|
match *self { |
|
|
|
match *self { |
|
|
|
Self::Symbol(_) => true, |
|
|
|
Self::Object(ref o) => Some(o.borrow_mut()), |
|
|
|
_ => false, |
|
|
|
_ => None, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is a symbol.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
|
|
|
pub fn is_symbol(&self) -> bool { |
|
|
|
|
|
|
|
matches!(self, Self::Symbol(_)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is a function
|
|
|
|
/// Returns true if the value is a function
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn is_function(&self) -> bool { |
|
|
|
pub fn is_function(&self) -> bool { |
|
|
|
match *self { |
|
|
|
matches!(self, Self::Object(o) if o.borrow().is_function()) |
|
|
|
Self::Object(ref o) => { |
|
|
|
|
|
|
|
let borrowed_obj = o.borrow(); |
|
|
|
|
|
|
|
borrowed_obj.is_callable() || borrowed_obj.is_constructable() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
_ => false, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is undefined.
|
|
|
|
/// Returns true if the value is undefined.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn is_undefined(&self) -> bool { |
|
|
|
pub fn is_undefined(&self) -> bool { |
|
|
|
match *self { |
|
|
|
matches!(self, Self::Undefined) |
|
|
|
Self::Undefined => true, |
|
|
|
|
|
|
|
_ => false, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is null.
|
|
|
|
/// Returns true if the value is null.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn is_null(&self) -> bool { |
|
|
|
pub fn is_null(&self) -> bool { |
|
|
|
match *self { |
|
|
|
matches!(self, Self::Null) |
|
|
|
Self::Null => true, |
|
|
|
|
|
|
|
_ => false, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is null or undefined.
|
|
|
|
/// Returns true if the value is null or undefined.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn is_null_or_undefined(&self) -> bool { |
|
|
|
pub fn is_null_or_undefined(&self) -> bool { |
|
|
|
match *self { |
|
|
|
matches!(self, Self::Null | Self::Undefined) |
|
|
|
Self::Null | Self::Undefined => true, |
|
|
|
|
|
|
|
_ => false, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is a 64-bit floating-point number.
|
|
|
|
/// Returns true if the value is a 64-bit floating-point number.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn is_double(&self) -> bool { |
|
|
|
pub fn is_double(&self) -> bool { |
|
|
|
match *self { |
|
|
|
matches!(self, Self::Rational(_)) |
|
|
|
Self::Rational(_) => true, |
|
|
|
|
|
|
|
_ => false, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is integer.
|
|
|
|
/// Returns true if the value is integer.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[allow(clippy::float_cmp)] |
|
|
|
#[allow(clippy::float_cmp)] |
|
|
|
pub fn is_integer(&self) -> bool { |
|
|
|
pub fn is_integer(&self) -> bool { |
|
|
|
// If it can fit in a i32 and the trucated version is
|
|
|
|
// If it can fit in a i32 and the trucated version is
|
|
|
@ -374,39 +385,40 @@ impl ValueData { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is a number
|
|
|
|
/// Returns true if the value is a number.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn is_number(&self) -> bool { |
|
|
|
pub fn is_number(&self) -> bool { |
|
|
|
match self { |
|
|
|
matches!(self, Self::Rational(_) | Self::Integer(_)) |
|
|
|
Self::Rational(_) | Self::Integer(_) => true, |
|
|
|
|
|
|
|
_ => false, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is a string
|
|
|
|
/// Returns true if the value is a string.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn is_string(&self) -> bool { |
|
|
|
pub fn is_string(&self) -> bool { |
|
|
|
match *self { |
|
|
|
matches!(self, Self::String(_)) |
|
|
|
Self::String(_) => true, |
|
|
|
|
|
|
|
_ => false, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is a boolean
|
|
|
|
/// Returns true if the value is a boolean.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn is_boolean(&self) -> bool { |
|
|
|
pub fn is_boolean(&self) -> bool { |
|
|
|
match *self { |
|
|
|
matches!(self, Self::Boolean(_)) |
|
|
|
Self::Boolean(_) => true, |
|
|
|
|
|
|
|
_ => false, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is a bigint
|
|
|
|
/// Returns true if the value is a bigint.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn is_bigint(&self) -> bool { |
|
|
|
pub fn is_bigint(&self) -> bool { |
|
|
|
match *self { |
|
|
|
matches!(self, Self::BigInt(_)) |
|
|
|
Self::BigInt(_) => true, |
|
|
|
} |
|
|
|
_ => false, |
|
|
|
|
|
|
|
|
|
|
|
/// Returns an optional reference to a `BigInt` if the value is a BigInt primitive.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
|
|
|
pub fn as_bigint(&self) -> Option<&BigInt> { |
|
|
|
|
|
|
|
match self { |
|
|
|
|
|
|
|
Self::BigInt(bigint) => Some(bigint), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the value is true
|
|
|
|
/// Returns true if the value is true.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// [toBoolean](https://tc39.es/ecma262/#sec-toboolean)
|
|
|
|
/// [toBoolean](https://tc39.es/ecma262/#sec-toboolean)
|
|
|
|
pub fn is_true(&self) -> bool { |
|
|
|
pub fn is_true(&self) -> bool { |
|
|
@ -466,23 +478,27 @@ impl ValueData { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn as_object(&self) -> Option<GcCellRef<'_, Object>> { |
|
|
|
/// Creates a new boolean value from the input
|
|
|
|
|
|
|
|
pub fn to_boolean(&self) -> bool { |
|
|
|
match *self { |
|
|
|
match *self { |
|
|
|
ValueData::Object(ref o) => Some(o.borrow()), |
|
|
|
Self::Undefined | Self::Null => false, |
|
|
|
_ => None, |
|
|
|
Self::Symbol(_) | Self::Object(_) => true, |
|
|
|
|
|
|
|
Self::String(ref s) if !s.is_empty() => true, |
|
|
|
|
|
|
|
Self::Rational(n) if n != 0.0 && !n.is_nan() => true, |
|
|
|
|
|
|
|
Self::Integer(n) if n != 0 => true, |
|
|
|
|
|
|
|
Self::BigInt(ref n) if *n != 0 => true, |
|
|
|
|
|
|
|
Self::Boolean(v) => v, |
|
|
|
|
|
|
|
_ => false, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Removes a property from a Value object.
|
|
|
|
/// Removes a property from a Value object.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// It will return a boolean based on if the value was removed, if there was no value to remove false is returned
|
|
|
|
/// It will return a boolean based on if the value was removed, if there was no value to remove false is returned.
|
|
|
|
pub fn remove_property(&self, field: &str) -> bool { |
|
|
|
pub fn remove_property(&self, field: &str) -> bool { |
|
|
|
let removed = match *self { |
|
|
|
self.as_object_mut() |
|
|
|
Self::Object(ref obj) => obj.borrow_mut().deref_mut().properties.remove(field), |
|
|
|
.and_then(|mut x| x.properties_mut().remove(field)) |
|
|
|
_ => None, |
|
|
|
.is_some() |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
removed.is_some() |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Resolve the property in the object.
|
|
|
|
/// Resolve the property in the object.
|
|
|
@ -492,36 +508,22 @@ impl ValueData { |
|
|
|
let _timer = BoaProfiler::global().start_event("Value::get_property", "value"); |
|
|
|
let _timer = BoaProfiler::global().start_event("Value::get_property", "value"); |
|
|
|
// Spidermonkey has its own GetLengthProperty: https://searchfox.org/mozilla-central/source/js/src/vm/Interpreter-inl.h#154
|
|
|
|
// Spidermonkey has its own GetLengthProperty: https://searchfox.org/mozilla-central/source/js/src/vm/Interpreter-inl.h#154
|
|
|
|
// This is only for primitive strings, String() objects have their lengths calculated in string.rs
|
|
|
|
// This is only for primitive strings, String() objects have their lengths calculated in string.rs
|
|
|
|
if self.is_string() && field == "length" { |
|
|
|
match self { |
|
|
|
if let Self::String(ref s) = *self { |
|
|
|
Self::Undefined => None, |
|
|
|
return Some(Property::default().value(Value::from(s.len()))); |
|
|
|
Self::String(ref s) if field == "length" => { |
|
|
|
} |
|
|
|
Some(Property::default().value(Value::from(s.chars().count()))) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self.is_undefined() { |
|
|
|
|
|
|
|
return None; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let obj: Object = match *self { |
|
|
|
|
|
|
|
Self::Object(ref obj) => { |
|
|
|
|
|
|
|
let hash = obj.clone(); |
|
|
|
|
|
|
|
// TODO: This will break, we should return a GcCellRefMut instead
|
|
|
|
|
|
|
|
// into_inner will consume the wrapped value and remove it from the hashmap
|
|
|
|
|
|
|
|
hash.into_inner() |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
Self::Symbol(ref obj) => { |
|
|
|
Self::Object(ref object) => { |
|
|
|
let hash = obj.clone(); |
|
|
|
let object = object.borrow(); |
|
|
|
hash.into_inner() |
|
|
|
match object.properties().get(field) { |
|
|
|
|
|
|
|
Some(value) => Some(value.clone()), |
|
|
|
|
|
|
|
None => object |
|
|
|
|
|
|
|
.internal_slots() |
|
|
|
|
|
|
|
.get(INSTANCE_PROTOTYPE) |
|
|
|
|
|
|
|
.and_then(|value| value.get_property(field)), |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
_ => return None, |
|
|
|
_ => None, |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match obj.properties.get(field) { |
|
|
|
|
|
|
|
Some(val) => Some(val.clone()), |
|
|
|
|
|
|
|
None => match obj.internal_slots.get(&INSTANCE_PROTOTYPE.to_string()) { |
|
|
|
|
|
|
|
Some(value) => value.get_property(field), |
|
|
|
|
|
|
|
None => None, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -537,18 +539,14 @@ impl ValueData { |
|
|
|
configurable: Option<bool>, |
|
|
|
configurable: Option<bool>, |
|
|
|
) { |
|
|
|
) { |
|
|
|
let _timer = BoaProfiler::global().start_event("Value::update_property", "value"); |
|
|
|
let _timer = BoaProfiler::global().start_event("Value::update_property", "value"); |
|
|
|
let obj: Option<Object> = match self { |
|
|
|
|
|
|
|
Self::Object(ref obj) => Some(obj.borrow_mut().deref_mut().clone()), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(mut obj_data) = obj { |
|
|
|
if let Some(ref mut object) = self.as_object_mut() { |
|
|
|
// Use value, or walk up the prototype chain
|
|
|
|
// Use value, or walk up the prototype chain
|
|
|
|
if let Some(ref mut prop) = obj_data.properties.get_mut(field) { |
|
|
|
if let Some(ref mut property) = object.properties_mut().get_mut(field) { |
|
|
|
prop.value = value; |
|
|
|
property.value = value; |
|
|
|
prop.enumerable = enumerable; |
|
|
|
property.enumerable = enumerable; |
|
|
|
prop.writable = writable; |
|
|
|
property.writable = writable; |
|
|
|
prop.configurable = configurable; |
|
|
|
property.configurable = configurable; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -556,24 +554,13 @@ impl ValueData { |
|
|
|
/// Resolve the property in the object.
|
|
|
|
/// Resolve the property in the object.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// Returns a copy of the Property.
|
|
|
|
/// Returns a copy of the Property.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn get_internal_slot(&self, field: &str) -> Value { |
|
|
|
pub fn get_internal_slot(&self, field: &str) -> Value { |
|
|
|
let _timer = BoaProfiler::global().start_event("Value::get_internal_slot", "value"); |
|
|
|
let _timer = BoaProfiler::global().start_event("Value::get_internal_slot", "value"); |
|
|
|
let obj: Object = match *self { |
|
|
|
|
|
|
|
Self::Object(ref obj) => { |
|
|
|
|
|
|
|
let hash = obj.clone(); |
|
|
|
|
|
|
|
hash.into_inner() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Self::Symbol(ref obj) => { |
|
|
|
|
|
|
|
let hash = obj.clone(); |
|
|
|
|
|
|
|
hash.into_inner() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
_ => return Value::undefined(), |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match obj.internal_slots.get(field) { |
|
|
|
self.as_object() |
|
|
|
Some(val) => val.clone(), |
|
|
|
.and_then(|x| x.internal_slots().get(field).cloned()) |
|
|
|
None => Value::undefined(), |
|
|
|
.unwrap_or_else(Value::undefined) |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist
|
|
|
|
/// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist
|
|
|
@ -615,21 +602,15 @@ impl ValueData { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Check whether an object has an internal state set.
|
|
|
|
/// Check whether an object has an internal state set.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn has_internal_state(&self) -> bool { |
|
|
|
pub fn has_internal_state(&self) -> bool { |
|
|
|
if let Self::Object(ref obj) = *self { |
|
|
|
matches!(self.as_object(), Some(object) if object.state().is_some()) |
|
|
|
obj.borrow().state.is_some() |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Get the internal state of an object.
|
|
|
|
/// Get the internal state of an object.
|
|
|
|
pub fn get_internal_state(&self) -> Option<InternalStateCell> { |
|
|
|
pub fn get_internal_state(&self) -> Option<InternalStateCell> { |
|
|
|
if let Self::Object(ref obj) = *self { |
|
|
|
self.as_object() |
|
|
|
obj.borrow().state.as_ref().cloned() |
|
|
|
.and_then(|object| object.state().as_ref().cloned()) |
|
|
|
} else { |
|
|
|
|
|
|
|
None |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Run a function with a reference to the internal state.
|
|
|
|
/// Run a function with a reference to the internal state.
|
|
|
@ -638,14 +619,14 @@ impl ValueData { |
|
|
|
///
|
|
|
|
///
|
|
|
|
/// This will panic if this value doesn't have an internal state or if the internal state doesn't
|
|
|
|
/// This will panic if this value doesn't have an internal state or if the internal state doesn't
|
|
|
|
/// have the concrete type `S`.
|
|
|
|
/// have the concrete type `S`.
|
|
|
|
pub fn with_internal_state_ref<S: Any + InternalState, R, F: FnOnce(&S) -> R>( |
|
|
|
pub fn with_internal_state_ref<S, R, F>(&self, f: F) -> R |
|
|
|
&self, |
|
|
|
where |
|
|
|
f: F, |
|
|
|
S: Any + InternalState, |
|
|
|
) -> R { |
|
|
|
F: FnOnce(&S) -> R, |
|
|
|
if let Self::Object(ref obj) = *self { |
|
|
|
{ |
|
|
|
let o = obj.borrow(); |
|
|
|
if let Some(object) = self.as_object() { |
|
|
|
let state = o |
|
|
|
let state = object |
|
|
|
.state |
|
|
|
.state() |
|
|
|
.as_ref() |
|
|
|
.as_ref() |
|
|
|
.expect("no state") |
|
|
|
.expect("no state") |
|
|
|
.downcast_ref() |
|
|
|
.downcast_ref() |
|
|
@ -662,14 +643,14 @@ impl ValueData { |
|
|
|
///
|
|
|
|
///
|
|
|
|
/// This will panic if this value doesn't have an internal state or if the internal state doesn't
|
|
|
|
/// This will panic if this value doesn't have an internal state or if the internal state doesn't
|
|
|
|
/// have the concrete type `S`.
|
|
|
|
/// have the concrete type `S`.
|
|
|
|
pub fn with_internal_state_mut<S: Any + InternalState, R, F: FnOnce(&mut S) -> R>( |
|
|
|
pub fn with_internal_state_mut<S, R, F>(&self, f: F) -> R |
|
|
|
&self, |
|
|
|
where |
|
|
|
f: F, |
|
|
|
S: Any + InternalState, |
|
|
|
) -> R { |
|
|
|
F: FnOnce(&mut S) -> R, |
|
|
|
if let Self::Object(ref obj) = *self { |
|
|
|
{ |
|
|
|
let mut o = obj.borrow_mut(); |
|
|
|
if let Some(mut object) = self.as_object_mut() { |
|
|
|
let state = o |
|
|
|
let state = object |
|
|
|
.state |
|
|
|
.state_mut() |
|
|
|
.as_mut() |
|
|
|
.as_mut() |
|
|
|
.expect("no state") |
|
|
|
.expect("no state") |
|
|
|
.downcast_mut() |
|
|
|
.downcast_mut() |
|
|
@ -680,7 +661,8 @@ impl ValueData { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Check to see if the Value has the field, mainly used by environment records
|
|
|
|
/// Check to see if the Value has the field, mainly used by environment records.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
pub fn has_field(&self, field: &str) -> bool { |
|
|
|
pub fn has_field(&self, field: &str) -> bool { |
|
|
|
let _timer = BoaProfiler::global().start_event("Value::has_field", "value"); |
|
|
|
let _timer = BoaProfiler::global().start_event("Value::has_field", "value"); |
|
|
|
self.get_property(field).is_some() |
|
|
|
self.get_property(field).is_some() |
|
|
@ -698,7 +680,7 @@ impl ValueData { |
|
|
|
let val = val.into(); |
|
|
|
let val = val.into(); |
|
|
|
|
|
|
|
|
|
|
|
if let Self::Object(ref obj) = *self { |
|
|
|
if let Self::Object(ref obj) = *self { |
|
|
|
if obj.borrow().kind == ObjectKind::Array { |
|
|
|
if obj.borrow().is_array() { |
|
|
|
if let Ok(num) = field.to_string().parse::<usize>() { |
|
|
|
if let Ok(num) = field.to_string().parse::<usize>() { |
|
|
|
if num > 0 { |
|
|
|
if num > 0 { |
|
|
|
let len = i32::from(&self.get_field("length")); |
|
|
|
let len = i32::from(&self.get_field("length")); |
|
|
@ -722,53 +704,50 @@ impl ValueData { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Set the private field in the value
|
|
|
|
/// Set the private field in the value
|
|
|
|
pub fn set_internal_slot(&self, field: &str, val: Value) -> Value { |
|
|
|
pub fn set_internal_slot(&self, field: &str, value: Value) -> Value { |
|
|
|
let _timer = BoaProfiler::global().start_event("Value::set_internal_slot", "exec"); |
|
|
|
let _timer = BoaProfiler::global().start_event("Value::set_internal_slot", "exec"); |
|
|
|
if let Self::Object(ref obj) = *self { |
|
|
|
if let Some(mut object) = self.as_object_mut() { |
|
|
|
obj.borrow_mut() |
|
|
|
object |
|
|
|
.internal_slots |
|
|
|
.internal_slots_mut() |
|
|
|
.insert(field.to_string(), val.clone()); |
|
|
|
.insert(field.to_string(), value.clone()); |
|
|
|
} |
|
|
|
} |
|
|
|
val |
|
|
|
value |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Set the kind of an object
|
|
|
|
/// Set the kind of an object.
|
|
|
|
pub fn set_kind(&self, kind: ObjectKind) { |
|
|
|
#[inline] |
|
|
|
|
|
|
|
pub fn set_data(&self, data: ObjectData) { |
|
|
|
if let Self::Object(ref obj) = *self { |
|
|
|
if let Self::Object(ref obj) = *self { |
|
|
|
(*obj.deref().borrow_mut()).kind = kind; |
|
|
|
(*obj.deref().borrow_mut()).data = data; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Set the property in the value
|
|
|
|
/// Set the property in the value.
|
|
|
|
pub fn set_property(&self, field: String, prop: Property) -> Property { |
|
|
|
pub fn set_property<S>(&self, field: S, property: Property) -> Property |
|
|
|
if let Self::Object(ref obj) = *self { |
|
|
|
where |
|
|
|
obj.borrow_mut().properties.insert(field, prop.clone()); |
|
|
|
S: Into<String>, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if let Some(mut object) = self.as_object_mut() { |
|
|
|
|
|
|
|
object |
|
|
|
|
|
|
|
.properties_mut() |
|
|
|
|
|
|
|
.insert(field.into(), property.clone()); |
|
|
|
} |
|
|
|
} |
|
|
|
prop |
|
|
|
property |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Set the property in the value
|
|
|
|
|
|
|
|
pub fn set_property_slice(&self, field: &str, prop: Property) -> Property { |
|
|
|
|
|
|
|
self.set_property(field.to_string(), prop) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Set internal state of an Object. Discards the previous state if it was set.
|
|
|
|
/// Set internal state of an Object. Discards the previous state if it was set.
|
|
|
|
pub fn set_internal_state<T: Any + InternalState>(&self, state: T) { |
|
|
|
pub fn set_internal_state<T: Any + InternalState>(&self, state: T) { |
|
|
|
if let Self::Object(ref obj) = *self { |
|
|
|
if let Some(mut object) = self.as_object_mut() { |
|
|
|
obj.borrow_mut() |
|
|
|
object.state_mut().replace(InternalStateCell::new(state)); |
|
|
|
.state |
|
|
|
|
|
|
|
.replace(InternalStateCell::new(state)); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Consume the function and return a Value
|
|
|
|
/// Consume the function and return a Value
|
|
|
|
pub fn from_func(native_func: Function) -> Value { |
|
|
|
pub fn from_func(function: Function) -> Value { |
|
|
|
// Object with Kind set to function
|
|
|
|
|
|
|
|
let mut new_func = crate::builtins::object::Object::function(); |
|
|
|
|
|
|
|
// Get Length
|
|
|
|
// Get Length
|
|
|
|
let length = native_func.params.len(); |
|
|
|
let length = function.params.len(); |
|
|
|
// Set [[Call]] internal slot
|
|
|
|
// Object with Kind set to function
|
|
|
|
new_func.set_func(native_func); |
|
|
|
let new_func = Object::function(function); |
|
|
|
// Wrap Object in GC'd Value
|
|
|
|
// Wrap Object in GC'd Value
|
|
|
|
let new_func_val = Value::from(new_func); |
|
|
|
let new_func_val = Value::from(new_func); |
|
|
|
// Set length to parameters
|
|
|
|
// Set length to parameters
|
|
|
|