diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 87eb35f436..d0edaeca66 100644 --- a/boa/src/builtins/json/mod.rs +++ b/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())) } } diff --git a/boa/src/builtins/json/tests.rs b/boa/src/builtins/json/tests.rs index 2358f11b1b..6ce78b82c3 100644 --- a/boa/src/builtins/json/tests.rs +++ b/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 + ); +} diff --git a/boa/src/builtins/value/conversions.rs b/boa/src/builtins/value/conversions.rs index 201f7656cb..997ea9651e 100644 --- a/boa/src/builtins/value/conversions.rs +++ b/boa/src/builtins/value/conversions.rs @@ -170,18 +170,6 @@ impl TryFrom<&Value> for Object { } } -impl From 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() diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 1bf320ed88..ad8cac3e3d 100644 --- a/boa/src/builtins/value/mod.rs +++ b/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 { + 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 = 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 = 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