Browse Source

Add proto object to from json (#462)

* remove From<JSONValue> for Value. Going from JSONValue to Value will soon need the interpreter

* pass interpreter to from_json

* move from_json to Value impl

* move to_json to Value impl alongside from_json

* add prototype to objects created from json

* consume the object and don't clone

* if it fits into i32, use integer; otherwise, use a rational

* WIP: throwing type error

* address most of the error cases

* cargo fmt

* address the rest of the error cases

* return null when JSONNumber::from_f64() returns None

* cargo fmt

* Update boa/src/builtins/value/mod.rs

* use JSONValue and use Result

* Update boa/src/builtins/json/mod.rs

Use and_then to avoid flatten

Co-authored-by: HalidOdat <halidodat@gmail.com>

Co-authored-by: Iban Eguia <razican@protonmail.ch>
Co-authored-by: HalidOdat <halidodat@gmail.com>
pull/471/head
n14little 4 years ago committed by GitHub
parent
commit
4763907fbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      boa/src/builtins/json/mod.rs
  2. 34
      boa/src/builtins/json/tests.rs
  3. 12
      boa/src/builtins/value/conversions.rs
  4. 184
      boa/src/builtins/value/mod.rs

12
boa/src/builtins/json/mod.rs

@ -42,7 +42,7 @@ pub fn parse(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValu
&ctx.to_string(args.get(0).expect("cannot get argument for JSON.parse"))?,
) {
Ok(json) => {
let j = Value::from(json);
let j = Value::from_json(json, ctx);
match args.get(1) {
Some(reviver) if reviver.is_function() => {
let mut holder = Value::new_object(None);
@ -107,7 +107,7 @@ pub fn stringify(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result
};
let replacer = match args.get(1) {
Some(replacer) if replacer.is_object() => replacer,
_ => return Ok(Value::from(object.to_json().to_string())),
_ => return Ok(Value::from(object.to_json(ctx)?.to_string())),
};
let replacer_as_object = replacer
@ -133,7 +133,7 @@ pub fn stringify(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result
)?),
);
}
Ok(Value::from(object_to_return.to_json().to_string()))
Ok(Value::from(object_to_return.to_json(ctx)?.to_string()))
})
.ok_or_else(Value::undefined)?
} else if replacer_as_object.kind == ObjectKind::Array {
@ -149,15 +149,15 @@ pub fn stringify(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result
for field in fields {
if let Some(value) = object
.get_property(&ctx.to_string(&field)?)
.map(|prop| prop.value.as_ref().map(|v| v.to_json()))
.flatten()
.and_then(|prop| prop.value.as_ref().map(|v| v.to_json(ctx)))
.transpose()?
{
obj_to_return.insert(field.to_string(), value);
}
}
Ok(Value::from(JSONValue::Object(obj_to_return).to_string()))
} else {
Ok(Value::from(object.to_json().to_string()))
Ok(Value::from(object.to_json(ctx)?.to_string()))
}
}

34
boa/src/builtins/json/tests.rs

@ -1,4 +1,12 @@
use crate::{exec::Interpreter, forward, forward_val, realm::Realm};
use crate::{
builtins::{
object::{INSTANCE_PROTOTYPE, PROTOTYPE},
value::same_value,
},
exec::Interpreter,
forward, forward_val,
realm::Realm,
};
#[test]
fn json_sanity() {
@ -236,3 +244,27 @@ fn json_parse_object_with_reviver() {
);
assert_eq!(result, r#"{"firstname":"boa","lastname":"interpreter"}"#);
}
#[test]
fn json_parse_sets_prototypes() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let init = r#"
const jsonString = "{
\"ob\":{\"ject\":1}
}";
const jsonObj = JSON.parse(jsonString);
"#;
eprintln!("{}", forward(&mut engine, init));
let object = forward_val(&mut engine, r#"jsonObj.ob"#).unwrap();
let object_prototype = object.get_internal_slot(INSTANCE_PROTOTYPE);
let global_object_prototype = engine
.realm
.global_obj
.get_field("Object")
.get_field(PROTOTYPE);
assert_eq!(
same_value(&object_prototype, &global_object_prototype, true),
true
);
}

12
boa/src/builtins/value/conversions.rs

