diff --git a/boa/src/builtins/json.rs b/boa/src/builtins/json.rs index 0a7e9865a2..bffd28428c 100644 --- a/boa/src/builtins/json.rs +++ b/boa/src/builtins/json.rs @@ -1,10 +1,22 @@ +//! This module implements the global `JSON` object. +//! +//! The `JSON` object contains methods for parsing [JavaScript Object Notation (JSON)][json] +//! and converting values to JSON. It can't be called or constructed, and aside from its +//! two method properties, it has no interesting functionality of its own. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! - [JSON specification][json] +//! +//! [spec]: https://tc39.es/ecma262/#sec-json +//! [json]: https://www.json.org/json-en.html +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON + use crate::builtins::function::NativeFunctionData; -use crate::builtins::object::{Object, ObjectKind, PROTOTYPE}; -/// The JSON Object -/// use crate::builtins::value::{to_value, ResultValue, Value, ValueData}; use crate::exec::Interpreter; -use serde_json::{self, to_string_pretty, Value as JSONValue}; +use serde_json::{self, Value as JSONValue}; /// Parse a JSON string into a Javascript object /// @@ -20,23 +32,42 @@ pub fn parse(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Err(err) => Err(to_value(err.to_string())), } } + /// Process a Javascript object into a JSON string pub fn stringify(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let obj = args.get(0).expect("cannot get argument for JSON.stringify"); - let json = obj.to_json(); - Ok(to_value(to_string_pretty(&json).expect(""))) + let json = obj.to_json().to_string(); + Ok(to_value(json)) } /// Create a new `JSON` object pub fn create_constructor(global: &Value) -> Value { - let mut json = Object::default(); - json.kind = ObjectKind::Ordinary; + let json = ValueData::new_obj(Some(global)); + + make_builtin_fn!(parse, named "parse", with length 2, of json); + make_builtin_fn!(stringify, named "stringify", with length 3, of json); - let prototype = ValueData::new_obj(Some(global)); - make_builtin_fn!(parse, named "parse", with length 2, of prototype); - make_builtin_fn!(stringify, named "stringify", with length 3, of prototype); + to_value(json) +} + +#[cfg(test)] +mod tests { + use crate::{exec::Executor, forward, realm::Realm}; - let json_value = to_value(json); - json_value.set_field_slice(PROTOTYPE, prototype); - json_value + #[test] + fn json_sanity() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + assert_eq!( + forward(&mut engine, r#"JSON.parse('{"aaa":"bbb"}').aaa == 'bbb'"#), + "true" + ); + assert_eq!( + forward( + &mut engine, + r#"JSON.stringify({aaa: 'bbb'}) == '{"aaa":"bbb"}'"# + ), + "true" + ); + } } diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index fc276f8143..7486d214c6 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -586,12 +586,12 @@ impl ValueData { | ValueData::Function(_) => JSONValue::Null, ValueData::Boolean(b) => JSONValue::Bool(b), ValueData::Object(ref obj) => { - let mut new_obj = Map::new(); - for (k, v) in obj.borrow().internal_slots.iter() { - if k != INSTANCE_PROTOTYPE { - new_obj.insert(k.clone(), v.to_json()); - } - } + let new_obj = obj + .borrow() + .properties + .iter() + .map(|(k, _)| (k.clone(), self.get_field_slice(k).to_json())) + .collect::>(); JSONValue::Object(new_obj) } ValueData::String(ref str) => JSONValue::String(str.clone()),