Bug 1370752: Part 2 - Allow fallback serializer when JSON.serialize fails. r=aswan
authorKris Maglione <maglione.k@gmail.com>
Fri, 09 Jun 2017 18:19:11 -0700
changeset 368205 f6232176ba57ffdd3c06b201e0e7562256936c76
parent 368204 903992e46e072238158b289a6bf494280e478a13
child 368206 317331a50bde2f2e59bcd6074078c1d6ec2bd20c
push id32159
push usercbook@mozilla.com
push dateTue, 11 Jul 2017 10:52:11 +0000
treeherdermozilla-central@b07db5d650b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1370752
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1370752: Part 2 - Allow fallback serializer when JSON.serialize fails. r=aswan Currently, we need to be able to handle serializing non-JSON-compatible objects without catastrophically failing to save the storage file. Ideally, we would ensure this in the ordinary toJSON method. However, that would require a unnecessary extra calls to JSON.stringify for each object that needs to be sanitized before returning a JSON-safe value, which is more expensive than we can afford. The fallback toJSONSafe method allows us to do this only when necessary, due to an initial failed JSON serialization. MozReview-Commit-ID: JXQ001dOGtW
toolkit/modules/JSONFile.jsm
--- a/toolkit/modules/JSONFile.jsm
+++ b/toolkit/modules/JSONFile.jsm
@@ -281,18 +281,30 @@ JSONFile.prototype = {
    *
    * If an error occurs, the previous file is not deleted.
    *
    * @return {Promise}
    * @resolves When the operation finished successfully.
    * @rejects JavaScript exception.
    */
   async _save() {
+    let json;
+    try {
+      json = JSON.stringify(this._data);
+    } catch (e) {
+      // If serialization fails, try fallback safe JSON converter.
+      if (typeof this._data.toJSONSafe == "function") {
+        json = JSON.stringify(this._data.toJSONSafe());
+      } else {
+        throw e;
+      }
+    }
+
     // Create or overwrite the file.
-    let bytes = gTextEncoder.encode(JSON.stringify(this._data));
+    let bytes = gTextEncoder.encode(json);
     if (this._beforeSave) {
       await Promise.resolve(this._beforeSave());
     }
     await OS.File.writeAtomic(this.path, bytes,
                               Object.assign(
                                 { tmpPath: this.path + ".tmp" },
                                 this._options));
   },