From fd451caf901f3bf69a872437643fa38d5eda8924 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Mon, 5 Oct 2020 13:54:36 +0200 Subject: [PATCH] fix(Variables): Fix handling of circular object references --- lib/classes/Variables.js | 50 +++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/lib/classes/Variables.js b/lib/classes/Variables.js index dc6823aff2d..fdbef814381 100644 --- a/lib/classes/Variables.js +++ b/lib/classes/Variables.js @@ -241,7 +241,7 @@ class Variables { * Generate an array of objects noting the terminal properties of the given root object and their * paths * @param root The object to generate a terminal property path/value set for - * @param current The current part of the given root that terminal properties are being sought + * @param initCurrent The current part of the given root that terminal properties are being sought * within * @param [context] An array containing the path to the current object root (intended for internal * use) @@ -249,32 +249,30 @@ class Variables { * @returns {TerminalProperty[]} The terminal properties of the given root object, with the path * and value of each */ - getProperties(root, atRoot, current, cntxt, rslts) { - let context = cntxt; - if (!context) { - context = []; - } - let results = rslts; - if (!results) { - results = []; - } - const addContext = (value, key) => - this.getProperties(root, false, value, context.concat(key), results); - if (Array.isArray(current)) { - current.map(addContext); - } else if ( - _.isObject(current) && - !_.isDate(current) && - !_.isRegExp(current) && - typeof current !== 'function' - ) { - if (atRoot || current !== root) { - _.mapValues(current, addContext); + getProperties(root, initAtRoot, initCurrent, initContext, initResults) { + const processedMap = new WeakMap(); + return (function self(atRoot, current, context, results) { + if (processedMap.has(current)) return processedMap.get(current); + if (!context) context = []; + if (!results) results = []; + if (_.isObject(current)) processedMap.set(current, results); + const addContext = (value, key) => self(false, value, context.concat(key), results); + if (Array.isArray(current)) { + current.map(addContext); + } else if ( + _.isObject(current) && + !_.isDate(current) && + !_.isRegExp(current) && + typeof current !== 'function' + ) { + if (atRoot || current !== root) { + _.mapValues(current, addContext); + } + } else { + results.push({ path: context, value: current }); } - } else { - results.push({ path: context, value: current }); - } - return results; + return results; + })(initAtRoot, initCurrent, initContext, initResults); } /**