| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| /** | |
| * Counts React commits, rendered fibers, and the `setState` calls that caused | |
| * them, from inside the renderer (#4109). | |
| * | |
| * Injected with `Page.addScriptToEvaluateOnNewDocument` before the app loads, | |
| * because both halves have to be in place first: | |
| * | |
| * 1. `__REACT_DEVTOOLS_GLOBAL_HOOK__` must exist before `react-dom` looks for | |
| * it, or no commit is ever reported. This is a minimal stand-in for the | |
| * DevTools hook — enough of the shape that react-dom injects into it. | |
| * | |
| * 2. `Function.prototype.bind` is wrapped to catch React creating a state | |
| * dispatcher. React binds `dispatchSetState` as `(null, fiber, queue)`, | |
| * and the queue is the only object in the runtime carrying | |
| * `lastRenderedReducer`, so that triple identifies it with no version | |
| * coupling to react-dom's internals. | |
| * | |
| * A fiber counts as rendered when it carries `Placement` (`flags & 1`) and its | |
| * alternate was last seen in the previous commit. That is identity-insensitive: | |
| * it reports what React actually re-rendered, not what a `memo` comparator was | |
| * asked about. | |
| * | |
| * `arm` gates recording so a measurement covers one interaction rather than the | |
| * app's whole life. Set `__MAKA_PROBE__.arm = false` before profiling with the | |
| * sampler, or the probe's own walk lands in the profile. | |
| */ | |
| (() => { | |
| if (globalThis.__MAKA_PROBE__) return 'already'; | |
| const probe = { | |
| commits: [], | |
| dispatches: [], | |
| arm: false, | |
| roots: new Set(), | |
| rootIdx: new WeakMap(), | |
| qid: 0, | |
| qlabel: new Map(), | |
| }; | |
| globalThis.__MAKA_PROBE__ = probe; | |
| const originalBind = Function.prototype.bind; | |
| Function.prototype.bind = function bind(...args) { | |
| const bound = originalBind.apply(this, args); | |
| try { | |
| const queue = args[2]; | |
| if ( | |
| args.length === 3 && | |
| args[0] === null && | |
| queue && | |
| typeof queue === 'object' && | |
| 'lastRenderedReducer' in queue && | |
| !queue.__qid | |
| ) { | |
| queue.__qid = ++probe.qid; | |
| const qid = queue.__qid; | |
| return function dispatchWithRecord(action) { | |
| if (probe.arm && probe.dispatches.length < 500) { | |
| probe.dispatches.push({ | |
| t: performance.now(), | |
| qid, | |
| prev: brief(queue.lastRenderedState), | |
| action: brief(action), | |
| st: (new Error().stack || '') | |
| .split('\n') | |
| .slice(2, 8) | |
| .map((line) => line.trim().slice(0, 130)), | |
| }); | |
| } | |
| return bound.apply(this, arguments); | |
| }; | |
| } | |
| } catch { | |
| // A bind we cannot inspect is a bind we do not care about. | |
| } | |
| return bound; | |
| }; | |
| function brief(value) { | |
| try { | |
| if (value == null) return String(value); | |
| if (typeof value === 'function') return 'fn'; | |
| if (typeof value !== 'object') return String(value).slice(0, 40); | |
| if (Array.isArray(value)) return `Arr${value.length}`; | |
| return `{${Object.keys(value).slice(0, 6)}}`; | |
| } catch { | |
| return '?'; | |
| } | |
| } | |
| const nameOf = (fiber) => { | |
| const type = fiber.type ?? fiber.elementType; | |
| if (typeof type === 'string') return type; | |
| if (typeof type === 'function') return type.displayName || type.name || 'anon'; | |
| if (type && typeof type === 'object') { | |
| const inner = type.type ?? type.render; | |
| return ( | |
| type.displayName || | |
| (typeof inner === 'function' ? inner.displayName || inner.name : null) || | |
| 'obj' | |
| ); | |
| } | |
| return `tag${fiber.tag}`; | |
| }; | |
| const hook = { | |
| renderers: new Map(), | |
| supportsFiber: true, | |
| _nextId: 1, | |
| inject(renderer) { | |
| const id = hook._nextId++; | |
| hook.renderers.set(id, renderer); | |
| return id; | |
| }, | |
| checkDCE() {}, | |
| onScheduleFiberRoot() {}, | |
| onCommitFiberUnmount() {}, | |
| onPostCommitFiberRoot() {}, | |
| setStrictMode() {}, | |
| getFiberRoots() { | |
| return probe.roots; | |
| }, | |
| on() {}, | |
| off() {}, | |
| sub() { | |
| return () => {}; | |
| }, | |
| emit() {}, | |
| onCommitFiberRoot(_id, root) { | |
| probe.roots.add(root); | |
| const idx = (probe.rootIdx.get(root) || 0) + 1; | |
| probe.rootIdx.set(root, idx); | |
| const key = '__pc'; | |
| let total = 0; | |
| let visited = 0; | |
| const renderRoots = []; | |
| const stack = [[root.current, 0, false]]; | |
| while (stack.length) { | |
| const [fiber, depth, hasRenderedAncestor] = stack.pop(); | |
| if (!fiber || visited++ > 90000) break; | |
| const alternate = fiber.alternate; | |
| const rendered = fiber.flags & 1 && alternate && alternate[key] === idx - 1; | |
| if (rendered) total++; | |
| let ancestor = hasRenderedAncestor; | |
| if (rendered && !hasRenderedAncestor && probe.arm) { | |
| ancestor = true; | |
| renderRoots.push({ d: depth, name: nameOf(fiber) }); | |
| } | |
| let state = fiber.memoizedState; | |
| let i = 0; | |
| while (state && i < 1200) { | |
| if (state.queue?.__qid && !probe.qlabel.has(state.queue.__qid)) { | |
| probe.qlabel.set(state.queue.__qid, `${nameOf(fiber)}@d${depth}#h${i}`); | |
| } | |
| state = state.next; | |
| i++; | |
| } | |
| fiber[key] = idx; | |
| if (fiber.child) stack.push([fiber.child, depth + 1, ancestor]); | |
| if (fiber.sibling) stack.push([fiber.sibling, depth, hasRenderedAncestor]); | |
| } | |
| if (probe.arm && probe.commits.length < 300) { | |
| renderRoots.sort((a, b) => a.d - b.d); | |
| probe.commits.push({ t: performance.now(), idx, total, rr: renderRoots.slice(0, 3) }); | |
| } | |
| }, | |
| }; | |
| Object.defineProperty(globalThis, '__REACT_DEVTOOLS_GLOBAL_HOOK__', { | |
| value: hook, | |
| writable: false, | |
| configurable: true, | |
| }); | |
| return 'installed'; | |
| })(); |