diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 3c2313a40d0..b52de61ece3 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -137,9 +137,6 @@ impl Json { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify pub(crate) fn stringify(_: &Value, args: &[Value], context: &mut Context) -> Result { let object = match args.get(0) { - Some(obj) if obj.is_symbol() || obj.is_function() || obj.is_undefined() => { - return Ok(Value::undefined()) - } None => return Ok(Value::undefined()), Some(obj) => obj, }; @@ -178,10 +175,11 @@ impl Json { let replacer = match args.get(1) { Some(replacer) if replacer.is_object() => replacer, _ => { - return Ok(Value::from(json_to_pretty_string( - &object.to_json(context)?, - gap, - ))) + if let Some(value) = object.to_json(context)? { + return Ok(Value::from(json_to_pretty_string(&value, gap))); + } else { + return Ok(Value::undefined()); + } } }; @@ -208,10 +206,11 @@ impl Json { ), ); } - Ok(Value::from(json_to_pretty_string( - &object_to_return.to_json(context)?, - gap, - ))) + if let Some(value) = object_to_return.to_json(context)? { + Ok(Value::from(json_to_pretty_string(&value, gap))) + } else { + Ok(Value::undefined()) + } }) .ok_or_else(Value::undefined)? } else if replacer_as_object.is_array() { @@ -234,19 +233,19 @@ impl Json { for field in fields { let v = object.get_field(field.to_string(context)?, context)?; if !v.is_undefined() { - let value = v.to_json(context)?; - obj_to_return.insert(field.to_string(context)?.to_string(), value); + if let Some(value) = v.to_json(context)? { + obj_to_return.insert(field.to_string(context)?.to_string(), value); + } } } Ok(Value::from(json_to_pretty_string( &JSONValue::Object(obj_to_return), gap, ))) + } else if let Some(value) = object.to_json(context)? { + Ok(Value::from(json_to_pretty_string(&value, gap))) } else { - Ok(Value::from(json_to_pretty_string( - &object.to_json(context)?, - gap, - ))) + Ok(Value::undefined()) } } } diff --git a/boa/src/object/gcobject.rs b/boa/src/object/gcobject.rs index 234bb23d359..bb1135e4dca 100644 --- a/boa/src/object/gcobject.rs +++ b/boa/src/object/gcobject.rs @@ -390,7 +390,7 @@ impl GcObject { } /// Converts an object to JSON, checking for reference cycles and throwing a TypeError if one is found - pub(crate) fn to_json(&self, context: &mut Context) -> Result { + pub(crate) fn to_json(&self, context: &mut Context) -> Result> { let rec_limiter = RecursionLimiter::new(self); if rec_limiter.live { Err(context.construct_type_error("cyclic object value")) @@ -401,24 +401,24 @@ impl GcObject { let this = Value::from(self.clone()); for key in keys { let value = this.get_field(key, context)?; - if value.is_undefined() || value.is_function() || value.is_symbol() { - arr.push(JSONValue::Null); + if let Some(value) = value.to_json(context)? { + arr.push(value); } else { - arr.push(value.to_json(context)?); + arr.push(JSONValue::Null); } } - Ok(JSONValue::Array(arr)) + Ok(Some(JSONValue::Array(arr))) } else { let mut new_obj = Map::new(); let this = Value::from(self.clone()); for k in self.borrow().keys() { let key = k.clone(); let value = this.get_field(k.to_string(), context)?; - if !value.is_undefined() && !value.is_function() && !value.is_symbol() { - new_obj.insert(key.to_string(), value.to_json(context)?); + if let Some(value) = value.to_json(context)? { + new_obj.insert(key.to_string(), value); } } - Ok(JSONValue::Object(new_obj)) + Ok(Some(JSONValue::Object(new_obj))) } } diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 99163dc3e20..b6a62fd8587 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -215,35 +215,37 @@ impl Value { } /// Converts the `Value` to `JSON`. - pub fn to_json(&self, context: &mut Context) -> Result { + pub fn to_json(&self, context: &mut Context) -> Result> { let to_json = self.get_field("toJSON", context)?; if to_json.is_function() { let json_value = context.call(&to_json, self, &[])?; return json_value.to_json(context); } + if self.is_function() { + return Ok(None); + } + match *self { - Self::Null => Ok(JSONValue::Null), - Self::Boolean(b) => Ok(JSONValue::Bool(b)), + Self::Null => Ok(Some(JSONValue::Null)), + Self::Boolean(b) => Ok(Some(JSONValue::Bool(b))), Self::Object(ref obj) => obj.to_json(context), - Self::String(ref str) => Ok(JSONValue::String(str.to_string())), + Self::String(ref str) => Ok(Some(JSONValue::String(str.to_string()))), Self::Rational(num) => { if num.is_finite() { - Ok(JSONValue::Number( + Ok(Some(JSONValue::Number( JSONNumber::from_str(&Number::to_native_string(num)) .expect("invalid number found"), - )) + ))) } else { - Ok(JSONValue::Null) + Ok(Some(JSONValue::Null)) } } - Self::Integer(val) => Ok(JSONValue::Number(JSONNumber::from(val))), + Self::Integer(val) => Ok(Some(JSONValue::Number(JSONNumber::from(val)))), Self::BigInt(_) => { Err(context.construct_type_error("BigInt value can't be serialized in JSON")) } - Self::Symbol(_) | Self::Undefined => { - unreachable!("Symbols and Undefined JSON Values depend on parent type"); - } + Self::Symbol(_) | Self::Undefined => Ok(None), } }