Skip to content

Commit 3223048

Browse files
author
Luca Forstner
committed
Fix bug in dropUndefinedKeys that kept references to original data
1 parent 06a2b2d commit 3223048

File tree

2 files changed

+37
-23
lines changed
  • packages
    • integration-tests/suites/public-api/startTransaction/circular_data
    • utils/src

2 files changed

+37
-23
lines changed

packages/integration-tests/suites/public-api/startTransaction/circular_data/test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ sentryTest('should be able to handle circular data', async ({ getLocalTestPath,
1313

1414
expect(eventData.contexts).toMatchObject({
1515
trace: {
16-
data: { lays: { contains: { lays: { contains: '[Circular ~]' } } } },
16+
data: { lays: { contains: '[Circular ~]' } },
1717
},
1818
});
1919

packages/utils/src/object.ts

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { WrappedFunction } from '@sentry/types';
44

55
import { htmlTreeAsString } from './browser';
66
import { isElement, isError, isEvent, isInstanceOf, isPlainObject, isPrimitive } from './is';
7-
import { memoBuilder, MemoFunc } from './memo';
87
import { truncate } from './string';
98

109
/**
@@ -204,42 +203,57 @@ export function extractExceptionKeysForMessage(exception: Record<string, unknown
204203
}
205204

206205
/**
207-
* Given any object, return the new object with removed keys that value was `undefined`.
206+
* Given any object, return a new object having removed all fields whose value was `undefined`.
208207
* Works recursively on objects and arrays.
209208
*
210209
* Attention: This function keeps circular references in the returned object.
211210
*/
212-
export function dropUndefinedKeys<T>(val: T): T {
211+
export function dropUndefinedKeys<T>(inputValue: T): T {
212+
// This map keeps track of what already visited nodes map to.
213+
// Our Set - based memoBuilder doesn't work here because we want to the output object to have the same circular
214+
// references as the input object.
215+
const memoizationMap = new Map<unknown, unknown>();
216+
213217
// This function just proxies `_dropUndefinedKeys` to keep the `memoBuilder` out of this function's API
214-
return _dropUndefinedKeys(val, memoBuilder());
218+
return _dropUndefinedKeys(inputValue, memoizationMap);
215219
}
216220

217-
function _dropUndefinedKeys<T>(val: T, memo: MemoFunc): T {
218-
const [memoize] = memo; // we don't need unmemoize because we don't need to visit nodes twice
219-
220-
if (isPlainObject(val)) {
221-
if (memoize(val)) {
222-
return val;
221+
function _dropUndefinedKeys<T>(inputValue: T, memoizationMap: Map<unknown, unknown>): T {
222+
if (isPlainObject(inputValue)) {
223+
const memoVal = memoizationMap.get(inputValue);
224+
if (memoVal !== undefined) {
225+
return memoVal as T;
223226
}
224-
const rv: { [key: string]: any } = {};
225-
for (const key of Object.keys(val)) {
226-
if (typeof val[key] !== 'undefined') {
227-
rv[key] = _dropUndefinedKeys(val[key], memo);
227+
228+
const returnValue: { [key: string]: any } = {};
229+
memoizationMap.set(inputValue, returnValue);
230+
231+
for (const key of Object.keys(inputValue)) {
232+
if (typeof inputValue[key] !== 'undefined') {
233+
returnValue[key] = _dropUndefinedKeys(inputValue[key], memoizationMap);
228234
}
229235
}
230-
return rv as T;
236+
237+
return returnValue as T;
231238
}
232239

233-
if (Array.isArray(val)) {
234-
if (memoize(val)) {
235-
return val;
240+
if (Array.isArray(inputValue)) {
241+
const memoVal = memoizationMap.get(inputValue);
242+
if (memoVal !== undefined) {
243+
return memoVal as T;
236244
}
237-
return (val as any[]).map(item => {
238-
return _dropUndefinedKeys(item, memo);
239-
}) as any;
245+
246+
const returnValue: unknown[] = [];
247+
memoizationMap.set(inputValue, returnValue);
248+
249+
inputValue.forEach((item: unknown) => {
250+
returnValue.push(_dropUndefinedKeys(item, memoizationMap));
251+
});
252+
253+
return returnValue as unknown as T;
240254
}
241255

242-
return val;
256+
return inputValue;
243257
}
244258

245259
/**

0 commit comments

Comments
 (0)