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

Handle NaN & Infinity values in serialization #1958

Merged
merged 2 commits into from
Jan 26, 2024
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

* Avoid any possibility of multiple conflicting native crash handlers or stack-unwinders running concurrently
[#1960](https://github.com/bugsnag/bugsnag-android/pull/1960)
* Metadata that includes non-finite doubles (NaN, +Infinity, -Infinity) are omitted instead of breaking serialization
[#1958](https://github.com/bugsnag/bugsnag-android/pull/1958)

## 6.1.0 (2023-12-05)

Expand All @@ -16,7 +18,7 @@

### Bug fixes

* Updating existing feature flags no longer causes them to change location.
* Updating existing feature-flags flags no longer causes them to change location.
[#1940](https://github.com/bugsnag/bugsnag-android/pull/1940)
* Fixed possible NDK crash when constructing several concurrent `Client` instances
[#1950](https://github.com/bugsnag/bugsnag-android/pull/1950)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ internal class BugsnagEventMapper(
event.userImpl = convertUser(map.readEntry("user"))

// populate metadata
val metadataMap: Map<String, Map<String, Any?>> = map.readEntry("metaData")
val metadataMap: Map<String, Map<String, Any?>> =
(map["metaData"] as? Map<String, Map<String, Any?>>).orEmpty()
metadataMap.forEach { (key, value) ->
event.addMetadata(key, value)
}

val featureFlagsList: List<Map<String, Any?>> = map.readEntry("featureFlags")
val featureFlagsList: List<Map<String, Any?>> =
(map["featureFlags"] as? List<Map<String, Any?>>).orEmpty()
featureFlagsList.forEach { featureFlagMap ->
event.addFeatureFlag(
featureFlagMap.readEntry("featureFlag"),
Expand All @@ -43,7 +45,8 @@ internal class BugsnagEventMapper(
}

// populate breadcrumbs
val breadcrumbList: List<MutableMap<String, Any?>> = map.readEntry("breadcrumbs")
val breadcrumbList: List<MutableMap<String, Any?>> =
(map["breadcrumbs"] as? List<MutableMap<String, Any?>>).orEmpty()
breadcrumbList.mapTo(event.breadcrumbs) {
Breadcrumb(
convertBreadcrumbInternal(it),
Expand Down Expand Up @@ -226,8 +229,7 @@ internal class BugsnagEventMapper(
is T -> return value
null -> throw IllegalStateException("cannot find json property '$key'")
else -> throw IllegalArgumentException(
"json property '$key' not " +
"of expected type, found ${value.javaClass.name}"
"json property '$key' not of expected type, found ${value.javaClass.name}"
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ class JsonWriter implements Closeable, Flushable {
*/
private static final String[] REPLACEMENT_CHARS;
private static final String[] HTML_SAFE_REPLACEMENT_CHARS;

static {
REPLACEMENT_CHARS = new String[128];
for (int i = 0; i <= 0x1f; i++) {
Expand All @@ -165,11 +166,14 @@ class JsonWriter implements Closeable, Flushable {
HTML_SAFE_REPLACEMENT_CHARS['\''] = "\\u0027";
}

/** The output data, containing at most one top-level array or object. */
/**
* The output data, containing at most one top-level array or object.
*/
private final Writer out;

private int[] stack = new int[32];
private int stackSize = 0;

{
push(EMPTY_DOCUMENT);
}
Expand Down Expand Up @@ -337,7 +341,7 @@ private JsonWriter open(int empty, String openBracket) throws IOException {
* given bracket.
*/
private JsonWriter close(int empty, int nonempty, String closeBracket)
throws IOException {
throws IOException {
int context = peek();
if (context != nonempty && context != empty) {
throw new IllegalStateException("Nesting problem.");
Expand Down Expand Up @@ -437,7 +441,7 @@ public JsonWriter jsonValue(String value) throws IOException {
}
writeDeferredName();
beforeValue();
out.append(value);
out.write(value);
return this;
}

Expand Down Expand Up @@ -490,17 +494,18 @@ public JsonWriter value(Boolean value) throws IOException {
/**
* Encodes {@code value}.
*
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or
* {@link Double#isInfinite() infinities}.
* @param value a finite value.
* @return this writer.
*/
public JsonWriter value(double value) throws IOException {
writeDeferredName();
if (!lenient && (Double.isNaN(value) || Double.isInfinite(value))) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
// omit these values instead of attempting to write them
deferredName = null;
} else {
writeDeferredName();
beforeValue();
out.write(Double.toString(value));
}
beforeValue();
out.append(Double.toString(value));
return this;
}

Expand All @@ -520,22 +525,24 @@ public JsonWriter value(long value) throws IOException {
* Encodes {@code value}.
*
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or
* {@link Double#isInfinite() infinities}.
* {@link Double#isInfinite() infinities}.
* @return this writer.
*/
public JsonWriter value(Number value) throws IOException {
if (value == null) {
return nullValue();
}

writeDeferredName();
String string = value.toString();
if (!lenient
&& (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
&& (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
// omit this value
deferredName = null;
} else {
writeDeferredName();
beforeValue();
out.write(string);
}
beforeValue();
out.append(string);
return this;
}

Expand Down Expand Up @@ -634,7 +641,7 @@ void beforeValue() throws IOException {
case NONEMPTY_DOCUMENT:
if (!lenient) {
throw new IllegalStateException(
"JSON must have only one top-level value.");
"JSON must have only one top-level value.");
}
// fall-through
case EMPTY_DOCUMENT: // first in document
Expand All @@ -647,12 +654,12 @@ void beforeValue() throws IOException {
break;

case NONEMPTY_ARRAY: // another in array
out.append(',');
out.write(',');
newline();
break;

case DANGLING_NAME: // value for name
out.append(separator);
out.write(separator);
replaceTop(NONEMPTY_OBJECT);
break;

Expand Down
Loading
Loading