Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions scripts/fiber/tests-passing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1667,11 +1667,20 @@ src/renderers/shared/stack/reconciler/__tests__/Transaction-test.js
* should allow nesting of transactions

src/renderers/shared/utils/__tests__/ReactErrorUtils-test.js
* should call the callback with only the passed argument
* should catch errors
* should rethrow caught errors
* should call the callback with only the passed argument
* should use invokeGuardedCallbackWithCatch in production
* it should rethrow errors caught by invokeGuardedCallbackAndCatchFirstError (development)
* should call the callback the passed arguments (development)
* should call the callback with the provided context (development)
* should return a caught error (development)
* should return null if no error is thrown (development)
* can nest with same debug name (development)
* does not return nested errors (development)
* it should rethrow errors caught by invokeGuardedCallbackAndCatchFirstError (production)
* should call the callback the passed arguments (production)
* should call the callback with the provided context (production)
* should return a caught error (production)
* should return null if no error is thrown (production)
* can nest with same debug name (production)
* does not return nested errors (production)

src/renderers/shared/utils/__tests__/accumulateInto-test.js
* throws if the second item is null
Expand Down
7 changes: 0 additions & 7 deletions src/renderers/native/__tests__/ReactNativeEvents-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

var RCTEventEmitter;
var React;
var ReactErrorUtils;
var ReactNative;
var ResponderEventPlugin;
var UIManager;
Expand All @@ -24,16 +23,10 @@ beforeEach(() => {

RCTEventEmitter = require('RCTEventEmitter');
React = require('React');
ReactErrorUtils = require('ReactErrorUtils');
ReactNative = require('ReactNative');
ResponderEventPlugin = require('ResponderEventPlugin');
UIManager = require('UIManager');
createReactNativeComponentClass = require('createReactNativeComponentClass');

// Ensure errors from event callbacks are properly surfaced (otherwise,
// jest/jsdom swallows them when we do the .dispatchEvent call)
ReactErrorUtils.invokeGuardedCallback =
ReactErrorUtils.invokeGuardedCallbackWithCatch;
});

