Skip to content

Commit 31cfbf6

Browse files
committed
Move stack trace cache inside V8 implementation
The fact that we need this cache is really a V8 quirk of only allowing structured data once. It's technically possible that the implementation also doesn't need parsing and doesn't need a cache. It's also not technically correct to use a shared cache for two different requests since the filter is on the request. But mainly I'm doing this so that I can access the filtered frames.
1 parent 6b81370 commit 31cfbf6

File tree

2 files changed

+23
-22
lines changed

2 files changed

+23
-22
lines changed

packages/react-server/src/ReactFlightServer.js

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -164,33 +164,18 @@ function defaultFilterStackFrame(
164164
);
165165
}
166166

167-
// DEV-only cache of parsed and filtered stack frames.
168-
const stackTraceCache: WeakMap<Error, ReactStackTrace> = __DEV__
169-
? new WeakMap()
170-
: (null: any);
171-
172167
function filterStackTrace(
173168
request: Request,
174169
error: Error,
175170
skipFrames: number,
176171
): ReactStackTrace {
177-
const existing = stackTraceCache.get(error);
178-
if (existing !== undefined) {
179-
// Return a clone because the Flight protocol isn't yet resilient to deduping
180-
// objects in the debug info. TODO: Support deduping stacks.
181-
const clone = existing.slice(0);
182-
for (let i = 0; i < clone.length; i++) {
183-
// $FlowFixMe[invalid-tuple-arity]
184-
clone[i] = clone[i].slice(0);
185-
}
186-
return clone;
187-
}
188172
// Since stacks can be quite large and we pass a lot of them, we filter them out eagerly
189173
// to save bandwidth even in DEV. We'll also replay these stacks on the client so by
190174
// stripping them early we avoid that overhead. Otherwise we'd normally just rely on
191175
// the DevTools or framework's ignore lists to filter them out.
192176
const filterStackFrame = request.filterStackFrame;
193177
const stack = parseStackTrace(error, skipFrames);
178+
const filteredStack = [];
194179
for (let i = 0; i < stack.length; i++) {
195180
const callsite = stack[i];
196181
const functionName = callsite[0];
@@ -203,16 +188,18 @@ function filterStackTrace(
203188
const envIdx = url.indexOf('/', 12);
204189
const suffixIdx = url.lastIndexOf('?');
205190
if (envIdx > -1 && suffixIdx > -1) {
206-
url = callsite[1] = url.slice(envIdx + 1, suffixIdx);
191+
url = url.slice(envIdx + 1, suffixIdx);
207192
}
208193
}
209-
if (!filterStackFrame(url, functionName)) {
210-
stack.splice(i, 1);
211-
i--;
194+
if (filterStackFrame(url, functionName)) {
195+
// Use a clone because the Flight protocol isn't yet resilient to deduping
196+
// objects in the debug info. TODO: Support deduping stacks.
197+
const clone = callsite.slice(0);
198+
clone[1] = url;
199+
filteredStack.push(clone);
212200
}
213201
}
214-
stackTraceCache.set(error, stack);
215-
return stack;
202+
return filteredStack;
216203
}
217204

218205
initAsyncDebugInfo();

packages/react-server/src/ReactFlightStackConfigV8.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,22 @@ function collectStackTrace(
126126
const frameRegExp =
127127
/^ {3} at (?:(.+) \((?:(.+):(\d+):(\d+)|\<anonymous\>)\)|(?:async )?(.+):(\d+):(\d+)|\<anonymous\>)$/;
128128

129+
// DEV-only cache of parsed and filtered stack frames.
130+
const stackTraceCache: WeakMap<Error, ReactStackTrace> = __DEV__
131+
? new WeakMap()
132+
: (null: any);
133+
129134
export function parseStackTrace(
130135
error: Error,
131136
skipFrames: number,
132137
): ReactStackTrace {
138+
// We can only get structured data out of error objects once. So we cache the information
139+
// so we can get it again each time. It also helps performance when the same error is
140+
// referenced more than once.
141+
const existing = stackTraceCache.get(error);
142+
if (existing !== undefined) {
143+
return existing;
144+
}
133145
// We override Error.prepareStackTrace with our own version that collects
134146
// the structured data. We need more information than the raw stack gives us
135147
// and we need to ensure that we don't get the source mapped version.
@@ -148,6 +160,7 @@ export function parseStackTrace(
148160
if (collectedStackTrace !== null) {
149161
const result = collectedStackTrace;
150162
collectedStackTrace = null;
163+
stackTraceCache.set(error, result);
151164
return result;
152165
}
153166

@@ -191,5 +204,6 @@ export function parseStackTrace(
191204
const col = +(parsed[4] || parsed[7]);
192205
parsedFrames.push([name, filename, line, col, 0, 0]);
193206
}
207+
stackTraceCache.set(error, parsedFrames);
194208
return parsedFrames;
195209
}

0 commit comments

Comments
 (0)