Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
May 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 16 additions & 17 deletions boa/src/builtins/json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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());
}
}
};

Expand All @@ -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() {
Expand All @@ -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())
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions boa/src/object/gcobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand All @@ -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)))
}
}

Expand Down
24 changes: 13 additions & 11 deletions boa/src/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}

Expand Down