From 4dcb41f5e6a5792c194ad5524e63be16a02433ff Mon Sep 17 00:00:00 2001 From: Pritesh Nandgaonkar Date: Thu, 9 May 2019 06:34:05 -0700 Subject: [PATCH] Iterative version of the serializer Summary: Iterative version of the custom serializer. The recursive implementation led to call stack overflow for news feed surface in fb4a. Reviewed By: danielbuechele Differential Revision: D15251643 fbshipit-source-id: b8af921b1a4d85c52d4d45a7abf95a5bb5f283f7 --- src/utils/serialization.js | 160 ++++++++++++++++++++++++++++++++----- 1 file changed, 138 insertions(+), 22 deletions(-) diff --git a/src/utils/serialization.js b/src/utils/serialization.js index 6654f5f1f..8445b4e89 100644 --- a/src/utils/serialization.js +++ b/src/utils/serialization.js @@ -13,33 +13,149 @@ export function deserialize(str: string): Object { return deserializeObject(JSON.parse(str)); } +function processArray( + element: any, + array: [any], + stack: Array, + dict: Map, +): {premature: boolean, outputArr: Array} { + let outputArr = []; + let premature = false; + for (const item of array) { + if (!dict.has(item)) { + if (!(item instanceof Object)) { + dict.set(item, item); + } else { + stack.push(element); + stack.push(item); + premature = true; + break; + } + } + if (dict.has(item)) { + dict.set(item, dict.get(item)); + outputArr.push(dict.get(item)); + } + } + return {premature, outputArr}; +} + +function processKeyValuePair( + element: any, + key: any, + value: any, + stack: Array, + dict: Map, +): {premature: boolean} { + let premature = false; + if (!dict.has(key)) { + if (!(key instanceof Object)) { + dict.set(key, key); + } else { + stack.push(element); + stack.push(key); + premature = true; + return {premature}; + } + } + if (!dict.has(value)) { + if (!(value instanceof Object)) { + dict.set(value, value); + } else { + stack.push(element); + stack.push(value); + premature = true; + } + } + return {premature}; +} + export function makeObjectSerializable(obj: any): any { if (!(obj instanceof Object)) { return obj; } - if (obj instanceof Map) { - return { - __flipper_object_type__: 'Map', - data: [...obj].map(makeObjectSerializable), - }; - } else if (obj instanceof Set) { - return { - __flipper_object_type__: 'Set', - data: [...obj].map(makeObjectSerializable), - }; - } else if (obj instanceof Date) { - return { - __flipper_object_type__: 'Date', - data: obj.toString(), - }; - } else if (obj instanceof Array) { - return obj.map(makeObjectSerializable); + let stack = [obj]; + const dict: Map = new Map(); + while (stack.length > 0) { + const element = stack.pop(); + if (element instanceof Map) { + const arr = []; + let premature = false; + for (const item of [...element]) { + const key = item[0]; + const value = item[1]; + premature = processKeyValuePair(element, key, value, stack, dict) + .premature; + if (premature) { + break; + } + if (dict.has(key) && dict.has(value)) { + dict.set(item, [dict.get(key), dict.get(value)]); + arr.push([dict.get(key), dict.get(value)]); + } + } + if (premature) { + continue; + } + dict.set(element, { + __flipper_object_type__: 'Map', + data: arr, + }); + } else if (element instanceof Set) { + const {premature, outputArr} = processArray( + element, + [...element], + stack, + dict, + ); + if (premature) { + continue; + } + dict.set(element, { + __flipper_object_type__: 'Set', + data: outputArr, + }); + } else if (element instanceof Date) { + dict.set(element, { + __flipper_object_type__: 'Date', + data: element.toString(), + }); + } else if (element instanceof Array) { + const {premature, outputArr} = processArray( + element, + element, + stack, + dict, + ); + if (premature) { + continue; + } + dict.set(element, outputArr); + } else if (element instanceof Object) { + const array = Object.entries(element); + let obj = {}; + let premature = false; + for (const item of array) { + const key = item[0]; + const value = item[1]; + premature = processKeyValuePair(element, key, value, stack, dict) + .premature; + if (premature) { + break; + } + const serializedKey = dict.get(key); + const serializedValue = dict.get(value); + if (serializedValue && serializedKey) { + obj = {...obj, [serializedKey]: serializedValue}; + } + } + if (premature) { + continue; + } + dict.set(element, obj); + } } - - return Object.entries(obj).reduce( - (acc, [key, value]) => ({...acc, [key]: makeObjectSerializable(value)}), - {}, - ); + return dict.get(obj); } export function deserializeObject(obj: any): any {