it('handles events', () => {
Expand Down
36 changes: 25 additions & 11 deletions src/renderers/shared/fiber/ReactFiberCommitWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var {
} = ReactTypeOfWork;
var { commitCallbacks } = require('ReactFiberUpdateQueue');
var { onCommitUnmount } = require('ReactFiberDevToolsHook');
var { invokeGuardedCallback } = require('ReactErrorUtils');

var {
Placement,
Expand Down Expand Up @@ -54,22 +55,35 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(

// Capture errors so they don't interrupt unmounting.
function safelyCallComponentWillUnmount(current, instance) {
try {
instance.componentWillUnmount();
} catch (error) {
captureError(current, error);
if (__DEV__) {
const unmountError = invokeGuardedCallback(null, instance.componentWillUnmount, instance);
if (unmountError) {
captureError(current, unmountError);
}
} else {
try {
instance.componentWillUnmount();
} catch (unmountError) {
captureError(current, unmountError);
}
}
}

// Capture errors so they don't interrupt unmounting.
function safelyDetachRef(current : Fiber) {
try {
const ref = current.ref;
if (ref !== null) {
ref(null);
const ref = current.ref;
if (ref !== null) {
if (__DEV__) {
const refError = invokeGuardedCallback(null, ref, null, null);
if (refError !== null) {
captureError(current, refError);
}
} else {
try {
ref(null);
} catch (refError) {
captureError(current, refError);
}
}
} catch (error) {
captureError(current, error);
}
}

Expand Down
63 changes: 43 additions & 20 deletions src/renderers/shared/fiber/ReactFiberScheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var {
getStackAddendumByWorkInProgressFiber,
} = require('ReactComponentTreeHook');
var { logCapturedError } = require('ReactFiberErrorLogger');
var { invokeGuardedCallback } = require('ReactErrorUtils');

var ReactFiberBeginWork = require('ReactFiberBeginWork');
var ReactFiberCompleteWork = require('ReactFiberCompleteWork');
Expand Down Expand Up @@ -164,6 +165,9 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
// Keeps track of whether we're currently in a work loop.
let isPerformingWork : boolean = false;

// Keeps track of whether the current deadline has expired.
let deadlineHasExpired : boolean = false;

// Keeps track of whether we should should batch sync updates.
let isBatchingUpdates : boolean = false;

Expand Down Expand Up @@ -414,9 +418,17 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
// ref unmounts.
nextEffect = firstEffect;
while (nextEffect !== null) {
try {
commitAllHostEffects(finishedWork);
} catch (error) {
let error = null;
if (__DEV__) {
error = invokeGuardedCallback(null, commitAllHostEffects, null, finishedWork);
} else {
try {
commitAllHostEffects(finishedWork);
} catch (e) {
error = e;
}
}
if (error !== null) {
invariant(
nextEffect !== null,
'Should have next effect. This error is likely caused by a bug ' +
Expand Down Expand Up @@ -444,9 +456,17 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
// This pass also triggers any renderer-specific initial effects.
nextEffect = firstEffect;
while (nextEffect !== null) {
try {
commitAllLifeCycles(finishedWork, nextEffect);
} catch (error) {
let error = null;
if (__DEV__) {
error = invokeGuardedCallback(null, commitAllLifeCycles, null, finishedWork);
} else {
try {
commitAllLifeCycles(finishedWork);
} catch (e) {
error = e;
}
}
if (error !== null) {
invariant(
nextEffect !== null,
'Should have next effect. This error is likely caused by a bug ' +
Expand Down Expand Up @@ -675,7 +695,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
}
}

function workLoop(priorityLevel, deadline : Deadline | null, deadlineHasExpired : boolean) : boolean {
function workLoop(priorityLevel, deadline : Deadline | null) {
// Clear any errors.
clearErrors();

Expand Down Expand Up @@ -743,8 +763,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
if (hostRootTimeMarker) {
console.timeEnd(hostRootTimeMarker);
}

return deadlineHasExpired;
}

function performWork(priorityLevel : PriorityLevel, deadline : Deadline | null) {
Expand All @@ -755,7 +773,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
);
isPerformingWork = true;
const isPerformingDeferredWork = Boolean(deadline);
let deadlineHasExpired = false;

// This outer loop exists so that we can restart the work loop after
// catching an error. It also lets us flush Task work at the end of a
Expand All @@ -776,18 +793,25 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
// Nothing in performWork should be allowed to throw. All unsafe
// operations must happen within workLoop, which is extracted to a
// separate function so that it can be optimized by the JS engine.
try {
priorityContextBeforeReconciliation = priorityContext;
priorityContext = nextPriorityLevel;
deadlineHasExpired = workLoop(priorityLevel, deadline, deadlineHasExpired);
} catch (error) {
priorityContextBeforeReconciliation = priorityContext;
let error = null;
if (__DEV__) {
error = invokeGuardedCallback(null, workLoop, null, priorityLevel, deadline);
} else {
try {
workLoop(priorityLevel, deadline);
} catch (e) {
error = e;
}
}
// Reset the priority context to its value before reconcilation.
priorityContext = priorityContextBeforeReconciliation;

if (error !== null) {
// We caught an error during either the begin or complete phases.
const failedWork = nextUnitOfWork;

if (failedWork !== null) {
// Reset the priority context to its value before reconciliation.
priorityContext = priorityContextBeforeReconciliation;

// "Capture" the error by finding the nearest boundary. If there is no
// error boundary, the nearest host container acts as one. If
// captureError returns null, the error was intentionally ignored.
Expand Down Expand Up @@ -818,8 +842,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
// inside resetAfterCommit.
fatalError = error;
}
} finally {
priorityContext = priorityContextBeforeReconciliation;
}

// Stop performing work
Expand Down Expand Up @@ -862,6 +884,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,

// We're done performing work. Time to clean up.
isPerformingWork = false;
deadlineHasExpired = false;
fatalError = null;
firstUncaughtError = null;
capturedErrors = null;
Expand Down
10 changes: 1 addition & 9 deletions src/renderers/shared/shared/event/EventPluginUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,7 @@ if (__DEV__) {
function executeDispatch(event, simulated, listener, inst) {
var type = event.type || 'unknown-event';
event.currentTarget = EventPluginUtils.getNodeFromInstance(inst);
if (simulated) {
ReactErrorUtils.invokeGuardedCallbackWithCatch(
type,
listener,
event
);
} else {
ReactErrorUtils.invokeGuardedCallback(type, listener, event);
}
ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
event.currentTarget = null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ var ReactCompositeComponent = {
if (safely) {
if (!skipLifecycle) {
var name = this.getName() + '.componentWillUnmount()';
ReactErrorUtils.invokeGuardedCallback(name, inst.componentWillUnmount.bind(inst));
ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(name, inst.componentWillUnmount, inst);
}
} else {
if (__DEV__) {
Expand Down
Loading