mirror of https://github.com/boa-dev/boa.git
HalidOdat
4 years ago
committed by
GitHub
26 changed files with 757 additions and 680 deletions
@ -0,0 +1,202 @@ |
|||||||
|
use super::*; |
||||||
|
|
||||||
|
/// Conversion to Javascript values from Rust values
|
||||||
|
pub trait ToValue { |
||||||
|
/// Convert this value to a Rust value
|
||||||
|
fn to_value(&self) -> Value; |
||||||
|
} |
||||||
|
/// Conversion to Rust values from Javascript values
|
||||||
|
pub trait FromValue { |
||||||
|
/// Convert this value to a Javascript value
|
||||||
|
fn from_value(value: Value) -> Result<Self, &'static str> |
||||||
|
where |
||||||
|
Self: Sized; |
||||||
|
} |
||||||
|
|
||||||
|
impl ToValue for Value { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
self.clone() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl FromValue for Value { |
||||||
|
fn from_value(value: Value) -> Result<Self, &'static str> { |
||||||
|
Ok(value) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ToValue for String { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
Gc::new(ValueData::String(self.clone())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl FromValue for String { |
||||||
|
fn from_value(v: Value) -> Result<Self, &'static str> { |
||||||
|
Ok(v.to_string()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'s> ToValue for &'s str { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
Gc::new(ValueData::String( |
||||||
|
String::from_str(*self).expect("Could not convert string to self to String"), |
||||||
|
)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ToValue for char { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
Gc::new(ValueData::String(self.to_string())) |
||||||
|
} |
||||||
|
} |
||||||
|
impl FromValue for char { |
||||||
|
fn from_value(v: Value) -> Result<Self, &'static str> { |
||||||
|
Ok(v.to_string() |
||||||
|
.chars() |
||||||
|
.next() |
||||||
|
.expect("Could not get next char")) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ToValue for f64 { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
Gc::new(ValueData::Rational(*self)) |
||||||
|
} |
||||||
|
} |
||||||
|
impl FromValue for f64 { |
||||||
|
fn from_value(v: Value) -> Result<Self, &'static str> { |
||||||
|
Ok(v.to_number()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ToValue for i32 { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
Gc::new(ValueData::Integer(*self)) |
||||||
|
} |
||||||
|
} |
||||||
|
impl FromValue for i32 { |
||||||
|
fn from_value(v: Value) -> Result<Self, &'static str> { |
||||||
|
Ok(v.to_integer()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ToValue for usize { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
Gc::new(ValueData::Integer(*self as i32)) |
||||||
|
} |
||||||
|
} |
||||||
|
impl FromValue for usize { |
||||||
|
fn from_value(v: Value) -> Result<Self, &'static str> { |
||||||
|
Ok(v.to_integer() as Self) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ToValue for bool { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
Gc::new(ValueData::Boolean(*self)) |
||||||
|
} |
||||||
|
} |
||||||
|
impl FromValue for bool { |
||||||
|
fn from_value(v: Value) -> Result<Self, &'static str> { |
||||||
|
Ok(v.is_true()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'s, T: ToValue> ToValue for &'s [T] { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
let mut arr = Object::default(); |
||||||
|
for (i, item) in self.iter().enumerate() { |
||||||
|
arr.properties |
||||||
|
.insert(i.to_string(), Property::default().value(item.to_value())); |
||||||
|
} |
||||||
|
to_value(arr) |
||||||
|
} |
||||||
|
} |
||||||
|
impl<T: ToValue> ToValue for Vec<T> { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
let mut arr = Object::default(); |
||||||
|
for (i, item) in self.iter().enumerate() { |
||||||
|
arr.properties |
||||||
|
.insert(i.to_string(), Property::default().value(item.to_value())); |
||||||
|
} |
||||||
|
to_value(arr) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: FromValue> FromValue for Vec<T> { |
||||||
|
fn from_value(v: Value) -> Result<Self, &'static str> { |
||||||
|
let len = v.get_field_slice("length").to_integer(); |
||||||
|
let mut vec = Self::with_capacity(len as usize); |
||||||
|
for i in 0..len { |
||||||
|
vec.push(from_value(v.get_field_slice(&i.to_string()))?) |
||||||
|
} |
||||||
|
Ok(vec) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ToValue for Object { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
Gc::new(ValueData::Object(Box::new(GcCell::new(self.clone())))) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl FromValue for Object { |
||||||
|
fn from_value(v: Value) -> Result<Self, &'static str> { |
||||||
|
match *v { |
||||||
|
ValueData::Object(ref obj) => Ok(obj.clone().into_inner()), |
||||||
|
_ => Err("Value is not a valid object"), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ToValue for JSONValue { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
Gc::new(ValueData::from_json(self.clone())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl FromValue for JSONValue { |
||||||
|
fn from_value(v: Value) -> Result<Self, &'static str> { |
||||||
|
Ok(v.to_json()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ToValue for () { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
Gc::new(ValueData::Null) |
||||||
|
} |
||||||
|
} |
||||||
|
impl FromValue for () { |
||||||
|
fn from_value(_: Value) -> Result<(), &'static str> { |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: ToValue> ToValue for Option<T> { |
||||||
|
fn to_value(&self) -> Value { |
||||||
|
match *self { |
||||||
|
Some(ref v) => v.to_value(), |
||||||
|
None => Gc::new(ValueData::Null), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
impl<T: FromValue> FromValue for Option<T> { |
||||||
|
fn from_value(value: Value) -> Result<Self, &'static str> { |
||||||
|
Ok(if value.is_null_or_undefined() { |
||||||
|
None |
||||||
|
} else { |
||||||
|
Some(FromValue::from_value(value)?) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// A utility function that just calls `FromValue::from_value`
|
||||||
|
pub fn from_value<A: FromValue>(v: Value) -> Result<A, &'static str> { |
||||||
|
FromValue::from_value(v) |
||||||
|
} |
||||||
|
|
||||||
|
/// A utility function that just calls `ToValue::to_value`
|
||||||
|
pub fn to_value<A: ToValue>(v: A) -> Value { |
||||||
|
v.to_value() |
||||||
|
} |
@ -0,0 +1,138 @@ |
|||||||
|
use super::*; |
||||||
|
|
||||||
|
impl PartialEq for ValueData { |
||||||
|
fn eq(&self, other: &Self) -> bool { |
||||||
|
match (self.clone(), other.clone()) { |
||||||
|
// TODO: fix this
|
||||||
|
// _ if self.ptr.to_inner() == &other.ptr.to_inner() => true,
|
||||||
|
_ if self.is_null_or_undefined() && other.is_null_or_undefined() => true, |
||||||
|
(Self::String(_), _) | (_, Self::String(_)) => self.to_string() == other.to_string(), |
||||||
|
(Self::Boolean(a), Self::Boolean(b)) if a == b => true, |
||||||
|
(Self::Rational(a), Self::Rational(b)) if a == b && !a.is_nan() && !b.is_nan() => true, |
||||||
|
(Self::Rational(a), _) if a == other.to_number() => true, |
||||||
|
(_, Self::Rational(a)) if a == self.to_number() => true, |
||||||
|
(Self::Integer(a), Self::Integer(b)) if a == b => true, |
||||||
|
_ => false, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Add for ValueData { |
||||||
|
type Output = Self; |
||||||
|
fn add(self, other: Self) -> Self { |
||||||
|
match (self, other) { |
||||||
|
(Self::String(ref s), ref o) => { |
||||||
|
Self::String(format!("{}{}", s.clone(), &o.to_string())) |
||||||
|
} |
||||||
|
(ref s, Self::String(ref o)) => Self::String(format!("{}{}", s.to_string(), o)), |
||||||
|
(ref s, ref o) => Self::Rational(s.to_number() + o.to_number()), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
impl Sub for ValueData { |
||||||
|
type Output = Self; |
||||||
|
fn sub(self, other: Self) -> Self { |
||||||
|
Self::Rational(self.to_number() - other.to_number()) |
||||||
|
} |
||||||
|
} |
||||||
|
impl Mul for ValueData { |
||||||
|
type Output = Self; |
||||||
|
fn mul(self, other: Self) -> Self { |
||||||
|
Self::Rational(self.to_number() * other.to_number()) |
||||||
|
} |
||||||
|
} |
||||||
|
impl Div for ValueData { |
||||||
|
type Output = Self; |
||||||
|
fn div(self, other: Self) -> Self { |
||||||
|
Self::Rational(self.to_number() / other.to_number()) |
||||||
|
} |
||||||
|
} |
||||||
|
impl Rem for ValueData { |
||||||
|
type Output = Self; |
||||||
|
fn rem(self, other: Self) -> Self { |
||||||
|
Self::Rational(self.to_number() % other.to_number()) |
||||||
|
} |
||||||
|
} |
||||||
|
impl BitAnd for ValueData { |
||||||
|
type Output = Self; |
||||||
|
fn bitand(self, other: Self) -> Self { |
||||||
|
Self::Integer(self.to_integer() & other.to_integer()) |
||||||
|
} |
||||||
|
} |
||||||
|
impl BitOr for ValueData { |
||||||
|
type Output = Self; |
||||||
|
fn bitor(self, other: Self) -> Self { |
||||||
|
Self::Integer(self.to_integer() | other.to_integer()) |
||||||
|
} |
||||||
|
} |
||||||
|
impl BitXor for ValueData { |
||||||
|
type Output = Self; |
||||||
|
fn bitxor(self, other: Self) -> Self { |
||||||
|
Self::Integer(self.to_integer() ^ other.to_integer()) |
||||||
|
} |
||||||
|
} |
||||||
|
impl Shl for ValueData { |
||||||
|
type Output = Self; |
||||||
|
fn shl(self, other: Self) -> Self { |
||||||
|
Self::Integer(self.to_integer() << other.to_integer()) |
||||||
|
} |
||||||
|
} |
||||||
|
impl Shr for ValueData { |
||||||
|
type Output = Self; |
||||||
|
fn shr(self, other: Self) -> Self { |
||||||
|
Self::Integer(self.to_integer() >> other.to_integer()) |
||||||
|
} |
||||||
|
} |
||||||
|
impl Not for ValueData { |
||||||
|
type Output = Self; |
||||||
|
fn not(self) -> Self { |
||||||
|
Self::Boolean(!self.is_true()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// The internal comparison abstract operation SameValue(x, y),
|
||||||
|
/// where x and y are ECMAScript language values, produces true or false.
|
||||||
|
/// Such a comparison is performed as follows:
|
||||||
|
///
|
||||||
|
/// https://tc39.es/ecma262/#sec-samevalue
|
||||||
|
/// strict mode currently compares the pointers
|
||||||
|
pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool { |
||||||
|
if strict { |
||||||
|
// Do both Values point to the same underlying valueData?
|
||||||
|
let x_ptr = Gc::into_raw(x.clone()); |
||||||
|
let y_ptr = Gc::into_raw(y.clone()); |
||||||
|
return x_ptr == y_ptr; |
||||||
|
} |
||||||
|
|
||||||
|
if x.get_type() != y.get_type() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if x.get_type() == "number" { |
||||||
|
let native_x: f64 = from_value(x.clone()).expect("failed to get value"); |
||||||
|
let native_y: f64 = from_value(y.clone()).expect("failed to get value"); |
||||||
|
return native_x.abs() - native_y.abs() == 0.0; |
||||||
|
} |
||||||
|
|
||||||
|
same_value_non_number(x, y) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn same_value_non_number(x: &Value, y: &Value) -> bool { |
||||||
|
debug_assert!(x.get_type() == y.get_type()); |
||||||
|
match x.get_type() { |
||||||
|
"undefined" => true, |
||||||
|
"null" => true, |
||||||
|
"string" => { |
||||||
|
if x.to_string() == y.to_string() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
false |
||||||
|
} |
||||||
|
"boolean" => { |
||||||
|
from_value::<bool>(x.clone()).expect("failed to get value") |
||||||
|
== from_value::<bool>(y.clone()).expect("failed to get value") |
||||||
|
} |
||||||
|
"object" => *x == *y, |
||||||
|
_ => false, |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue