From 47271d5f91e53efe69703765cc7bfc2812194cbb Mon Sep 17 00:00:00 2001 From: Jason Williams Date: Sun, 14 Oct 2018 18:06:22 +0100 Subject: [PATCH] building up value --- src/lib/js/mod.rs | 1 - src/lib/js/object.rs | 2 +- src/lib/js/value.rs | 363 ++++++++++++++++++++++++++++++++++++++++ src/lib/lib.rs | 1 + src/lib/syntax/lexer.rs | 2 +- tests/lexer_test.rs | 1 + 6 files changed, 367 insertions(+), 3 deletions(-) diff --git a/src/lib/js/mod.rs b/src/lib/js/mod.rs index 2d97619a91..c8b51b4183 100644 --- a/src/lib/js/mod.rs +++ b/src/lib/js/mod.rs @@ -1,4 +1,3 @@ -extern crate serde_json; /// The global `Function` object and function value representations pub mod function; // /// The global `JSON` object diff --git a/src/lib/js/object.rs b/src/lib/js/object.rs index f7d1d0f718..67c7bfc2d1 100644 --- a/src/lib/js/object.rs +++ b/src/lib/js/object.rs @@ -1,4 +1,4 @@ -use js::value::{Value, ValueData}; +use js::value::Value; use std::collections::HashMap; pub static PROTOTYPE: &'static str = "prototype"; pub static INSTANCE_PROTOTYPE: &'static str = "__proto__"; diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index f564a145c7..eae6293249 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -1,7 +1,13 @@ use gc::{Gc, GcCell}; use js::function::Function; use js::object::{ObjectData, Property, INSTANCE_PROTOTYPE, PROTOTYPE}; +use serde_json::map::Map; +use serde_json::Number as JSONNumber; +use serde_json::Value as JSONValue; use std::collections::HashMap; +use std::fmt::{Display, Formatter, Result as FmtResult}; +use std::iter::FromIterator; +use std::ops::Deref; use std::str::FromStr; /// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`) @@ -183,6 +189,104 @@ impl Value { self.get_field(field.to_string()) } + /// Set the field in the value + pub fn set_field(&self, field: String, val: Value) -> Value { + match *self.ptr { + ValueData::Object(ref obj) => { + obj.borrow_mut() + .insert(field.clone(), Property::from_value(val.clone())); + } + ValueData::Function(ref func) => { + func.borrow_mut() + .object + .insert(field.clone(), Property::from_value(val.clone())); + } + _ => (), + } + val + } + + /// Set the field in the value + pub fn set_field_slice<'a>(&self, field: &'a str, val: Value) -> Value { + self.set_field(field.to_string(), val) + } + + /// Set the property in the value + pub fn set_prop(&self, field: String, prop: Property) -> Property { + match *self.ptr { + ValueData::Object(ref obj) => { + obj.borrow_mut().insert(field.clone(), prop.clone()); + } + ValueData::Function(ref func) => { + func.borrow_mut().object.insert(field.clone(), prop.clone()); + } + _ => (), + } + prop + } + + /// Convert from a JSON value to a JS value + pub fn from_json(json: JSONValue) -> ValueData { + match json { + JSONValue::Number(v) => ValueData::Number(v.as_f64().unwrap()), + JSONValue::String(v) => ValueData::String(v), + JSONValue::Bool(v) => ValueData::Boolean(v), + JSONValue::Array(vs) => { + let mut i = 0; + let mut data: ObjectData = FromIterator::from_iter(vs.iter().map(|json| { + i += 1; + ( + (i - 1).to_string().to_string(), + Property::from_value(to_value(json.clone())), + ) + })); + data.insert( + "length".to_string(), + Property::from_value(to_value(vs.len() as i32)), + ); + ValueData::Object(GcCell::new(data)) + } + JSONValue::Object(obj) => { + let data: ObjectData = FromIterator::from_iter(obj.iter().map(|(key, json)| { + (key.clone(), Property::from_value(to_value(json.clone()))) + })); + ValueData::Object(GcCell::new(data)) + } + JSONValue::Null => ValueData::Null, + } + } + + fn to_json(&self) -> JSONValue { + match *self.ptr { + ValueData::Null | ValueData::Undefined => JSONValue::Null, + ValueData::Boolean(b) => JSONValue::Bool(b), + ValueData::Object(ref obj) => { + let mut nobj = Map::new(); + for (k, v) in obj.borrow().iter() { + if k != INSTANCE_PROTOTYPE { + nobj.insert(k.clone(), v.value.to_json()); + } + } + JSONValue::Object(nobj) + } + ValueData::String(ref str) => JSONValue::String(str.clone()), + ValueData::Number(num) => JSONValue::Number(JSONNumber::from_f64(num).unwrap()), + ValueData::Integer(val) => JSONValue::Number(JSONNumber::from(val)), + ValueData::Function(_) => JSONValue::Null, + } + } + + /// Get the type of the value + pub fn get_type(&self) -> &'static str { + match *self.ptr { + ValueData::Number(_) | ValueData::Integer(_) => "number", + ValueData::String(_) => "string", + ValueData::Boolean(_) => "boolean", + ValueData::Null => "null", + ValueData::Undefined => "undefined", + _ => "object", + } + } /// Get the value for undefined pub fn undefined() -> Value { Value { @@ -190,3 +294,262 @@ impl Value { } } } + +impl Display for Value { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + match *self.ptr { + ValueData::Null => write!(f, "null"), + ValueData::Undefined => write!(f, "undefined"), + ValueData::Boolean(v) => write!(f, "{}", v), + ValueData::String(ref v) => write!(f, "{}", v), + ValueData::Number(v) => write!( + f, + "{}", + match v { + // https://tc39.github.io/ecma262/#sec-tostring-applied-to-the-number-type + _ if v.is_nan() => "NaN".to_string(), + _ if v.is_infinite() && v.is_sign_positive() => "Infinity".to_string(), + _ if v.is_infinite() && v.is_sign_negative() => "-Infinity".to_string(), + _ => v.to_string(), + } + ), + ValueData::Object(ref v) => { + try!(write!(f, "{}", "{")); + match v.borrow().iter().last() { + Some((last_key, _)) => { + for (key, val) in v.borrow().iter() { + try!(write!(f, "{}: {}", key, val.value)); + if key != last_key { + try!(write!(f, "{}", ", ")); + } + } + } + None => (), + } + write!(f, "{}", "}") + } + ValueData::Integer(v) => write!(f, "{}", v), + ValueData::Function(ref v) => write!(f, "function({})", v.borrow().args.join(", ")), + } + } +} + +impl PartialEq for Value { + fn eq(&self, other: &Value) -> bool { + match (self.ptr.clone().deref(), other.ptr.clone().deref()) { + // 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, + (ValueData::String(_), _) | (_, ValueData::String(_)) => { + self.to_string() == other.to_string() + } + (ValueData::Boolean(a), ValueData::Boolean(b)) if a == b => true, + (ValueData::Number(a), ValueData::Number(b)) + if a == b && !a.is_nan() && !b.is_nan() => + { + true + } + (ValueData::Number(ref a), _) if a == other.to_num() => true, + (_, ValueData::Number(ref a)) if a == self.to_num() => true, + (ValueData::Integer(a), ValueData::Integer(b)) if a == b => true, + _ => false, + } + } +} + +/// 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 + where + Self: Sized; +} + +impl ToValue for String { + fn to_value(&self) -> Value { + Value { + ptr: Gc::new(ValueData::String(self.clone())), + } + } +} + +impl FromValue for String { + fn from_value(v: Value) -> Result { + Ok(v.to_string()) + } +} + +impl<'s> ToValue for &'s str { + fn to_value(&self) -> Value { + Value { + ptr: Gc::new(ValueData::String(String::from_str(*self).unwrap())), + } + } +} + +impl ToValue for char { + fn to_value(&self) -> Value { + Value { + ptr: Gc::new(ValueData::String(self.to_string())), + } + } +} +impl FromValue for char { + fn from_value(v: Value) -> Result { + Ok(v.to_string().chars().next().unwrap()) + } +} + +impl ToValue for f64 { + fn to_value(&self) -> Value { + Value { + ptr: Gc::new(ValueData::Number(*self)), + } + } +} +impl FromValue for f64 { + fn from_value(v: Value) -> Result { + Ok(v.to_num()) + } +} + +impl ToValue for i32 { + fn to_value(&self) -> Value { + Value { + ptr: Gc::new(ValueData::Integer(*self)), + } + } +} +impl FromValue for i32 { + fn from_value(v: Value) -> Result { + Ok(v.to_int()) + } +} + +impl ToValue for bool { + fn to_value(&self) -> Value { + Value { + ptr: Gc::new(ValueData::Boolean(*self)), + } + } +} +impl FromValue for bool { + fn from_value(v: Value) -> Result { + Ok(v.is_true()) + } +} + +impl<'s, T: ToValue> ToValue for &'s [T] { + fn to_value(&self) -> Value { + let mut arr = HashMap::new(); + let mut i = 0; + for item in self.iter() { + arr.insert(i.to_string(), Property::from_value(item.to_value())); + i += 1; + } + to_value(arr) + } +} +impl ToValue for Vec { + fn to_value(&self) -> Value { + let mut arr = HashMap::new(); + let mut i = 0; + for item in self.iter() { + arr.insert(i.to_string(), Property::from_value(item.to_value())); + i += 1; + } + to_value(arr) + } +} + +impl FromValue for Vec { + fn from_value(v: Value) -> Result, &'static str> { + let len = v.get_field_slice("length").to_int(); + let mut vec = Vec::with_capacity(len as usize); + for i in 0..len { + vec.push(try!(from_value(v.get_field(i.to_string())))) + } + Ok(vec) + } +} + +impl ToValue for ObjectData { + fn to_value(&self) -> Value { + Value { + ptr: Gc::new(ValueData::Object(GcCell::new(self.clone()))), + } + } +} +impl FromValue for ObjectData { + fn from_value(v: Value) -> Result { + match *v.ptr { + ValueData::Object(ref obj) => { + let objData = obj.clone().into_inner(); + Ok(objData) + } + ValueData::Function(ref func) => Ok(func.borrow().object.clone()), + _ => Err("Value is not a valid object"), + } + } +} + +impl ToValue for JSONValue { + fn to_value(&self) -> Value { + Value { + ptr: Gc::new(Value::from_json(self.clone())), + } + } +} +impl FromValue for JSONValue { + fn from_value(v: Value) -> Result { + Ok(v.to_json()) + } +} + +impl ToValue for () { + fn to_value(&self) -> Value { + Value { + ptr: Gc::new(ValueData::Null), + } + } +} +impl FromValue for () { + fn from_value(_: Value) -> Result<(), &'static str> { + Ok(()) + } +} + +impl ToValue for Option { + fn to_value(&self) -> Value { + match *self { + Some(ref v) => v.to_value(), + None => Value { + ptr: Gc::new(ValueData::Null), + }, + } + } +} +impl FromValue for Option { + fn from_value(value: Value) -> Result, &'static str> { + Ok(if value.is_null_or_undefined() { + None + } else { + Some(try!(FromValue::from_value(value))) + }) + } +} + +/// A utility function that just calls FromValue::from_value +pub fn from_value(v: Value) -> Result { + FromValue::from_value(v) +} + +/// A utility function that just calls ToValue::to_value +pub fn to_value(v: A) -> Value { + v.to_value() +} diff --git a/src/lib/lib.rs b/src/lib/lib.rs index 3b71e6da3f..ed12daa6f2 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -1,4 +1,5 @@ extern crate gc; +extern crate serde_json; #[macro_use] extern crate gc_derive; diff --git a/src/lib/syntax/lexer.rs b/src/lib/syntax/lexer.rs index 1205e44765..b5377b5e93 100644 --- a/src/lib/syntax/lexer.rs +++ b/src/lib/syntax/lexer.rs @@ -113,7 +113,7 @@ impl<'a> Lexer<'a> { /// /// ```rust,no_run /// let buffer = std::fs::read_to_string("yourSourceCode.js").unwrap(); - /// let lexer = js::syntax::lexer::Lexer::new(&buffer); + /// let lexer = boa::syntax::lexer::Lexer::new(&buffer); /// ``` pub fn new(buffer: &'a str) -> Lexer<'a> { Lexer { diff --git a/tests/lexer_test.rs b/tests/lexer_test.rs index 02bfba47a3..9e0972eddc 100644 --- a/tests/lexer_test.rs +++ b/tests/lexer_test.rs @@ -1,4 +1,5 @@ extern crate boa; + use boa::syntax::ast::keyword::Keyword; use boa::syntax::ast::punc::Punctuator; use boa::syntax::ast::token::TokenData;