@ -170,18 +170,6 @@ impl TryFrom<&Value> for Object {
}
}
impl From<JSONValue> for Value {
fn from(value: JSONValue) -> Self {
Self(Gc::new(ValueData::from_json(value)))
}
}
impl From<&Value> for JSONValue {
fn from(value: &Value) -> Self {
value.to_json()
}
}
impl From<()> for Value {
fn from(_: ()) -> Self {
Value::null()

184
boa/src/builtins/value/mod.rs

@ -15,11 +15,13 @@ use crate::builtins::{
};
use crate::BoaProfiler;
use crate::exec::Interpreter;
use gc::{Finalize, Gc, GcCell, GcCellRef, Trace};
use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue};
use std::{
any::Any,
collections::HashSet,
convert::TryFrom,
f64::NAN,
fmt::{self, Display},
ops::{Add, BitAnd, BitOr, BitXor, Deref, DerefMut, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub},
@ -149,6 +151,99 @@ impl Value {
Self::object(object)
}
/// Convert from a JSON value to a JS value
pub fn from_json(json: JSONValue, interpreter: &mut Interpreter) -> Self {
match json {
JSONValue::Number(v) => {
if let Some(Ok(integer_32)) = v.as_i64().map(i32::try_from) {
Self::integer(integer_32)
} else {
Self::rational(v.as_f64().expect("Could not convert value to f64"))
}
}
JSONValue::String(v) => Self::string(v),
JSONValue::Bool(v) => Self::boolean(v),
JSONValue::Array(vs) => {
let mut new_obj = Object::default();
let length = vs.len();
for (idx, json) in vs.into_iter().enumerate() {
new_obj.properties.insert(
idx.to_string(),
Property::default()
.value(Self::from_json(json, interpreter))
.writable(true)
.configurable(true),
);
}
new_obj.properties.insert(
"length".to_string(),
Property::default().value(Self::from(length)),
);
Self::object(new_obj)
}
JSONValue::Object(obj) => {
let new_obj = Value::new_object(Some(&interpreter.realm.global_obj));
for (key, json) in obj.into_iter() {
let value = Self::from_json(json, interpreter);
new_obj.set_property(
key,
Property::default()
.value(value)
.writable(true)
.configurable(true),
);
}
new_obj
}
JSONValue::Null => Self::null(),
}
}
/// Conversts the `Value` to `JSON`.
pub fn to_json(&self, interpreter: &mut Interpreter) -> Result<JSONValue, Value> {
match *self.data() {
ValueData::Null => Ok(JSONValue::Null),
ValueData::Boolean(b) => Ok(JSONValue::Bool(b)),
ValueData::Object(ref obj) => {
if obj.borrow().kind == ObjectKind::Array {
let mut arr: Vec<JSONValue> = Vec::new();
for k in obj.borrow().properties.keys() {
if k != "length" {
let value = self.get_field(k.to_string());
if value.is_undefined() || value.is_function() {
arr.push(JSONValue::Null);
} else {
arr.push(self.get_field(k.to_string()).to_json(interpreter)?);
}
}
}
Ok(JSONValue::Array(arr))
} else {
let mut new_obj = Map::new();
for k in obj.borrow().properties.keys() {
let key = k.clone();
let value = self.get_field(k.to_string());
if !value.is_undefined() && !value.is_function() {
new_obj.insert(key, value.to_json(interpreter)?);
}
}
Ok(JSONValue::Object(new_obj))
}
}
ValueData::String(ref str) => Ok(JSONValue::String(str.clone())),
ValueData::Rational(num) => Ok(JSONNumber::from_f64(num)
.map(JSONValue::Number)
.unwrap_or(JSONValue::Null)),
ValueData::Integer(val) => Ok(JSONValue::Number(JSONNumber::from(val))),
ValueData::BigInt(_) => Err(interpreter
.throw_type_error("BigInt value can't be serialized in JSON")
.expect_err("throw_type_error should always return an error")),
ValueData::Symbol(_) | ValueData::Undefined => {
unreachable!("Symbols and Undefined JSON Values depend on parent type");
}
}
}
}
impl Deref for Value {
@ -671,95 +766,6 @@ impl ValueData {
new_func_val
}
/// Convert from a JSON value to a JS value
pub fn from_json(json: JSONValue) -> Self {
match json {
JSONValue::Number(v) => {
Self::Rational(v.as_f64().expect("Could not convert value to f64"))
}
JSONValue::String(v) => Self::String(v),
JSONValue::Bool(v) => Self::Boolean(v),
JSONValue::Array(vs) => {
let mut new_obj = Object::default();
for (idx, json) in vs.iter().enumerate() {
new_obj.properties.insert(
idx.to_string(),
Property::default()
.value(Value::from(json.clone()))
.writable(true)
.configurable(true),
);
}
new_obj.properties.insert(
"length".to_string(),
Property::default().value(Value::from(vs.len())),
);
Self::Object(Box::new(GcCell::new(new_obj)))
}
JSONValue::Object(obj) => {
let mut new_obj = Object::default();
for (key, json) in obj.iter() {
new_obj.properties.insert(
key.clone(),
Property::default()
.value(Value::from(json.clone()))
.writable(true)
.configurable(true),
);
}
Self::Object(Box::new(GcCell::new(new_obj)))
}
JSONValue::Null => Self::Null,
}
}
/// Conversts the `Value` to `JSON`.
pub fn to_json(&self) -> JSONValue {
match *self {
Self::Null => JSONValue::Null,
Self::Boolean(b) => JSONValue::Bool(b),
Self::Object(ref obj) => {
if obj.borrow().kind == ObjectKind::Array {
let mut arr: Vec<JSONValue> = Vec::new();
obj.borrow().properties.keys().for_each(|k| {
if k != "length" {
let value = self.get_field(k.to_string());
if value.is_undefined() || value.is_function() {
arr.push(JSONValue::Null);
} else {
arr.push(self.get_field(k.to_string()).to_json());
}
}
});
JSONValue::Array(arr)
} else {
let mut new_obj = Map::new();
obj.borrow().properties.keys().for_each(|k| {
let key = k.clone();
let value = self.get_field(k.to_string());
if !value.is_undefined() && !value.is_function() {
new_obj.insert(key, value.to_json());
}
});
JSONValue::Object(new_obj)
}
}
Self::String(ref str) => JSONValue::String(str.clone()),
Self::Rational(num) => JSONValue::Number(
JSONNumber::from_f64(num).expect("Could not convert to JSONNumber"),
),
Self::Integer(val) => JSONValue::Number(JSONNumber::from(val)),
Self::BigInt(_) => {
// TODO: throw TypeError
panic!("TypeError: \"BigInt value can't be serialized in JSON\"");
}
Self::Symbol(_) | Self::Undefined => {
unreachable!("Symbols and Undefined JSON Values depend on parent type");
}
}
}
/// Get the type of the value
///
/// https://tc39.es/ecma262/#sec-typeof-operator

Loading…
Cancel
Save