Browse Source

Return optional value in to_json functions (fixes #1064) (#1201)

pull/1223/head
fermian 4 years ago committed by GitHub
parent
commit
c843afd466
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 33
      boa/src/builtins/json/mod.rs
  2. 16
      boa/src/object/gcobject.rs
  3. 24
      boa/src/value/mod.rs

33
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<Value> {
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())
}
}
}

16
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<JSONValue> {
pub(crate) fn to_json(&self, context: &mut Context) -> Result<Option<JSONValue>> {
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)))
}
}

24
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<JSONValue> {
pub fn to_json(&self, context: &mut Context) -> Result<Option<JSONValue>> {
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),
}
}

Loading…
Cancel
Save