mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
1cc9a5dc71
* prevent spamming warnings related to performance measurement code * minor changes in names and such * - * -
337 lines
9.9 KiB
JavaScript
337 lines
9.9 KiB
JavaScript
/**
|
|
* Copyright 2016-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDebugTool
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactInvalidSetStateWarningDevTool = require('ReactInvalidSetStateWarningDevTool');
|
|
var ReactHostOperationHistoryDevtool = require('ReactHostOperationHistoryDevtool');
|
|
var ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
|
|
var ReactChildrenMutationWarningDevtool = require('ReactChildrenMutationWarningDevtool');
|
|
var ExecutionEnvironment = require('ExecutionEnvironment');
|
|
|
|
var performanceNow = require('performanceNow');
|
|
var warning = require('warning');
|
|
|
|
var eventHandlers = [];
|
|
var handlerDoesThrowForEvent = {};
|
|
|
|
function emitEvent(handlerFunctionName, arg1, arg2, arg3, arg4, arg5) {
|
|
eventHandlers.forEach(function(handler) {
|
|
try {
|
|
if (handler[handlerFunctionName]) {
|
|
handler[handlerFunctionName](arg1, arg2, arg3, arg4, arg5);
|
|
}
|
|
} catch (e) {
|
|
warning(
|
|
handlerDoesThrowForEvent[handlerFunctionName],
|
|
'exception thrown by devtool while handling %s: %s',
|
|
handlerFunctionName,
|
|
e + '\n' + e.stack
|
|
);
|
|
handlerDoesThrowForEvent[handlerFunctionName] = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
var isProfiling = false;
|
|
var flushHistory = [];
|
|
var lifeCycleTimerStack = [];
|
|
var currentFlushNesting = 0;
|
|
var currentFlushMeasurements = null;
|
|
var currentFlushStartTime = null;
|
|
var currentTimerDebugID = null;
|
|
var currentTimerStartTime = null;
|
|
var currentTimerNestedFlushDuration = null;
|
|
var currentTimerType = null;
|
|
|
|
var lifeCycleTimerHasWarned = false;
|
|
|
|
function clearHistory() {
|
|
ReactComponentTreeDevtool.purgeUnmountedComponents();
|
|
ReactHostOperationHistoryDevtool.clearHistory();
|
|
}
|
|
|
|
function getTreeSnapshot(registeredIDs) {
|
|
return registeredIDs.reduce((tree, id) => {
|
|
var ownerID = ReactComponentTreeDevtool.getOwnerID(id);
|
|
var parentID = ReactComponentTreeDevtool.getParentID(id);
|
|
tree[id] = {
|
|
displayName: ReactComponentTreeDevtool.getDisplayName(id),
|
|
text: ReactComponentTreeDevtool.getText(id),
|
|
updateCount: ReactComponentTreeDevtool.getUpdateCount(id),
|
|
childIDs: ReactComponentTreeDevtool.getChildIDs(id),
|
|
// Text nodes don't have owners but this is close enough.
|
|
ownerID: ownerID || ReactComponentTreeDevtool.getOwnerID(parentID),
|
|
parentID,
|
|
};
|
|
return tree;
|
|
}, {});
|
|
}
|
|
|
|
function resetMeasurements() {
|
|
var previousStartTime = currentFlushStartTime;
|
|
var previousMeasurements = currentFlushMeasurements || [];
|
|
var previousOperations = ReactHostOperationHistoryDevtool.getHistory();
|
|
|
|
if (currentFlushNesting === 0) {
|
|
currentFlushStartTime = null;
|
|
currentFlushMeasurements = null;
|
|
clearHistory();
|
|
return;
|
|
}
|
|
|
|
if (previousMeasurements.length || previousOperations.length) {
|
|
var registeredIDs = ReactComponentTreeDevtool.getRegisteredIDs();
|
|
flushHistory.push({
|
|
duration: performanceNow() - previousStartTime,
|
|
measurements: previousMeasurements || [],
|
|
operations: previousOperations || [],
|
|
treeSnapshot: getTreeSnapshot(registeredIDs),
|
|
});
|
|
}
|
|
|
|
clearHistory();
|
|
currentFlushStartTime = performanceNow();
|
|
currentFlushMeasurements = [];
|
|
}
|
|
|
|
function checkDebugID(debugID) {
|
|
warning(debugID, 'ReactDebugTool: debugID may not be empty.');
|
|
}
|
|
|
|
function beginLifeCycleTimer(debugID, timerType) {
|
|
if (currentFlushNesting === 0) {
|
|
return;
|
|
}
|
|
if (currentTimerType && !lifeCycleTimerHasWarned) {
|
|
warning(
|
|
false,
|
|
'There is an internal error in the React performance measurement code. ' +
|
|
'Did not expect %s timer to start while %s timer is still in ' +
|
|
'progress for %s instance.',
|
|
timerType,
|
|
currentTimerType || 'no',
|
|
(debugID === currentTimerDebugID) ? 'the same' : 'another'
|
|
);
|
|
lifeCycleTimerHasWarned = true;
|
|
}
|
|
currentTimerStartTime = performanceNow();
|
|
currentTimerNestedFlushDuration = 0;
|
|
currentTimerDebugID = debugID;
|
|
currentTimerType = timerType;
|
|
}
|
|
|
|
function endLifeCycleTimer(debugID, timerType) {
|
|
if (currentFlushNesting === 0) {
|
|
return;
|
|
}
|
|
if (currentTimerType !== timerType && !lifeCycleTimerHasWarned) {
|
|
warning(
|
|
false,
|
|
'There is an internal error in the React performance measurement code. ' +
|
|
'We did not expect %s timer to stop while %s timer is still in ' +
|
|
'progress for %s instance. Please report this as a bug in React.',
|
|
timerType,
|
|
currentTimerType || 'no',
|
|
(debugID === currentTimerDebugID) ? 'the same' : 'another'
|
|
);
|
|
lifeCycleTimerHasWarned = true;
|
|
}
|
|
if (isProfiling) {
|
|
currentFlushMeasurements.push({
|
|
timerType,
|
|
instanceID: debugID,
|
|
duration: performanceNow() - currentTimerStartTime - currentTimerNestedFlushDuration,
|
|
});
|
|
}
|
|
currentTimerStartTime = null;
|
|
currentTimerNestedFlushDuration = null;
|
|
currentTimerDebugID = null;
|
|
currentTimerType = null;
|
|
}
|
|
|
|
function pauseCurrentLifeCycleTimer() {
|
|
var currentTimer = {
|
|
startTime: currentTimerStartTime,
|
|
nestedFlushStartTime: performanceNow(),
|
|
debugID: currentTimerDebugID,
|
|
timerType: currentTimerType,
|
|
};
|
|
lifeCycleTimerStack.push(currentTimer);
|
|
currentTimerStartTime = null;
|
|
currentTimerNestedFlushDuration = null;
|
|
currentTimerDebugID = null;
|
|
currentTimerType = null;
|
|
}
|
|
|
|
function resumeCurrentLifeCycleTimer() {
|
|
var {startTime, nestedFlushStartTime, debugID, timerType} = lifeCycleTimerStack.pop();
|
|
var nestedFlushDuration = performanceNow() - nestedFlushStartTime;
|
|
currentTimerStartTime = startTime;
|
|
currentTimerNestedFlushDuration += nestedFlushDuration;
|
|
currentTimerDebugID = debugID;
|
|
currentTimerType = timerType;
|
|
}
|
|
|
|
var ReactDebugTool = {
|
|
addDevtool(devtool) {
|
|
eventHandlers.push(devtool);
|
|
},
|
|
removeDevtool(devtool) {
|
|
for (var i = 0; i < eventHandlers.length; i++) {
|
|
if (eventHandlers[i] === devtool) {
|
|
eventHandlers.splice(i, 1);
|
|
i--;
|
|
}
|
|
}
|
|
},
|
|
isProfiling() {
|
|
return isProfiling;
|
|
},
|
|
beginProfiling() {
|
|
if (isProfiling) {
|
|
return;
|
|
}
|
|
|
|
isProfiling = true;
|
|
flushHistory.length = 0;
|
|
resetMeasurements();
|
|
ReactDebugTool.addDevtool(ReactHostOperationHistoryDevtool);
|
|
},
|
|
endProfiling() {
|
|
if (!isProfiling) {
|
|
return;
|
|
}
|
|
|
|
isProfiling = false;
|
|
resetMeasurements();
|
|
ReactDebugTool.removeDevtool(ReactHostOperationHistoryDevtool);
|
|
},
|
|
getFlushHistory() {
|
|
return flushHistory;
|
|
},
|
|
onBeginFlush() {
|
|
currentFlushNesting++;
|
|
resetMeasurements();
|
|
pauseCurrentLifeCycleTimer();
|
|
emitEvent('onBeginFlush');
|
|
},
|
|
onEndFlush() {
|
|
resetMeasurements();
|
|
currentFlushNesting--;
|
|
resumeCurrentLifeCycleTimer();
|
|
emitEvent('onEndFlush');
|
|
},
|
|
onBeginLifeCycleTimer(debugID, timerType) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onBeginLifeCycleTimer', debugID, timerType);
|
|
beginLifeCycleTimer(debugID, timerType);
|
|
},
|
|
onEndLifeCycleTimer(debugID, timerType) {
|
|
checkDebugID(debugID);
|
|
endLifeCycleTimer(debugID, timerType);
|
|
emitEvent('onEndLifeCycleTimer', debugID, timerType);
|
|
},
|
|
onBeginReconcilerTimer(debugID, timerType) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onBeginReconcilerTimer', debugID, timerType);
|
|
},
|
|
onEndReconcilerTimer(debugID, timerType) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onEndReconcilerTimer', debugID, timerType);
|
|
},
|
|
onError(debugID) {
|
|
if (currentTimerDebugID != null) {
|
|
endLifeCycleTimer(currentTimerDebugID, currentTimerType);
|
|
}
|
|
emitEvent('onError', debugID);
|
|
},
|
|
onBeginProcessingChildContext() {
|
|
emitEvent('onBeginProcessingChildContext');
|
|
},
|
|
onEndProcessingChildContext() {
|
|
emitEvent('onEndProcessingChildContext');
|
|
},
|
|
onHostOperation(debugID, type, payload) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onHostOperation', debugID, type, payload);
|
|
},
|
|
onComponentHasMounted(debugID) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onComponentHasMounted', debugID);
|
|
},
|
|
onComponentHasUpdated(debugID) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onComponentHasUpdated', debugID);
|
|
},
|
|
onSetState() {
|
|
emitEvent('onSetState');
|
|
},
|
|
onSetDisplayName(debugID, displayName) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onSetDisplayName', debugID, displayName);
|
|
},
|
|
onSetChildren(debugID, childDebugIDs) {
|
|
checkDebugID(debugID);
|
|
childDebugIDs.forEach(checkDebugID);
|
|
emitEvent('onSetChildren', debugID, childDebugIDs);
|
|
},
|
|
onSetOwner(debugID, ownerDebugID) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onSetOwner', debugID, ownerDebugID);
|
|
},
|
|
onSetParent(debugID, parentDebugID) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onSetParent', debugID, parentDebugID);
|
|
},
|
|
onSetText(debugID, text) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onSetText', debugID, text);
|
|
},
|
|
onMountRootComponent(debugID) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onMountRootComponent', debugID);
|
|
},
|
|
onBeforeMountComponent(debugID, element) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onBeforeMountComponent', debugID, element);
|
|
},
|
|
onMountComponent(debugID) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onMountComponent', debugID);
|
|
},
|
|
onBeforeUpdateComponent(debugID, element) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onBeforeUpdateComponent', debugID, element);
|
|
},
|
|
onUpdateComponent(debugID) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onUpdateComponent', debugID);
|
|
},
|
|
onUnmountComponent(debugID) {
|
|
checkDebugID(debugID);
|
|
emitEvent('onUnmountComponent', debugID);
|
|
},
|
|
onTestEvent() {
|
|
emitEvent('onTestEvent');
|
|
},
|
|
};
|
|
|
|
ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
|
|
ReactDebugTool.addDevtool(ReactComponentTreeDevtool);
|
|
ReactDebugTool.addDevtool(ReactChildrenMutationWarningDevtool);
|
|
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
|
|
if ((/[?&]react_perf\b/).test(url)) {
|
|
ReactDebugTool.beginProfiling();
|
|
}
|
|
|
|
module.exports = ReactDebugTool;
|