From 67a3fb99ef173a8beda2b414effd1e3ffb2f4f01 Mon Sep 17 00:00:00 2001 From: Aaron Greenberg
Date: Mon, 24 Apr 2017 17:55:12 -0400 Subject: [PATCH] Fix cloneDeep with circularly dependent Sets/Maps (#3123) --- .internal/baseClone.js | 29 +++++++++++++++++++++-------- .internal/cloneMap.js | 22 ---------------------- .internal/cloneSet.js | 22 ---------------------- 3 files changed, 21 insertions(+), 52 deletions(-) delete mode 100644 .internal/cloneMap.js delete mode 100644 .internal/cloneSet.js diff --git a/.internal/baseClone.js b/.internal/baseClone.js index 7f3804c9e..d2f9483d5 100644 --- a/.internal/baseClone.js +++ b/.internal/baseClone.js @@ -7,9 +7,7 @@ import cloneBuffer from './cloneBuffer.js' import copyArray from './copyArray.js' import cloneArrayBuffer from './cloneArrayBuffer.js' import cloneDataView from './cloneDataView.js' -import cloneMap from './cloneMap.js' import cloneRegExp from './cloneRegExp.js' -import cloneSet from './cloneSet.js' import cloneSymbol from './cloneSymbol.js' import cloneTypedArray from './cloneTypedArray.js' import copySymbols from './copySymbols.js' @@ -19,7 +17,9 @@ import getAllKeysIn from './getAllKeysIn.js' import getTag from './getTag.js' import initCloneObject from './initCloneObject.js' import isBuffer from '../isBuffer.js' +import isMap from '../isMap.js' import isObject from '../isObject.js' +import isSet from '../isSet.js' import keys from '../keys.js' /** Used to compose bitmasks for cloning. */ @@ -79,16 +79,15 @@ const hasOwnProperty = Object.prototype.hasOwnProperty * Initializes an object clone based on its `toStringTag`. * * **Note:** This function only supports cloning values with tags of - * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`. * * @private * @param {Object} object The object to clone. * @param {string} tag The `toStringTag` of the object to clone. - * @param {Function} cloneFunc The function to clone values. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the initialized clone. */ -function initCloneByTag(object, tag, cloneFunc, isDeep) { +function initCloneByTag(object, tag, isDeep) { const Ctor = object.constructor switch (tag) { case arrayBufferTag: @@ -107,7 +106,7 @@ function initCloneByTag(object, tag, cloneFunc, isDeep) { return cloneTypedArray(object, isDeep) case mapTag: - return cloneMap(object, isDeep, cloneFunc) + return new Ctor case numberTag: case stringTag: @@ -117,7 +116,7 @@ function initCloneByTag(object, tag, cloneFunc, isDeep) { return cloneRegExp(object) case setTag: - return cloneSet(object, isDeep, cloneFunc) + return new Ctor case symbolTag: return cloneSymbol(object) @@ -198,7 +197,7 @@ function baseClone(value, bitmask, customizer, key, object, stack) { if (!cloneableTags[tag]) { return object ? value : {} } - result = initCloneByTag(value, tag, baseClone, isDeep) + result = initCloneByTag(value, tag, isDeep) } } // Check for circular references and return its corresponding clone. @@ -209,6 +208,20 @@ function baseClone(value, bitmask, customizer, key, object, stack) { } stack.set(value, result) + if (isSet(value)) { + value.forEach(subValue => { + result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack)) + }) + return result + } + + if (isMap(value)) { + value.forEach((subValue, key) => { + result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack)) + }) + return result + } + const keysFunc = isFull ? (isFlat ? getAllKeysIn : getAllKeys) : (isFlat ? keysIn : keys) diff --git a/.internal/cloneMap.js b/.internal/cloneMap.js deleted file mode 100644 index 5927d2177..000000000 --- a/.internal/cloneMap.js +++ /dev/null @@ -1,22 +0,0 @@ -import addMapEntry from './addMapEntry.js' -import arrayReduce from './arrayReduce.js' -import mapToArray from './mapToArray.js' - -/** Used to compose bitmasks for cloning. */ -const CLONE_DEEP_FLAG = 1 - -/** - * Creates a clone of `map`. - * - * @private - * @param {Object} map The map to clone. - * @param {Function} cloneFunc The function to clone values. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the cloned map. - */ -function cloneMap(map, isDeep, cloneFunc) { - const array = isDeep ? cloneFunc(mapToArray(map), CLONE_DEEP_FLAG) : mapToArray(map) - return arrayReduce(array, addMapEntry, new map.constructor) -} - -export default cloneMap diff --git a/.internal/cloneSet.js b/.internal/cloneSet.js deleted file mode 100644 index bb354d8e9..000000000 --- a/.internal/cloneSet.js +++ /dev/null @@ -1,22 +0,0 @@ -import addSetEntry from './addSetEntry.js' -import arrayReduce from './arrayReduce.js' -import setToArray from './setToArray.js' - -/** Used to compose bitmasks for cloning. */ -const CLONE_DEEP_FLAG = 1 - -/** - * Creates a clone of `set`. - * - * @private - * @param {Object} set The set to clone. - * @param {Function} cloneFunc The function to clone values. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the cloned set. - */ -function cloneSet(set, isDeep, cloneFunc) { - const array = isDeep ? cloneFunc(setToArray(set), CLONE_DEEP_FLAG) : setToArray(set) - return arrayReduce(array, addSetEntry, new set.constructor) -} - -export default cloneSet