Skip to content

Commit

Permalink
Ensure function arity is preserved after build (#31808)
Browse files Browse the repository at this point in the history
Co-authored-by: eps1lon <[email protected]>
  • Loading branch information
davesnx and eps1lon authored Dec 18, 2024
1 parent 6a4b46c commit 2bd1c75
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 6 deletions.
12 changes: 8 additions & 4 deletions packages/react-dom/src/client/ReactDOMRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,17 +106,19 @@ ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render =
}

if (__DEV__) {
if (typeof arguments[1] === 'function') {
// using a reference to `arguments` bails out of GCC optimizations which affect function arity
const args = arguments;
if (typeof args[1] === 'function') {
console.error(
'does not support the second callback argument. ' +
'To execute a side effect after rendering, declare it in a component body with useEffect().',
);
} else if (isValidContainer(arguments[1])) {
} else if (isValidContainer(args[1])) {
console.error(
'You passed a container to the second argument of root.render(...). ' +
"You don't need to pass it again since you already passed it to create the root.",
);
} else if (typeof arguments[1] !== 'undefined') {
} else if (typeof args[1] !== 'undefined') {
console.error(
'You passed a second argument to root.render(...) but it only accepts ' +
'one argument.',
Expand All @@ -131,7 +133,9 @@ ReactDOMHydrationRoot.prototype.unmount = ReactDOMRoot.prototype.unmount =
// $FlowFixMe[missing-this-annot]
function (): void {
if (__DEV__) {
if (typeof arguments[0] === 'function') {
// using a reference to `arguments` bails out of GCC optimizations which affect function arity
const args = arguments;
if (typeof args[0] === 'function') {
console.error(
'does not support a callback argument. ' +
'To execute a side effect after rendering, declare it in a component body with useEffect().',
Expand Down
8 changes: 6 additions & 2 deletions packages/react-reconciler/src/ReactFiberHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3626,7 +3626,9 @@ function dispatchReducerAction<S, A>(
action: A,
): void {
if (__DEV__) {
if (typeof arguments[3] === 'function') {
// using a reference to `arguments` bails out of GCC optimizations which affect function arity
const args = arguments;
if (typeof args[3] === 'function') {
console.error(
"State updates from the useState() and useReducer() Hooks don't support the " +
'second callback argument. To execute a side effect after ' +
Expand Down Expand Up @@ -3666,7 +3668,9 @@ function dispatchSetState<S, A>(
action: A,
): void {
if (__DEV__) {
if (typeof arguments[3] === 'function') {
// using a reference to `arguments` bails out of GCC optimizations which affect function arity
const args = arguments;
if (typeof args[3] === 'function') {
console.error(
"State updates from the useState() and useReducer() Hooks don't support the " +
'second callback argument. To execute a side effect after ' +
Expand Down
44 changes: 44 additions & 0 deletions packages/react/src/__tests__/React-hooks-arity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/

'use strict';

let React;
let ReactNoop;

describe('arity', () => {
beforeEach(() => {
jest.resetModules();

React = require('react');
ReactNoop = require('react-noop-renderer');
});

it("ensure useState setter's arity is correct", () => {
function Component() {
const [, setState] = React.useState(() => 'Halo!');

expect(setState.length).toBe(1);
return null;
}

ReactNoop.render(<Component />);
});

it("ensure useReducer setter's arity is correct", () => {
function Component() {
const [, dispatch] = React.useReducer(() => 'Halo!');

expect(dispatch.length).toBe(1);
return null;
}

ReactNoop.render(<Component />);
});
});
11 changes: 11 additions & 0 deletions scripts/rollup/validate/eslintrc.cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ module.exports = {
rules: {
'no-undef': 'error',
'no-shadow-restricted-names': 'error',
'no-restricted-syntax': [
'error',
// TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
{
selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
message:
'Google Closure Compiler optimized `arguments` access. ' +
'This affects function arity. ' +
'Create a reference to `arguments` to avoid this optimization',
},
],
},

// These plugins aren't used, but eslint complains if an eslint-ignore comment
Expand Down
11 changes: 11 additions & 0 deletions scripts/rollup/validate/eslintrc.cjs2015.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ module.exports = {
rules: {
'no-undef': 'error',
'no-shadow-restricted-names': 'error',
'no-restricted-syntax': [
'error',
// TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
{
selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
message:
'Google Closure Compiler optimized `arguments` access. ' +
'This affects function arity. ' +
'Create a reference to `arguments` to avoid this optimization',
},
],
},

// These plugins aren't used, but eslint complains if an eslint-ignore comment
Expand Down
11 changes: 11 additions & 0 deletions scripts/rollup/validate/eslintrc.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ module.exports = {
rules: {
'no-undef': 'error',
'no-shadow-restricted-names': 'error',
'no-restricted-syntax': [
'error',
// TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
{
selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
message:
'Google Closure Compiler optimized `arguments` access. ' +
'This affects function arity. ' +
'Create a reference to `arguments` to avoid this optimization',
},
],
},

// These plugins aren't used, but eslint complains if an eslint-ignore comment
Expand Down
11 changes: 11 additions & 0 deletions scripts/rollup/validate/eslintrc.fb.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ module.exports = {
rules: {
'no-undef': 'error',
'no-shadow-restricted-names': 'error',
'no-restricted-syntax': [
'error',
// TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
{
selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
message:
'Google Closure Compiler optimized `arguments` access. ' +
'This affects function arity. ' +
'Create a reference to `arguments` to avoid this optimization',
},
],
},

// These plugins aren't used, but eslint complains if an eslint-ignore comment
Expand Down
11 changes: 11 additions & 0 deletions scripts/rollup/validate/eslintrc.rn.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ module.exports = {
rules: {
'no-undef': 'error',
'no-shadow-restricted-names': 'error',
'no-restricted-syntax': [
'error',
// TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
{
selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
message:
'Google Closure Compiler optimized `arguments` access. ' +
'This affects function arity. ' +
'Create a reference to `arguments` to avoid this optimization',
},
],
},

// These plugins aren't used, but eslint complains if an eslint-ignore comment
Expand Down

0 comments on commit 2bd1c75

Please sign in to comment.