Commit Graph

312 Commits

Author SHA1 Message Date
mofeiZ
e30872a4e0 [compiler][be] Playground now compiles entire program (#31774)
Compiler playground now runs the entire program through
`babel-plugin-react-compiler` instead of a custom pipeline which
previously duplicated function inference logic from `Program.ts`. In
addition, the playground output reflects the tranformed file (instead of
a "virtual file" of manually concatenated functions).

This helps with the following:
- Reduce potential discrepencies between playground and babel plugin
behavior. See attached fixture output for an example where we previously
diverged.
- Let playground users see compiler-inserted imports (e.g. `_c` or
`useFire`)

This also helps us repurpose playground into a more general tool for
compiler-users instead of just for compiler engineers.
- imports and other functions are preserved.
We differentiate between imports and globals in many cases (e.g.
`inferEffectDeps`), so it may be misleading to omit imports in printed
output
- playground now shows other program-changing behavior like position of
outlined functions and hoisted declarations
- emitted compiled functions do not need synthetic names
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31774).
* #31809
* __->__ #31774
2024-12-16 14:43:21 -05:00
Joseph Savona
a1b3bd0da0 Optimize method calls w props receiver (#31775)
Redo of #31771 without ghstack
2024-12-13 17:10:07 -08:00
Jack Pope
16367ceb02 [compiler] Fix dropped ref with spread props in InlineJsxTransform (#31726)
When supporting ref as prop in
https://github.com/facebook/react/pull/31558, I missed fixing the
optimization to pass a spread-props-only props object in without an
additional object copy. In the case that we have only a ref along with a
spread, we cannot return only the spread object. This results in
dropping the ref.

In this example
```javascript
<Foo ref={ref} {...props} />
```

The bugged output is:
```javascript
{
  // ...
  props: props
}
```

With this change we now get the correct output:
```javascript
{
  // ...
  props: {ref: ref, ...props}
}
```
2024-12-10 16:11:17 -05:00
Mike Vitousek
76d603a72a [compiler] Support for non-declatation for in/of iterators
ghstack-source-id: a28801e022
Pull Request resolved: https://github.com/facebook/react/pull/31710
2024-12-09 12:04:00 -08:00
Mike Vitousek
226b85926a [compiler] Support for context variable loop iterators
Summary:
Fixing a compiler todo

ghstack-source-id: c4d9226b17
Pull Request resolved: https://github.com/facebook/react/pull/31709
2024-12-09 12:03:52 -08:00
Jordan Brown
6bcf0d20da [compiler] Empty dep arrays for globals/module-scoped values/imports (#31666)
Any LoadGlobal in the "infer deps" position can safely use an empty dep
array. Globals have no reactive deps!

I just keep messing up sapling. This is the revised version of #31662
2024-12-03 13:54:08 -05:00
Jordan Brown
1b1283ade7 [compiler] Support default imports for autodep config (#31657)
## Summary

Allows us to add deps for things like `import useWrapperEffect from
'useWrapperEffect'`

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31657).
* __->__ #31657
* #31652
2024-12-03 07:42:53 -05:00
Jordan Brown
2ab471c8d2 [compiler] Don't include current field accesses in auto-deps (#31652)
## Summary

Drops .current field accesses in inferred dep arrays

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31652).
* #31657
* __->__ #31652
2024-12-03 07:42:28 -05:00
mofeiZ
865d2c418d [compiler] Add meta internal option for useMemoCache import (#31654)
Adds `target: 'donotuse_meta_internal'`, which inserts useMemoCache
imports directly from `react`. Note that this is only valid for Meta
bundles, as others do not [re-export the `c`
function](5b0ef217ef/packages/react/index.fb.js (L68-L70)).

```js
// target=donotuse_meta_internal
import {c as _c} from 'react';

// target=19
import {c as _c} from 'react/compiler-runtime';

// target=17,18
import {c as _c} from 'react-compiler-runtime';
```

Meta is a bit special in that react runtime and compiler are guaranteed
to be up-to-date and compatible. It also has its own bundling and module
resolution logic, which makes importing from `react/compiler-runtime`
tricky.

I'm also fine with implementing the alternative which adds an internal
stub for `react-compiler-runtime` and
[bundles](5b0ef217ef/scripts/rollup/bundles.js (L120))
the runtime for internal builds.
2024-12-02 17:42:58 -05:00
Jordan Brown
2a9f4c04e5 [compiler] Infer deps configuration (#31616)
Adds a way to configure how we insert deps for experimental purposes.

```
[
  {
    module: 'react',
    imported: 'useEffect',
    numRequiredArgs: 1,
  },
  {
    module: 'MyExperimentalEffectHooks',
    imported: 'useExperimentalEffect',
    numRequiredArgs: 2,
  },
]
```

would insert dependencies for calls of `useEffect` imported from `react`
if they have 1 argument and calls of useExperimentalEffect` from
`MyExperimentalEffectHooks` if they have 2 arguments. The pushed dep
array is appended to the arg list.
2024-11-22 17:19:20 -05:00
Joseph Savona
eee5ca2a92 [compiler] Prune all unused array destructure items during DCE (#31619)
We didn't originally support holes within array patterns, so DCE was
only able to prune unused items from the end of an array pattern. Now
that we support holes we can replace any unused item with a hole, and
then just prune the items to the last identifier/spread entry.

Note: this was motivated by finding useState where either the state or
setState go unused — both are strong indications that you're violating
the rules in some way. By DCE-ing the unused portions of the useState
destructuring we can easily check if you're ignoring either value.

closes #31603 

This is a redo of that PR not using ghstack
2024-11-22 15:59:59 -05:00
Jordan Brown
e697386c10 [compiler] First cut at dep inference (#31386)
This is for researching/prototyping, not a feature we are releasing
imminently.

Putting up an early version of inferring effect dependencies to get
feedback on the approach. We do not plan to ship this as-is, and may not
start by going after direct `useEffect` calls. Until we make that
decision, the heuristic I use to detect when to insert effect deps will
suffice for testing.

The approach is simple: when we see a useEffect call with no dep array
we insert the deps inferred for the lambda passed in. If the first
argument is not a lambda then we do not do anything.

This diff is the easy part. I think the harder part will be ensuring
that we can infer the deps even when we have to bail out of memoization.
We have no other features that *must* run regardless of rules of react
violations. Does anyone foresee any issues using the compiler passes to
infer reactive deps when there may be violations?

I have a few questions:
1. Will there ever be more than one instruction in a block containing a
useEffect? if no, I can get rid of the`addedInstrs` variable that I use
to make sure I insert the effect deps array temp creation at the right
spot.
2. Are there any cases for resolving the first argument beyond just
looking at the lvalue's identifier id that I'll need to take into
account? e.g., do I need to recursively resolve certain bindings?

---------

Co-authored-by: Mofei Zhang <feifei0@meta.com>
2024-11-22 12:15:13 -05:00
lauren
d2e8954d06 [compiler] Update react deps (#31585)
```
=> Found "react@0.0.0-experimental-4beb1fd8-20241118"
info Reasons this module exists
   - "_project_#babel-plugin-react-compiler" depends on it
   - Hoisted from "_project_#babel-plugin-react-compiler#react"
   - Hoisted from "_project_#snap#react"
info Disk size without dependencies: "252KB"
info Disk size with unique dependencies: "252KB"
info Disk size with transitive dependencies: "252KB"
info Number of shared dependencies: 0
  Done in 0.60s.
```

```
=> Found "react-dom@0.0.0-experimental-4beb1fd8-20241118"
info Reasons this module exists
   - "_project_#babel-plugin-react-compiler" depends on it
   - Hoisted from "_project_#babel-plugin-react-compiler#react-dom"
   - Hoisted from "_project_#snap#react-dom"
info Disk size without dependencies: "8.04MB"
info Disk size with unique dependencies: "8.17MB"
info Disk size with transitive dependencies: "8.17MB"
info Number of shared dependencies: 1
  Done in 0.56s.
```
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31585).
* #31586
* __->__ #31585
2024-11-19 10:52:38 -05:00
Aditya Subramanyam
579cc2a44c [playground] Add support for "use no memo" (#31561)
Fixes #31331

## Summary
There is a bug in
playground(https://github.com/facebook/react/issues/31331) which doesnt
support 'use memo' or 'use no memo' directives. Its misleading while
debugging components in the playground

## How did you test this change?
Ran test cases and added a few extra test cases as well

## Changes
1) Adds support for 'use memo' and 'use no memo'
2) Cleanup E2E test cases a bit
3) Adds test cases for use memo
4) Added documentation to run test cases

## Implementation
`parseFunctions` returns a set of functions to be compiled. But, it
doesnt filter out/handle memoized opted/un-opted functions using
directives.

ive just created a `compile` flag to enable/disable compiling
[here](https://github.com/facebook/react/pull/31561/files#diff-305de47a3fe3ce778e22d5c5cf438419a59de8e7f785b45f659e7b41b1e30b03R113)

Then I am just skipping those functions from getting compile
[here](https://github.com/facebook/react/pull/31561/files#diff-305de47a3fe3ce778e22d5c5cf438419a59de8e7f785b45f659e7b41b1e30b03R253)
2024-11-18 15:38:22 -05:00
mofeiZ
e33b13795d [compiler] repro for type inference + control flow bug (#31570)
Repro for bug in our type inference system.

We currently propagate inferred types through control flow / potential
type guards. Note that this is inconsistent with both
[Flow](https://flow.org/try/#1N4Igxg9gdgZglgcxALlAIwIZoKYBsD6uEEAztvhgE6UYCe+JADpdhgCYowa5kA0I2KAFcAtiRQAXSkOz9sADwxgJ+NPTbYuQ3BMnTZA+Y2yU4IwRO4A6SFBIrGVDGM7c+h46fNRLuKxJIGWh8MeT0ZfhYlCStpHzNsFBAMIQkIEQwJODAQfiEyfBE4eWw2fDgofDBMsAALfAA3KjgsXGxxZC4eAw0G-GhcWn9aY3wWZldu-g1mbGqJUoBaCRHEzrcDEgBrbAk62kXhXFxJ923d-cPRHEpTgyEoMDaqZdW7vKgoOfaSKgOKpqmDA+d4gB5fMA-P6LCCMLLQbiLOoYCqgh6-GDYRYIXYLSgkRZkCR4jpddwPfJLZjpOBkO4AX34kA0SRWxgABAAxYjsgC87OAAB0oOzReythU2Mh2YKQNyILLeMKxeymrgZNLhCIbsL6QBuYVs7DsgBCVD5AuVYolUClMpAZsoiqtorVGvZWpuSqg9OFMAeyjg0HZdTmW3lAAp5NKAPJoABWcwkAEppWZGLg4O12fJ2bSuTyhSKxSwJEJKCKAOQ2tiVvMi3MAMkbOasNb5vP5svlsoNPuFfoD8JFGQqUel8vZAB9TVReCHoHa0MRnlBUwWIJbi6K4DB2RHbGxk1uVSrd-uAIShsDh4hR5PHoun5-siS1SgQADuHuw34AotQECUBGsqysmfYvuyvrbqepblg2EFitBKpwRWOZ9vSuQgA0JgkEGUBJBk9gmCA9JAA)
and
[Typescript](https://www.typescriptlang.org/play/?#code/C4TwDgpgBAYg9nKBeKBvAUFLUDWBLAOwBMAuKAInjnIBpNsA3AQwBsBXCMgtgWwCMIAJ3QBfANzpQkKACEmg5GnpZ8xMuTmDayqM3aco3fkLoj0AMzYEAxsDxwCUawAsI1nFQAUADzJw+AFZuwACUZEwAzhFCwBFQ3lB4cVRK2InmUJ4AhJ4A5KpEuYmOCQBkpfEAdAXISCiUCOQhIalp2MDOgnAA7oYQvQCigl2CnuRWEN6QthBETTpmZhZWtvaOPEyEPmQpAD6y8jRODqRQfAgsEEwEYbAIrVh4GZ7WJy0Ybdgubh4IPiEST5YQQQYBsQQlQHYMxpEFgiHxCQiIA)
2024-11-18 11:54:11 -05:00
Jack Pope
4beb1fd8ba [compiler] Support enableRefAsProp in jsx transform (#31558)
Since `enableRefAsProp` shipped everywhere, the ReactElement
implementation on prod puts refs on both `element.ref` and
`element.props.ref`. Here we let the `ref` case fall through so its now
available on props, matching the JSX runtime.
2024-11-18 10:51:16 -05:00
mofeiZ
0480cdb58c [compiler][be] Clean up nested function context in DCE (#31202)
Now that we rely on function context exclusively, let's clean up
`HIRFunction.context` after DCE. This PR is in preparation of #31204,
which would otherwise have unnecessary declarations (of context values
that become entirely DCE'd)

'
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31202).
* __->__ #31202
* #31203
* #31201
* #31200
* #31521
2024-11-15 13:06:39 -05:00
mofeiZ
0f3c62b466 [compiler][be] Patch test fixtures for evaluator (#31203)
Add more `FIXTURE_ENTRYPOINT`s

'
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31203).
* #31202
* __->__ #31203
* #31201
* #31200
* #31521
2024-11-15 13:06:29 -05:00
mofeiZ
858633f900 [compiler] Lower JSXMemberExpression with LoadLocal (#31201)
`JSXMemberExpression` is currently the only instruction (that I know of)
that directly references identifier lvalues without a corresponding
`LoadLocal`.

This has some side effects:
- deadcode elimination and constant propagation now reach
JSXMemberExpressions
- we can delete `LoweredFunction.dependencies` without dangling
references (previously, the only reference to JSXMemberExpression
objects in HIR was in function dependencies)
- JSXMemberExpression now is consistent with all other instructions
(e.g. has a rvalue-producing LoadLocal)

'
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31201).
* #31202
* #31203
* __->__ #31201
* #31200
* #31521
2024-11-15 13:06:19 -05:00
mofeiZ
c09402aa2f [compiler] Stop using function dependencies in propagateScopeDeps (#31200)
Recursively visit inner function instructions to extract dependencies
instead of using `LoweredFunction.dependencies` directly.

This is currently gated by enableFunctionDependencyRewrite, which needs
to be removed before we delete `LoweredFunction.dependencies` altogether
(#31204).

Some nice side effects
- optional-chaining deps for inner functions
- full DCE and outlining for inner functions (see #31202)
- fewer extraneous instructions (see #31204)

-
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31200).
* #31202
* #31203
* #31201
* __->__ #31200
* #31521
2024-11-15 13:06:05 -05:00
mofeiZ
4972718c26 [compiler] Fix: ref.current now correctly reactive (#31521)
We were previously filtering out `ref.current` dependencies in
propagateScopeDependencies:checkValidDependency`. This is incorrect.

Instead, we now always take a dependency on ref values (the outer box)
as they may be reactive. Pruning is done in
pruneNonReactiveDependencies.

This PR includes a small patch to `collectReactiveIdentifier`. Prior to
this, we conservatively assumed that pruned scopes always produced
reactive declarations. This assumption fixed a bug with non-reactivity,
but some of these declarations are `useRef` calls. Now we have special
handling for this case
```js
// This often produces a pruned scope
React.useRef(1);
```
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31521).
* #31202
* #31203
* #31201
* #31200
* __->__ #31521
2024-11-15 13:05:55 -05:00
Niklas Mollenhauer
3c15d219aa [compiler] Disable emit of .tsbuildinfo (#31459)
## Summary
`@rollup/plugin-typescript` emits a warning while building, hinting that
`outputToFilesystem` defaults to true.

Although "noEmit" is set to `true` for the tsconfig, rollup writes a
`dist/.tsbuildinfo`. That file is then also shipped inside the npm
module and doesn't offer any benefit for library consumers. Setting this
option to false results in the file not being written and thus omitted
from the npm module.

## How did you test this change?
`dist/.tsbuildinfo` is not emitted any more.
2024-11-14 15:43:36 -05:00
mofeiZ
3770c11011 [compiler] repro for reactive ref.current accesses (#31519)
See test fixture
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31519).
* #31521
* __->__ #31519
2024-11-12 14:04:54 -05:00
mofeiZ
2ec26bc432 [compiler] Repro for mutable range edge case (#31479)
See test fixtures
2024-11-11 18:04:29 -05:00
Sathya Gunasekaran
a88b9e5f68 [compiler] Outline JSX with non-jsx children (#31442)
Previously, we bailed out on outlining jsx that had children that were
not part of the outlined jsx.

Now, we add support for children by treating as attributes.
2024-11-06 17:54:44 +00:00
Sathya Gunasekaran
09197bb786 [compiler] Outline jsx with duplicate attributes (#31441)
Previously, we would skip outlining jsx expressions that had duplicate
jsx attributes as we would not rename them causing incorrect
compilation.

In this PR, we add outlining support for duplicate jsx attributes by
renaming them.
2024-11-06 17:50:13 +00:00
Sathya Gunasekaran
2df8f61885 [compiler] Store original and new prop names (#31440)
Previously, we'd directly store the original attributes from the jsx
expressions. But this isn't enough as we want to rename duplicate
attributes.

This PR refactors the prop collection logic to store both the original
and new names for jsx attributes in the newly outlined jsx expression.

For now, both the new and old names are the same. In the future, they
will be different when we add support for outlining expressions with
duplicate attribute names.
2024-11-06 17:44:52 +00:00
mofeiZ
c3570b158d [compiler] Collect temporaries and optional chains from inner functions (#31346)
Recursively collect identifier / property loads and optional chains from
inner functions. This PR is in preparation for #31200

Previously, we only did this in `collectHoistablePropertyLoads` to
understand hoistable property loads from inner functions.
1. collectTemporariesSidemap
2. collectOptionalChainSidemap
3. collectHoistablePropertyLoads
- ^ this recursively calls `collectTemporariesSidemap`,
`collectOptionalChainSidemap`, and `collectOptionalChainSidemap` on
inner functions
4. collectDependencies

Now, we have
1. collectTemporariesSidemap
- recursively record identifiers in inner functions. Note that we track
all temporaries in the same map as `IdentifierIds` are currently unique
across functions
2. collectOptionalChainSidemap
    - recursively records optional chain sidemaps in inner functions
3. collectHoistablePropertyLoads
    - (unchanged, except to remove recursive collection of temporaries)
4. collectDependencies
- unchanged: to be modified to recursively collect dependencies in next
PR

'
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31346).
* #31202
* #31203
* #31201
* #31200
* __->__ #31346
* #31199
2024-11-05 19:25:05 -05:00
mofeiZ
fd018af617 [compiler] Delete propagateScopeDeps (non-hir) (#31199)
`enablePropagateScopeDepsHIR` is now used extensively in Meta. This has
been tested for over two weeks in our e2e tests and production.

The rest of this stack deletes `LoweredFunction.dependencies`, which the
non-hir version of `PropagateScopeDeps` depends on. To avoid a more
forked HIR (non-hir with dependencies and hir with no dependencies),
let's go ahead and clean up the non-hir version of
PropagateScopeDepsHIR.

Note that all fixture changes in this PR were previously reviewed when
they were copied to `propagate-scope-deps-hir-fork`. Will clean up /
merge these duplicate fixtures in a later PR

'
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31199).
* #31202
* #31203
* #31201
* #31200
* #31346
* __->__ #31199
2024-11-05 19:22:04 -05:00
mofeiZ
f2f002c7c1 [compiler][be] Stabilize compiler output: sort deps and decls by name (#31362)
All dependencies and declarations of a reactive scope can be reordered
to scope start/end. i.e. generated code does not depend on conditional
short-circuiting logic as dependencies are inferred to have no side
effects.

Sorting these by name helps us get higher signal compilation snapshot
diffs when upgrading the compiler and testing PRs
2024-11-05 18:26:50 -05:00
mofeiZ
792fa065ca [compiler][ez] Clean up pragma parsing for tests + playground (#31347)
Move environment config parsing for `inlineJsxTransform`,
`lowerContextAccess`, and some dev-only options out of snap (test
fixture). These should now be available for playground via
`@inlineJsxTransform` and `lowerContextAccess`.

Other small change:
Changed zod fields from `nullish()` -> `nullable().default(null)`.
[`nullish`](https://zod.dev/?id=nullish) fields accept `null |
undefined` and default to `undefined`. We don't distinguish between null
and undefined for any of these options, so let's only accept null +
default to null. This also makes EnvironmentConfig in the playground
more accurate. Previously, some fields just didn't show up as
`prettyFormat({field: undefined})` does not print `field`.
2024-11-05 18:19:44 -05:00
mofeiZ
33195602ea [compiler][ez] tsconfig: treat all snap fixtures as modules (#31350)
Qol improvement. Currently, typescript lints treat test fixtures without
an export as a 'global script' (see
[docs](https://www.typescriptlang.org/docs/handbook/2/modules.html#how-javascript-modules-are-defined)).
This gives confusing lints for duplicate declarations (in the global
scope)
2024-11-05 17:57:18 -05:00
mofeiZ
5ca2bc6d63 [compiler][ez] Fixture repro for function hoisting bug (#31349)
Repro for bug reported by @alexmckenley
2024-11-05 17:56:53 -05:00
mofeiZ
bddb7c9b5c [compiler] Add fixture for objectexpr computed key bug (#31348)
We were bailing out on complex computed-key syntax (prior to #31344) as
we assumed that this caused bugs (due to inferring computed key rvalues
to have `freeze` effects).

This fixture shows that this bailout is unrelated to the underlying bug
2024-11-05 17:56:36 -05:00
mofeiZ
527bcaa83d [compiler] patch: rewrite scope dep/decl in inlineJsxTransform (#31431)
This bugfix is needed to land #31199 PropagateScopeDepsHIR infers scope
declarations for the `inline-jsx-transform` test fixture (the non-hir
version does not).

These declarations must get the rewritten phi identifiers
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31431).
* #31204
* #31202
* #31203
* #31201
* #31200
* #31346
* #31199
* __->__ #31431
* #31345
* #31197
2024-11-05 15:27:39 -05:00
mofeiZ
e7e269b726 [compiler] bugfix for hoistable deps for nested functions (#31345)
`PropertyPathRegistry` is responsible for uniqueing identifier and
property paths. This is necessary for the hoistability CFG merging logic
which takes unions and intersections of these nodes to determine a basic
block's hoistable reads, as a function of its neighbors. We also depend
on this to merge optional chained and non-optional chained property
paths

This fixes a small bug in #31066 in which we create a new registry for
nested functions. Now, we use the same registry for a component / hook
and all its inner functions

'
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31345).
* #31204
* #31202
* #31203
* #31201
* #31200
* #31346
* #31199
* #31431
* __->__ #31345
* #31197
2024-11-05 15:25:54 -05:00
mofeiZ
dd1a021bad [compiler][ez] Patch hoistability for ObjectMethods (#31197)
Extends #31066 to ObjectMethods (somehow missed this before).

'
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31197).
* #31204
* #31202
* #31203
* #31201
* #31200
* #31346
* #31199
* #31431
* #31345
* __->__ #31197
2024-11-05 15:25:39 -05:00
Jack Pope
543eb09321 [compiler] Wrap inline jsx transform codegen in conditional (#31267)
JSX inlining is a prod-only optimization. We want to enforce this while
maintaining the same compiler output in DEV and PROD.

Here we add a conditional to the transform that only replaces JSX with
object literals outside of DEV. Then a later build step can handle DCE
based on the value of `__DEV__`
2024-11-04 13:19:05 -05:00
lauren
603e6108f3 [compiler] Update react deps to experimental instead of beta (#31385)
Some tests rely on experimental APIs so let's just use
react@experimental instead of beta
2024-10-29 21:51:41 -04:00
lauren
4abe4b5821 [compiler] Check if local identifier is a hook when resolving globals (#31384)
When resolving import specifiers from the react namespace (`import
{imported as local} from 'react'`), we were previously only checking if
the `imported` identifier was a hook if we didn't already have its
definition in the global registry. We also need to check if `local` is a
hook in the case of aliasing since there may be hook-like APIs in react
that don't start with `use` (eg they are experimental or unstable).

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31384).
* #31385
* __->__ #31384
* #31383
2024-10-29 21:36:48 -04:00
lauren
3928cb00db [compiler] Ref validation repro for ImportSpecifier with renamed local (#31383)
This was originally reported in
https://github.com/reactwg/react-compiler/discussions/27.

Adding a failing repro to capture this case.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31383).
* #31385
* #31384
* __->__ #31383
2024-10-29 21:36:36 -04:00
Sathya Gunasekaran
02c0e824e4 [compiler][ez] Remove unused param (#31376) 2024-10-28 15:08:27 +00:00
Sathya Gunasekaran
aded0ef831 [compiler] Handle member expr as computed property (#31344)
This PR loosens the restriction on the types of computed properties we
can handle.

Previously, we would disallow anything that is not an identifier because
non-identifiers could be mutating. But member expressions are not
mutating so we can treat them similar to identifiers.
2024-10-28 13:10:01 +00:00
Mike Vitousek
fe04dbcbc4 [compiler] Fix to ref access check to ban ref?.current
ghstack-source-id: ea417a468e
Pull Request resolved: https://github.com/facebook/react/pull/31360
2024-10-25 16:51:36 -07:00
Simen Bekkhus
ae90522bc6 chore: remove unsued deps from React Compiler Babel plugin (#31315)
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

Since the Babel plugin is bundled into a single file (except for
`@babel/types`
45804af18d/compiler/packages/babel-plugin-react-compiler/rollup.config.js (L18))
we can move these deps to `devDependencies`.

Main motivation is e.g. not installing ancient version of
`pretty-format` (asked in https://github.com/facebook/react/issues/29062
without getting a reason, but if consumers can just skip the deps
entirely that's even better).

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

## How did you test this change?

I tested by installing the plugin into an empty project, deleting
everything in `node_modules` _except_ for `babel-plugin-react-compiler`
and doing `require('babel-plugin-react-compiler')`. It still worked
fine, so it should work in other cases as well 😀

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->
2024-10-22 13:51:58 -04:00
Joseph Savona
cdde15efe1 [compiler] InlineJSXTransform transforms jsx inside function expressions (#31282)
InlineJSXTransform wasn't traversing into function expressions or object
methods, so any JSX inside such functions wouldn't have gotten inlined.
This PR updates to traverse nested functions to transform all JSX within
a hook or component.

Note that this still doesn't transform JSX outside of components or
hooks, ie in standalone render helpers.
2024-10-18 11:27:48 -07:00
Sathya Gunasekaran
c91b3b090a JSX Outlining (#30956)
Currently, the react compiler can not compile within callbacks which can
potentially cause over rendering. Consider this example:
```jsx
function Component(countries, onDelete) {
  const name = useFoo();
  return countries.map(() => {
    return (
      <Foo>
        <Bar name={name}/>
        <Baz onclick={onDelete} />
      </Foo>
    );
  });
}
```

In this case, there's no memoization of the nested jsx elements. But
instead if we were to manually refactor the nested jsx into separate
component like this:
```jsx
function Component(countries, onDelete) {
  const name = useFoo();
  return countries.map(() => {
    return <Temp name={name} onDelete={onDelete} />;
  });
}

function Temp({ name, onDelete }) {
  return (
    <Foo>
      <Bar name={name} />
      <Baz onclick={onDelete} />
    </Foo>
  );
}

```

The compiler can now optimise both these components:
```jsx
function Component(countries, onDelete) {
  const $ = _c(4);
  const name = useFoo();
  let t0;
  if ($[0] !== name || $[1] !== onDelete || $[2] !== countries) {
    t0 = countries.map(() => <Temp name={name} onDelete={onDelete} />);
    $[0] = name;
    $[1] = onDelete;
    $[2] = countries;
    $[3] = t0;
  } else {
    t0 = $[3];
  }
  return t0;
}

function Temp(t0) {
  const $ = _c(7);
  const { name, onDelete } = t0;
  let t1;
  if ($[0] !== name) {
    t1 = <Bar name={name} />;
    $[0] = name;
    $[1] = t1;
  } else {
    t1 = $[1];
  }
  let t2;
  if ($[2] !== onDelete) {
    t2 = <Baz onclick={onDelete} />;
    $[2] = onDelete;
    $[3] = t2;
  } else {
    t2 = $[3];
  }
  let t3;
  if ($[4] !== t1 || $[5] !== t2) {
    t3 = (
      <Foo>
        {t1}
        {t2}
      </Foo>
    );
    $[4] = t1;
    $[5] = t2;
    $[6] = t3;
  } else {
    t3 = $[6];
  }
  return t3;
}
```

Now, when `countries` is updated by adding one single value, only the
newly added value is re-rendered and not the entire list. Rather than
having to do this manually, this PR teaches the react compiler to do
this transformation.

This PR adds a new pass (`OutlineJsx`) to capture nested jsx statements
and outline them in a separate component. This newly outlined component
can then by memoized by the compiler, giving us more fine grained
rendering.
2024-10-17 18:15:32 +01:00
Mike Vitousek
147374d71a [compiler] Kill markReactiveIdentifier and friends
Summary:
With the previous PR we no longer need to mark identifiers as reactive in contexts where we don't have places. We already deleted most uses of markReactiveId; the last case was to track identifiers through loadlocals etc -- but we already use a disjoint alias map that accounts for loadlocals when setting reactivity.

ghstack-source-id: 69ce0a78b0
Pull Request resolved: https://github.com/facebook/react/pull/31178
2024-10-11 17:26:57 -07:00
Mike Vitousek
6cf5bd9013 [compiler] Allow refs to be lazily initialized during render
Summary:
The official guidance for useRef notes an exception to the rule that refs cannot be accessed during render: to avoid recreating the ref's contents, you can test that the ref is uninitialized and then initialize it using an if statement:

```
if (ref.current == null) {
  ref.current = SomeExpensiveOperation()
}
```

The compiler didn't recognize this exception, however, leading to code that obeyed all the official guidance for refs being rejected by the compiler. This PR fixes that, by extending the ref validation machinery with an awareness of guard operations that allow lazy initialization. We now understand `== null` and similar operations, when applied to a ref and consumed by an if terminal, as marking the consequent of the if as a block in which the ref can be safely written to. In order to do so we need to create a notion of ref ids, which link different usages of the same ref via both the ref and the ref value.

ghstack-source-id: d2729274f3
Pull Request resolved: https://github.com/facebook/react/pull/31188
2024-10-11 16:14:33 -07:00
Mike Vitousek
7b7fac073d [compiler] Represent phis with places rather than identifiers
Summary:
The fact that phis are identifiers rather than places is unfortunate in a few cases. In some later analyses, we might wish to know whether a phi is reactive, but we don't have an easy way to do that currently.

Most of the changes here is just replacing phi.id with phi.place.identifier and such. Interesting bits are EnterSSA (several functions now take places rather than identifiers, and InferReactivePlaces now needs to mark places as reactive explicitly.

ghstack-source-id: 5f4fb396cd
Pull Request resolved: https://github.com/facebook/react/pull/31171
2024-10-10 12:41:03 -07:00