Ensure function arity is preserved after build (#31808)
Co-authored-by: eps1lon <sebastian.silbermann@vercel.com>
David Sancho committed
Dec 18, 2024 at 14:08 UTC
2bd1c756c6fffefb00cdb2986218fa2701ece82e
8 files changed
+113
-6
packages/react-dom/src/client/ReactDOMRoot.js
+8
-4
@@ -106,17 +106,19 @@ ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render =
106
}
107
108
if (__DEV__) {
109
- if (typeof arguments[1] === 'function') {
109
+ // using a reference to `arguments` bails out of GCC optimizations which affect function arity
110
+ const args = arguments;
111
+ if (typeof args[1] === 'function') {
112
console.error(
113
'does not support the second callback argument. ' +
114
'To execute a side effect after rendering, declare it in a component body with useEffect().',
115
);
114
- } else if (isValidContainer(arguments[1])) {
116
+ } else if (isValidContainer(args[1])) {
117
console.error(
118
'You passed a container to the second argument of root.render(...). ' +
119
"You don't need to pass it again since you already passed it to create the root.",
120
);
119
- } else if (typeof arguments[1] !== 'undefined') {
121
+ } else if (typeof args[1] !== 'undefined') {
122
console.error(
123
'You passed a second argument to root.render(...) but it only accepts ' +
124
'one argument.',
@@ -131,7 +133,9 @@ ReactDOMHydrationRoot.prototype.unmount = ReactDOMRoot.prototype.unmount =
133
// $FlowFixMe[missing-this-annot]
134
function (): void {
135
if (__DEV__) {
134
- if (typeof arguments[0] === 'function') {
136
+ // using a reference to `arguments` bails out of GCC optimizations which affect function arity
137
+ const args = arguments;
138
+ if (typeof args[0] === 'function') {
139
console.error(
140
'does not support a callback argument. ' +
141
'To execute a side effect after rendering, declare it in a component body with useEffect().',
packages/react-reconciler/src/ReactFiberHooks.js
+6
-2
@@ -3626,7 +3626,9 @@ function dispatchReducerAction<S, A>(
3626
action: A,
3627
): void {
3628
if (__DEV__) {
3629
- if (typeof arguments[3] === 'function') {
3629
+ // using a reference to `arguments` bails out of GCC optimizations which affect function arity
3630
+ const args = arguments;
3631
+ if (typeof args[3] === 'function') {
3632
console.error(
3633
"State updates from the useState() and useReducer() Hooks don't support the " +
3634
'second callback argument. To execute a side effect after ' +
@@ -3666,7 +3668,9 @@ function dispatchSetState<S, A>(
3668
action: A,
3669
): void {
3670
if (__DEV__) {
3669
- if (typeof arguments[3] === 'function') {
3671
+ // using a reference to `arguments` bails out of GCC optimizations which affect function arity
3672
+ const args = arguments;
3673
+ if (typeof args[3] === 'function') {
3674
console.error(
3675
"State updates from the useState() and useReducer() Hooks don't support the " +
3676
'second callback argument. To execute a side effect after ' +
packages/react/src/__tests__/React-hooks-arity.js
new
+44
@@ -0,0 +1,44 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @emails react-core
8
+ */
9
+
10
+'use strict';
11
+
12
+let React;
13
+let ReactNoop;
14
+
15
+describe('arity', () => {
16
+ beforeEach(() => {
17
+ jest.resetModules();
18
+
19
+ React = require('react');
20
+ ReactNoop = require('react-noop-renderer');
21
+ });
22
+
23
+ it("ensure useState setter's arity is correct", () => {
24
+ function Component() {
25
+ const [, setState] = React.useState(() => 'Halo!');
26
+
27
+ expect(setState.length).toBe(1);
28
+ return null;
29
+ }
30
+
31
+ ReactNoop.render(<Component />);
32
+ });
33
+
34
+ it("ensure useReducer setter's arity is correct", () => {
35
+ function Component() {
36
+ const [, dispatch] = React.useReducer(() => 'Halo!');
37
+
38
+ expect(dispatch.length).toBe(1);
39
+ return null;
40
+ }
41
+
42
+ ReactNoop.render(<Component />);
43
+ });
44
+});
scripts/rollup/validate/eslintrc.cjs.js
+11
@@ -86,6 +86,17 @@ module.exports = {
86
rules: {
87
'no-undef': 'error',
88
'no-shadow-restricted-names': 'error',
89
+ 'no-restricted-syntax': [
90
+ 'error',
91
+ // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
92
+ {
93
+ selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
94
+ message:
95
+ 'Google Closure Compiler optimized `arguments` access. ' +
96
+ 'This affects function arity. ' +
97
+ 'Create a reference to `arguments` to avoid this optimization',
98
+ },
99
+ ],
100
},
101
102
// These plugins aren't used, but eslint complains if an eslint-ignore comment
scripts/rollup/validate/eslintrc.cjs2015.js
+11
@@ -81,6 +81,17 @@ module.exports = {
81
rules: {
82
'no-undef': 'error',
83
'no-shadow-restricted-names': 'error',
84
+ 'no-restricted-syntax': [
85
+ 'error',
86
+ // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
87
+ {
88
+ selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
89
+ message:
90
+ 'Google Closure Compiler optimized `arguments` access. ' +
91
+ 'This affects function arity. ' +
92
+ 'Create a reference to `arguments` to avoid this optimization',
93
+ },
94
+ ],
95
},
96
97
// These plugins aren't used, but eslint complains if an eslint-ignore comment
scripts/rollup/validate/eslintrc.esm.js
+11
@@ -83,6 +83,17 @@ module.exports = {
83
rules: {
84
'no-undef': 'error',
85
'no-shadow-restricted-names': 'error',
86
+ 'no-restricted-syntax': [
87
+ 'error',
88
+ // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
89
+ {
90
+ selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
91
+ message:
92
+ 'Google Closure Compiler optimized `arguments` access. ' +
93
+ 'This affects function arity. ' +
94
+ 'Create a reference to `arguments` to avoid this optimization',
95
+ },
96
+ ],
97
},
98
99
// These plugins aren't used, but eslint complains if an eslint-ignore comment
scripts/rollup/validate/eslintrc.fb.js
+11
@@ -71,6 +71,17 @@ module.exports = {
71
rules: {
72
'no-undef': 'error',
73
'no-shadow-restricted-names': 'error',
74
+ 'no-restricted-syntax': [
75
+ 'error',
76
+ // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
77
+ {
78
+ selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
79
+ message:
80
+ 'Google Closure Compiler optimized `arguments` access. ' +
81
+ 'This affects function arity. ' +
82
+ 'Create a reference to `arguments` to avoid this optimization',
83
+ },
84
+ ],
85
},
86
87
// These plugins aren't used, but eslint complains if an eslint-ignore comment
scripts/rollup/validate/eslintrc.rn.js
+11
@@ -73,6 +73,17 @@ module.exports = {
73
rules: {
74
'no-undef': 'error',
75
'no-shadow-restricted-names': 'error',
76
+ 'no-restricted-syntax': [
77
+ 'error',
78
+ // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
79
+ {
80
+ selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
81
+ message:
82
+ 'Google Closure Compiler optimized `arguments` access. ' +
83
+ 'This affects function arity. ' +
84
+ 'Create a reference to `arguments` to avoid this optimization',
85
+ },
86
+ ],
87
},
88
89
// These plugins aren't used, but eslint complains if an eslint-ignore comment