@samitouri / QOS-React-1 / commits / b8ae38f88b

Audit try/finally around console patching (#31286)

Otherwise if something errors they can be left patched. [Review without whitespace](https://github.com/facebook/react/pull/31286/files?w=1)

Sebastian Markbåge committed Oct 18, 2024 at 12:05 UTC b8ae38f88b70f8a0ea96421a4355266aafefee7f
4 files changed +197 -184
packages/react-devtools-shared/src/backend/shared/DevToolsComponentStackFrame.js
+83 -84
@@ -82,105 +82,104 @@ export function describeNativeComponentFrame(
82 const previousDispatcher = currentDispatcherRef.H;
83 currentDispatcherRef.H = null;
84 disableLogs();
85 + try {
86 + // NOTE: keep in sync with the implementation in ReactComponentStackFrame
87
86 - // NOTE: keep in sync with the implementation in ReactComponentStackFrame
87 -
88 - /**
89 - * Finding a common stack frame between sample and control errors can be
90 - * tricky given the different types and levels of stack trace truncation from
91 - * different JS VMs. So instead we'll attempt to control what that common
92 - * frame should be through this object method:
93 - * Having both the sample and control errors be in the function under the
94 - * `DescribeNativeComponentFrameRoot` property, + setting the `name` and
95 - * `displayName` properties of the function ensures that a stack
96 - * frame exists that has the method name `DescribeNativeComponentFrameRoot` in
97 - * it for both control and sample stacks.
98 - */
99 - const RunInRootFrame = {
100 - DetermineComponentFrameRoot(): [?string, ?string] {
101 - let control;
102 - try {
103 - // This should throw.
104 - if (construct) {
105 - // Something should be setting the props in the constructor.
106 - const Fake = function () {
107 - throw Error();
108 - };
109 - // $FlowFixMe[prop-missing]
110 - Object.defineProperty(Fake.prototype, 'props', {
111 - set: function () {
112 - // We use a throwing setter instead of frozen or non-writable props
113 - // because that won't throw in a non-strict mode function.
88 + /**
89 + * Finding a common stack frame between sample and control errors can be
90 + * tricky given the different types and levels of stack trace truncation from
91 + * different JS VMs. So instead we'll attempt to control what that common
92 + * frame should be through this object method:
93 + * Having both the sample and control errors be in the function under the
94 + * `DescribeNativeComponentFrameRoot` property, + setting the `name` and
95 + * `displayName` properties of the function ensures that a stack
96 + * frame exists that has the method name `DescribeNativeComponentFrameRoot` in
97 + * it for both control and sample stacks.
98 + */
99 + const RunInRootFrame = {
100 + DetermineComponentFrameRoot(): [?string, ?string] {
101 + let control;
102 + try {
103 + // This should throw.
104 + if (construct) {
105 + // Something should be setting the props in the constructor.
106 + const Fake = function () {
107 throw Error();
115 - },
116 - });
117 - if (typeof Reflect === 'object' && Reflect.construct) {
118 - // We construct a different control for this case to include any extra
119 - // frames added by the construct call.
120 - try {
121 - Reflect.construct(Fake, []);
122 - } catch (x) {
123 - control = x;
108 + };
109 + // $FlowFixMe[prop-missing]
110 + Object.defineProperty(Fake.prototype, 'props', {
111 + set: function () {
112 + // We use a throwing setter instead of frozen or non-writable props
113 + // because that won't throw in a non-strict mode function.
114 + throw Error();
115 + },
116 + });
117 + if (typeof Reflect === 'object' && Reflect.construct) {
118 + // We construct a different control for this case to include any extra
119 + // frames added by the construct call.
120 + try {
121 + Reflect.construct(Fake, []);
122 + } catch (x) {
123 + control = x;
124 + }
125 + Reflect.construct(fn, [], Fake);
126 + } else {
127 + try {
128 + Fake.call();
129 + } catch (x) {
130 + control = x;
131 + }
132 + // $FlowFixMe[prop-missing] found when upgrading Flow
133 + fn.call(Fake.prototype);
134 }
125 - Reflect.construct(fn, [], Fake);
135 } else {
136 try {
128 - Fake.call();
137 + throw Error();
138 } catch (x) {
139 control = x;
140 }
132 - // $FlowFixMe[prop-missing] found when upgrading Flow
133 - fn.call(Fake.prototype);
134 - }
135 - } else {
136 - try {
137 - throw Error();
138 - } catch (x) {
139 - control = x;
140 - }
141 - // TODO(luna): This will currently only throw if the function component
142 - // tries to access React/ReactDOM/props. We should probably make this throw
143 - // in simple components too
144 - const maybePromise = fn();
141 + // TODO(luna): This will currently only throw if the function component
142 + // tries to access React/ReactDOM/props. We should probably make this throw
143 + // in simple components too
144 + const maybePromise = fn();
145
146 - // If the function component returns a promise, it's likely an async
147 - // component, which we don't yet support. Attach a noop catch handler to
148 - // silence the error.
149 - // TODO: Implement component stacks for async client components?
150 - if (maybePromise && typeof maybePromise.catch === 'function') {
151 - maybePromise.catch(() => {});
146 + // If the function component returns a promise, it's likely an async
147 + // component, which we don't yet support. Attach a noop catch handler to
148 + // silence the error.
149 + // TODO: Implement component stacks for async client components?
150 + if (maybePromise && typeof maybePromise.catch === 'function') {
151 + maybePromise.catch(() => {});
152 + }
153 + }
154 + } catch (sample) {
155 + // This is inlined manually because closure doesn't do it for us.
156 + if (sample && control && typeof sample.stack === 'string') {
157 + return [sample.stack, control.stack];
158 }
159 }
154 - } catch (sample) {
155 - // This is inlined manually because closure doesn't do it for us.
156 - if (sample && control && typeof sample.stack === 'string') {
157 - return [sample.stack, control.stack];
158 - }
159 - }
160 - return [null, null];
161 - },
162 - };
163 - // $FlowFixMe[prop-missing]
164 - RunInRootFrame.DetermineComponentFrameRoot.displayName =
165 - 'DetermineComponentFrameRoot';
166 - const namePropDescriptor = Object.getOwnPropertyDescriptor(
167 - RunInRootFrame.DetermineComponentFrameRoot,
168 - 'name',
169 - );
170 - // Before ES6, the `name` property was not configurable.
171 - if (namePropDescriptor && namePropDescriptor.configurable) {
172 - // V8 utilizes a function's `name` property when generating a stack trace.
173 - Object.defineProperty(
160 + return [null, null];
161 + },
162 + };
163 + // $FlowFixMe[prop-missing]
164 + RunInRootFrame.DetermineComponentFrameRoot.displayName =
165 + 'DetermineComponentFrameRoot';
166 + const namePropDescriptor = Object.getOwnPropertyDescriptor(
167 RunInRootFrame.DetermineComponentFrameRoot,
175 - // Configurable properties can be updated even if its writable descriptor
176 - // is set to `false`.
177 - // $FlowFixMe[cannot-write]
168 'name',
179 - {value: 'DetermineComponentFrameRoot'},
169 );
181 - }
170 + // Before ES6, the `name` property was not configurable.
171 + if (namePropDescriptor && namePropDescriptor.configurable) {
172 + // V8 utilizes a function's `name` property when generating a stack trace.
173 + Object.defineProperty(
174 + RunInRootFrame.DetermineComponentFrameRoot,
175 + // Configurable properties can be updated even if its writable descriptor
176 + // is set to `false`.
177 + // $FlowFixMe[cannot-write]
178 + 'name',
179 + {value: 'DetermineComponentFrameRoot'},
180 + );
181 + }
182
183 - try {
183 const [sampleStack, controlStack] =
184 RunInRootFrame.DetermineComponentFrameRoot();
185 if (sampleStack && controlStack) {
packages/react-reconciler/src/ReactFiberHooks.js
+21 -9
@@ -1299,8 +1299,11 @@ function mountReducer<S, I, A>(
1299 initialState = init(initialArg);
1300 if (shouldDoubleInvokeUserFnsInHooksDEV) {
1301 setIsStrictModeForDevtools(true);
1302 - init(initialArg);
1303 - setIsStrictModeForDevtools(false);
1302 + try {
1303 + init(initialArg);
1304 + } finally {
1305 + setIsStrictModeForDevtools(false);
1306 + }
1307 }
1308 } else {
1309 initialState = ((initialArg: any): S);
@@ -1900,9 +1903,12 @@ function mountStateImpl<S>(initialState: (() => S) | S): Hook {
1903 initialState = initialStateInitializer();
1904 if (shouldDoubleInvokeUserFnsInHooksDEV) {
1905 setIsStrictModeForDevtools(true);
1903 - // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
1904 - initialStateInitializer();
1905 - setIsStrictModeForDevtools(false);
1906 + try {
1907 + // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
1908 + initialStateInitializer();
1909 + } finally {
1910 + setIsStrictModeForDevtools(false);
1911 + }
1912 }
1913 }
1914 hook.memoizedState = hook.baseState = initialState;
@@ -2856,8 +2862,11 @@ function mountMemo<T>(
2862 const nextValue = nextCreate();
2863 if (shouldDoubleInvokeUserFnsInHooksDEV) {
2864 setIsStrictModeForDevtools(true);
2859 - nextCreate();
2860 - setIsStrictModeForDevtools(false);
2865 + try {
2866 + nextCreate();
2867 + } finally {
2868 + setIsStrictModeForDevtools(false);
2869 + }
2870 }
2871 hook.memoizedState = [nextValue, nextDeps];
2872 return nextValue;
@@ -2880,8 +2889,11 @@ function updateMemo<T>(
2889 const nextValue = nextCreate();
2890 if (shouldDoubleInvokeUserFnsInHooksDEV) {
2891 setIsStrictModeForDevtools(true);
2883 - nextCreate();
2884 - setIsStrictModeForDevtools(false);
2892 + try {
2893 + nextCreate();
2894 + } finally {
2895 + setIsStrictModeForDevtools(false);
2896 + }
2897 }
2898 hook.memoizedState = [nextValue, nextDeps];
2899 return nextValue;
packages/react-reconciler/src/ReactFiberWorkLoop.js
+11 -8
@@ -3987,15 +3987,18 @@ function doubleInvokeEffectsOnFiber(
3987 shouldDoubleInvokePassiveEffects: boolean = true,
3988 ) {
3989 setIsStrictModeForDevtools(true);
3990 - disappearLayoutEffects(fiber);
3991 - if (shouldDoubleInvokePassiveEffects) {
3992 - disconnectPassiveEffect(fiber);
3993 - }
3994 - reappearLayoutEffects(root, fiber.alternate, fiber, false);
3995 - if (shouldDoubleInvokePassiveEffects) {
3996 - reconnectPassiveEffects(root, fiber, NoLanes, null, false);
3990 + try {
3991 + disappearLayoutEffects(fiber);
3992 + if (shouldDoubleInvokePassiveEffects) {
3993 + disconnectPassiveEffect(fiber);
3994 + }
3995 + reappearLayoutEffects(root, fiber.alternate, fiber, false);
3996 + if (shouldDoubleInvokePassiveEffects) {
3997 + reconnectPassiveEffects(root, fiber, NoLanes, null, false);
3998 + }
3999 + } finally {
4000 + setIsStrictModeForDevtools(false);
4001 }
3998 - setIsStrictModeForDevtools(false);
4002 }
4003
4004 function doubleInvokeEffectsInDEVIfNecessary(
packages/shared/ReactComponentStackFrame.js
+82 -83
@@ -103,103 +103,102 @@ export function describeNativeComponentFrame(
103 ReactSharedInternals.H = null;
104 disableLogs();
105 }
106 -
107 - /**
108 - * Finding a common stack frame between sample and control errors can be
109 - * tricky given the different types and levels of stack trace truncation from
110 - * different JS VMs. So instead we'll attempt to control what that common
111 - * frame should be through this object method:
112 - * Having both the sample and control errors be in the function under the
113 - * `DescribeNativeComponentFrameRoot` property, + setting the `name` and
114 - * `displayName` properties of the function ensures that a stack
115 - * frame exists that has the method name `DescribeNativeComponentFrameRoot` in
116 - * it for both control and sample stacks.
117 - */
118 - const RunInRootFrame = {
119 - DetermineComponentFrameRoot(): [?string, ?string] {
120 - let control;
121 - try {
122 - // This should throw.
123 - if (construct) {
124 - // Something should be setting the props in the constructor.
125 - const Fake = function () {
126 - throw Error();
127 - };
128 - // $FlowFixMe[prop-missing]
129 - Object.defineProperty(Fake.prototype, 'props', {
130 - set: function () {
131 - // We use a throwing setter instead of frozen or non-writable props
132 - // because that won't throw in a non-strict mode function.
106 + try {
107 + /**
108 + * Finding a common stack frame between sample and control errors can be
109 + * tricky given the different types and levels of stack trace truncation from
110 + * different JS VMs. So instead we'll attempt to control what that common
111 + * frame should be through this object method:
112 + * Having both the sample and control errors be in the function under the
113 + * `DescribeNativeComponentFrameRoot` property, + setting the `name` and
114 + * `displayName` properties of the function ensures that a stack
115 + * frame exists that has the method name `DescribeNativeComponentFrameRoot` in
116 + * it for both control and sample stacks.
117 + */
118 + const RunInRootFrame = {
119 + DetermineComponentFrameRoot(): [?string, ?string] {
120 + let control;
121 + try {
122 + // This should throw.
123 + if (construct) {
124 + // Something should be setting the props in the constructor.
125 + const Fake = function () {
126 throw Error();
134 - },
135 - });
136 - if (typeof Reflect === 'object' && Reflect.construct) {
137 - // We construct a different control for this case to include any extra
138 - // frames added by the construct call.
139 - try {
140 - Reflect.construct(Fake, []);
141 - } catch (x) {
142 - control = x;
127 + };
128 + // $FlowFixMe[prop-missing]
129 + Object.defineProperty(Fake.prototype, 'props', {
130 + set: function () {
131 + // We use a throwing setter instead of frozen or non-writable props
132 + // because that won't throw in a non-strict mode function.
133 + throw Error();
134 + },
135 + });
136 + if (typeof Reflect === 'object' && Reflect.construct) {
137 + // We construct a different control for this case to include any extra
138 + // frames added by the construct call.
139 + try {
140 + Reflect.construct(Fake, []);
141 + } catch (x) {
142 + control = x;
143 + }
144 + Reflect.construct(fn, [], Fake);
145 + } else {
146 + try {
147 + Fake.call();
148 + } catch (x) {
149 + control = x;
150 + }
151 + // $FlowFixMe[prop-missing] found when upgrading Flow
152 + fn.call(Fake.prototype);
153 }
144 - Reflect.construct(fn, [], Fake);
154 } else {
155 try {
147 - Fake.call();
156 + throw Error();
157 } catch (x) {
158 control = x;
159 }
151 - // $FlowFixMe[prop-missing] found when upgrading Flow
152 - fn.call(Fake.prototype);
153 - }
154 - } else {
155 - try {
156 - throw Error();
157 - } catch (x) {
158 - control = x;
159 - }
160 - // TODO(luna): This will currently only throw if the function component
161 - // tries to access React/ReactDOM/props. We should probably make this throw
162 - // in simple components too
163 - const maybePromise = fn();
160 + // TODO(luna): This will currently only throw if the function component
161 + // tries to access React/ReactDOM/props. We should probably make this throw
162 + // in simple components too
163 + const maybePromise = fn();
164
165 - // If the function component returns a promise, it's likely an async
166 - // component, which we don't yet support. Attach a noop catch handler to
167 - // silence the error.
168 - // TODO: Implement component stacks for async client components?
169 - if (maybePromise && typeof maybePromise.catch === 'function') {
170 - maybePromise.catch(() => {});
165 + // If the function component returns a promise, it's likely an async
166 + // component, which we don't yet support. Attach a noop catch handler to
167 + // silence the error.
168 + // TODO: Implement component stacks for async client components?
169 + if (maybePromise && typeof maybePromise.catch === 'function') {
170 + maybePromise.catch(() => {});
171 + }
172 + }
173 + } catch (sample) {
174 + // This is inlined manually because closure doesn't do it for us.
175 + if (sample && control && typeof sample.stack === 'string') {
176 + return [sample.stack, control.stack];
177 }
178 }
173 - } catch (sample) {
174 - // This is inlined manually because closure doesn't do it for us.
175 - if (sample && control && typeof sample.stack === 'string') {
176 - return [sample.stack, control.stack];
177 - }
178 - }
179 - return [null, null];
180 - },
181 - };
182 - // $FlowFixMe[prop-missing]
183 - RunInRootFrame.DetermineComponentFrameRoot.displayName =
184 - 'DetermineComponentFrameRoot';
185 - const namePropDescriptor = Object.getOwnPropertyDescriptor(
186 - RunInRootFrame.DetermineComponentFrameRoot,
187 - 'name',
188 - );
189 - // Before ES6, the `name` property was not configurable.
190 - if (namePropDescriptor && namePropDescriptor.configurable) {
191 - // V8 utilizes a function's `name` property when generating a stack trace.
192 - Object.defineProperty(
179 + return [null, null];
180 + },
181 + };
182 + // $FlowFixMe[prop-missing]
183 + RunInRootFrame.DetermineComponentFrameRoot.displayName =
184 + 'DetermineComponentFrameRoot';
185 + const namePropDescriptor = Object.getOwnPropertyDescriptor(
186 RunInRootFrame.DetermineComponentFrameRoot,
194 - // Configurable properties can be updated even if its writable descriptor
195 - // is set to `false`.
196 - // $FlowFixMe[cannot-write]
187 'name',
198 - {value: 'DetermineComponentFrameRoot'},
188 );
200 - }
189 + // Before ES6, the `name` property was not configurable.
190 + if (namePropDescriptor && namePropDescriptor.configurable) {
191 + // V8 utilizes a function's `name` property when generating a stack trace.
192 + Object.defineProperty(
193 + RunInRootFrame.DetermineComponentFrameRoot,
194 + // Configurable properties can be updated even if its writable descriptor
195 + // is set to `false`.
196 + // $FlowFixMe[cannot-write]
197 + 'name',
198 + {value: 'DetermineComponentFrameRoot'},
199 + );
200 + }
201
202 - try {
202 const [sampleStack, controlStack] =
203 RunInRootFrame.DetermineComponentFrameRoot();
204 if (sampleStack && controlStack) {