Commit Graph
19463 Commits
Author SHA1 Message Date
Sathya Gunasekaran 42c04fdcc7 [test] Add test to show Forget's inference is run inside lambda
JSX reads the value of `z` and does not mutate it so `z` can be independently 
memoized.
2023-03-08 00:16:00 +00:00
Sathya Gunasekaran 8695ea05c3 [test] Add test for capturing a ref before renaming
The renaming does not affect the lambda correctly.
2023-03-08 00:15:57 +00:00
Sathya Gunasekaran 80ab2f25a6 [test] Add test for capturing a renamed ref
The ref isn't renamed inside the lambda, needs to be fixed.
2023-03-08 00:15:54 +00:00
Sathya Gunasekaran d155d2dd81 [test] Add test for lambda that conditionally captures ref
Conditionally captured refs are correctly added to dependency list of the 
lambda.
2023-03-08 00:15:51 +00:00
Sathya Gunasekaran 1e8b6b51d3 [test] Add test for lambda that shadows ref
There is no ref captured as it is shadowed and the lambda does not depend on the 
ref.
2023-03-08 00:15:48 +00:00
Andrew Clark 161f6ae42c Codemod act -> await act (3/?) (#26336)
Similar to the rationale for `waitFor` (see #26285), we should always
await the result of an `act` call so that microtasks have a chance to
fire.

This only affects the internal `act` that we use in our repo, for now.
In the public `act` API, we don't yet require this; however, we
effectively will for any update that triggers suspense once `use` lands.
So we likely will start warning in an upcoming minor.
2023-03-07 14:39:30 -05:00
Andrew Clark 58605f7988 Codemod act -> await act (2/?) (#26335)
Similar to the rationale for `waitFor` (see #26285), we should always
await the result of an `act` call so that microtasks have a chance to
fire.

This only affects the internal `act` that we use in our repo, for now.
In the public `act` API, we don't yet require this; however, we
effectively will for any update that triggers suspense once `use` lands.
So we likely will start warning in an upcoming minor.
2023-03-07 12:07:30 -05:00
Andrew Clark 703c67560d Codemod act -> await act (1/?) (#26334)
Similar to the rationale for `waitFor` (see
https://github.com/facebook/react/pull/26285), we should always await
the result of an `act` call so that microtasks have a chance to fire.

This only affects the internal `act` that we use in our repo, for now.
In the public `act` API, we don't yet require this; however, we
effectively will for any update that triggers suspense once `use` lands.
So we likely will start warning in an upcoming minor.
2023-03-07 10:15:34 -05:00
Andrew Clark b380c24852 Convert class equivlance tests to flushSync (#26333)
There's an old collection of test suites that test class component
behavior across ES6 (regular JavaScript classes), CoffeeScript classes,
and TypeScript classes. They work by running the same tests in all
environments and comparing the results.

Rather than use `act` or `waitFor` in these, I've changed them to use
`flushSync` instead so that they can flush synchronously. The reason is
that CoffeeScript doesn't have async/await, so we'd have to write those
tests differently than how they are written in the corresponding
modules. Since none of these tests cover any concurrent behavior, I
believe it's fine in this case to do everything synchronously; they
don't use any concurrent features, anyway, so effectively it's just
skipping a microtask.
2023-03-07 10:14:41 -05:00
Rubén Norte 8f812e75d6 Refactor ReactFabricHostComponent (#26323)
## Summary

This is a small refactor of `ReactFabricHostComponent` to remove
unnecessary dependencies, unused methods and type definitions to
simplify a larger refactor of the class in a following PR
(https://github.com/facebook/react/pull/26321).

## How did you test this change?

Existing unit tests.
2023-03-07 14:33:55 +00:00
Josh Story 978fae4b4f [Float][Fiber] implement a faster hydration match for hoistable elements (#26154)
This PR is now based on #26256 

The original matching function for `hydrateHoistable` some challenging
time complexity since we built up the list of matchable nodes for each
link of that type and then had to check to exclusion. This new
implementation aims to improve the complexity

For hoisted title tags we match the first title if it is valid (not in
SVG context and does not have `itemprop`, the two ways you opt out of
hoisting when rendering titles). This path is much faster than others
and we use it because valid Documents only have 1 title anyway and if we
did have a mismatch the rendered title still ends up as the
Document.title so there is no functional degradation for misses.

For hoisted link and meta tags we track all potentially hydratable
Elements of this type in a cache per Document. The cache is refreshed
once each commit if and only if there is a title or meta hoistable
hydrating. The caches are partitioned by a natural key for each type
(href for link and content for meta). Then secondary attributes are
checked to see if the potential match is matchable.

For link we check `rel`, `title`, and `crossorigin`. These should
provide enough entropy that we never have collisions except is contrived
cases and even then it should not affect functionality of the page. This
should also be tolerant of links being injected in arbitrary places in
the Document by 3rd party scripts and browser extensions

For meta we check `name`, `property`, `http-equiv`, and `charset`. These
should provide enough entropy that we don't have meaningful collisions.
It is concievable with og tags that there may be true duplciates `<meta
property="og:image:size:height" content="100" />` but even if we did
bind to the wrong instance meta tags are typically only read from SSR by
bots and rarely inserted by 3rd parties so an adverse functional outcome
is not expected.
2023-03-06 19:52:35 -08:00
Joe Savona 1c023263a3 Use shorthand where possible for ObjectExpression properties
During codegen, emit object properties as shorthand where possible (`{x}` 
instead of `{x: x}`)
2023-03-06 16:35:52 -08:00
Joe Savona e381aa042f Support spread elements in ArrayExpression
Similar to the previous, but for array expression: `const x = [...y]`
2023-03-06 15:32:43 -08:00
Joe Savona 93cca54aba Support spread patterns in object literals
Support ObjectExpression with spread items, eg `const x = {...y}`.
2023-03-06 15:24:43 -08:00
Joe Savona 7893a6c403 Represent ObjectExpression properties as Array<ObjectProperty>
Refactors the representation of ObjectExpression properties from a Map to an 
`Array<ObjectProperty>` to prepare for the next diff which adds spread element 
support.
2023-03-06 15:19:34 -08:00
Josh Story 8a9f82ed58 [Float][Fizz][Fiber] - Do not hoist elements with itemProp & hydrate more tolerantly in hoist contexts (#26256)
## Do not hoist elements with `itemProp`
In HTML `itemprop` signifies a property of an `itemscope` with respect
to the Microdata spec
(https://html.spec.whatwg.org/multipage/microdata.html#microdata)

additionally `itemprop` is valid on any tag and can even make some tags
that are otherwise invalid in the `<body>` valid there (`<meta>` for
instance).

Originally I tried an approach where if you rendered something otherwise
hoistable inside an `itemscope` it would not hoist if it had an
`itemprop`. This meant that some components with `itemprop` could hoist
(if they were not scoped, which is generally invalid microdata
implementation). However the problem is things that do hoist, hoist into
the head and body and these tags can have an `itemscope`. This creates a
ton of ambiguity when trying to hydrate in these hoist scopes because we
can't know for certain whether a DOM node we find there was hoisted or
not even if it has an `itemprop` attribute. There are other scenarios
too that have abiguous semantics like rendering a hoistable with
`itemProp` outside of `<html itemScope={true>`. Is it fair to embed that
hoistable inside that itemScope even though it was defined outside?

To simplify the situation and disambiguate I dropped the `itemscope`
portion from the implementation and now any host component that could
normally be hoisted will not hoist if it has an `itemProp` prop.

In addition to the changes made for `itemProp` this PR also modifies
part of the hydration implementation to be more tolerant of tags
injected by 3rd parties. This was opportunistically done when we needed
to have context information like `inItemScope` but with the most recent
implementation that has been removed. I have however left the hydration
changes in place as it is a goal to make React handle hydrating the
entire Document even when we cannot control whether 3rd parties are
going to inject tags that React will not render but are also not
hoistables

-------

##### Original Description when we considered tracking itemScope
>One recent decision was to make elements using the `itemProp` prop not
hoistable if they were inside and itemScope. This better fits with
Microdata spec which allows for meta tags and other tag types usually
reserved for the `<head>` to be used in the `<body>` when using
itemScope.
>
>To implement this a number of small changes were necessary
>
>1. HostContext in prod needed to expand beyond just tracking the
element namespace for new element creation. It now tracks whether we are
in an itemScope. To keep this efficient it is modeled as a bitmask.
>2. To disambiguate what is and is not a potential instance in the DOM
for hoistables the hydration algo was updated to skip past non-matching
instances while attempting to claim the instance rather than ahead of
time (getNextHydratable).
>3. React will not consider an itemScope on `<html>`, `<head>`, or
`<body>` as a valid scope for the hoisting opt-out. This is important as
an invariant so we can make assumptions about certain tags in these
scopes. This should not be a functional breaking change because if any
of these tags have an `itemScope` then it can just be moved into the
first node inside the `<body>`
>
>Since we were already updating the logic for hydration to better
support `itemScope` opt-out I also changed the hydration behavior for
suspected 3rd party nodes in `<head>` and `<body>`. Now if you are
hydrating in either of those contexts hydration will skip past any
non-matching nodes until it finds a match. This allows 3rd party scripts
and extensions to inject nodes in either context that React does not
expect and still avoid a hydration mismatch.
>
>This new algorithm isn't perfect and it is possible for a mismatch to
occur. The most glaring case may be if a 3rd party script prepends a
`<div>` into `<body>` and you render a `<div>` in `<body>` in your app.
there is nothing to signal to React that this div was 3rd party so it
will claim is as the hydrated instance and hydration will almost
certainly fail immediately afterwards.
>
>The expectation is that this is rare and that if falling back to client
rendering is transparent to the user then there is not problem here. We
will continue to evaluate this and may change the hydration matching
algorithm further to match user and developer expectations
2023-03-06 15:00:54 -08:00
Joe Savona f3c662845b Support string literal keys for object expressions
For ObjectExpression, we now support computed keys where the key is a string 
literal.
2023-03-06 14:51:18 -08:00
Joe Savona a9d6d2d95a [rfc] always create collections even if elements have errors
BuildHIR currently propagates UnsupportedNodes for collection types where the 
element itself can fail (for example object expressions where the key may not be 
valid). However, given that we currently abort compilation after the first 
failing pass (and will probably do so for quite a while) I think we can simplify 
and just always return the collection. Note that I already did this for 
destructuring. I'm open to leaving the code as-is if you prefer, though.
2023-03-06 14:43:58 -08:00
Joe Savona b793fe424d Separate eachLValue/eachOperand visitors
This PR starts to clean up our handling of lvalues and rvalues by adding new 
`eachInstructionLValues()` and `mapInstructionLValues()` helpers. Now, 
`eachInstructionOperand()` and `mapInstructionOperands()` only visit true 
rvalues, and the new passes must be used to visit lvalues. This allows us to 
remove the special-casing for StoreLocal and Destructure in most of the passes.
2023-03-06 14:33:52 -08:00
Sathya Gunasekaran cf03bac12a [be] Move skipped test to kitchensink 2023-03-07 16:21:34 +00:00
Sathya Gunasekaran 8477195c74 [babel] Re-export compiled functions 2023-03-07 16:08:16 +00:00
Jan Kassens 3cad3a54ed Use content hash for facebook-www builds (#26331)
Currently, any commit to React causes an internal sync since the Git
commit hash is part of the build. This creates a lot more sync commits
and noise than necessary, see:
https://github.com/facebook/react/commits/builds/facebook-www

This PR changes the version string to be a hash of the target build
files instead. This way we get a new version with any change that
actually impacts the generated files and still have a matching version
across the files.
2023-03-06 17:13:17 -05:00
Jan Kassens ba353a50a6 Build: make version in build artifacts match (#26329)
Some build artifacts contain multiple version strings. It seems like an
oversight to me that this `.replace` call just replaces the one that
happens to be first.
2023-03-06 15:38:43 -05:00
Jan Kassens 88313ffd57 Codesandbox: upgrade to Node.js 18 (#26330)
Turns out Codesandbox didn't support `String.prototype.replaceAll` in
#26329.

This updates the config to use Node.js 18 for Codesandbox builds.
2023-03-06 15:38:03 -05:00
Sathya Gunasekaran 39898c1858 [babel] Add gating module support
Instead of replacing original function with compiled code, this adds an option 
to append the code and switch between the two based on an `isForgetEnabled` test 
condition that's imported from the specified gatingModule.
2023-03-06 19:54:00 +00:00
Sathya Gunasekaran 2ede182f5e [test] Enable gatingModule in tests 2023-03-06 19:53:57 +00:00
Sathya Gunasekaran c642de322c [test] Enable "use forget" directive in tests 2023-03-06 19:53:54 +00:00
Sathya Gunasekaran 8c24fc2be2 [pipeline] Type the result as t.FunctionDeclaration
A more narrower type as we only accept FunctionDeclaration as inputs
2023-03-06 19:53:52 +00:00
Sathya Gunasekaran 3cd31a11fb [babel] Add option to specify a gatingModule 2023-03-06 19:53:49 +00:00
Sathya Gunasekaran 637c2e68f1 [be][babel] Move directive checking to separate function 2023-03-06 19:53:46 +00:00
mofeiZ 0e1bba87ea [be][cleanup] Split primitive tests to individual fixtures
Followup to #1273 

No changes, just moving test functions to their own fixture files.
2023-03-06 18:25:53 -05:00
Mofei Zhang 8be45e7d4a [rhir][optim] Preserve conditional deps when propagating reactive scopes
--- 

**This PR slightly changes the semantics of ReactiveScopeDependencies**. 
Previously, reading a ReactiveScopeDependency is guaranteed to preserve the 
`nullthrows` semantics of its own declarations (not that of its inner scopes). 
This does not affect the overall correctness properties, since we already hoist 
reading of conditional dependencies (and thus may throw earlier than the 
original source). 

E.g. we already do not preserve *where* the nullthrows occurs. 

```javascript 

function Component(props) { 

// throws here, before print(x) 

const c_0 = props.a.b !== $[0]; 

let x; 

if (c_0) { 

x = {}; 

print(x); 

if (...) mutate1(x, props.a.b); 

mutate2(x, props.a.b); 

// ... 

``` 

### Summary 

This is an optimization, not a correctness property. 

When propagating reactive dependencies of an inner scope up to its parent, we 
want to *retain information about conditional dependencies* -- not the derived 
unconditional dependencies. This helps us produce more granular dependencies in 
the parent scope. 

Current implementation: 

```javascript 

const innerScopeDeps = innerScope.depTree.deriveMinimalUnconditionalDeps(); 

for (const dep of innerScopeDeps) { 

currentScope.depTree.addDep(dep); 

} 

``` 

New implementation: 

```javascript 

// union of a tree takes union of each node 

currentScope.depTree = currentScope.depTree.union(innerScope.depTree); 

``` 

### Example 

In the below example: 

- `scope @1` has a conditional dependency of `props.a.b`, but that reduces to 
the unconditional dependency `props` 

- `scope @0` itself has a unconditional dependency of `props.a.b` 

- Currently, Forget joins the derived / reduced dependencies of inner scopes, 
which adds `props` as unconditional dependency of `scope @0` 

- With this change, Forget joins the property trees and retains info about 
conditional deps, which adds `props.a.b` as a conditional dep of `scope @0`. 

```javascript 

// scope @0 (deps=[???] decls=[x, y]) 

let y = {}; 

// scope @1 (deps=[props] decls=[x]) 

let x = {}; 

if (foo) mutate1(x, props.a.b); 

mutate2(y, props.a.b); 

``` 

### Followup 

We currently keep track of properties unconditionally accessed per 
ReactiveBlock. Eventually we want to keep track of properties unconditionally 
accessed across blocks (as according to control flow). 

Consider the following code, in which sibling scopes 0 and 1 are sequentially 
executed. In this case, we can safely add props.a.b as a dependency of scope 1. 

```javascript 

// scope@0 (deps=[props.a.b], decls=[x]) 

let x = { a: foo(props.a.b) }; 

// scope@1 (deps=[???], decls=[y]) 

let y = {}; 

if (...) { 

mutate(y, props.a.b); 

} 

```
2023-03-06 16:33:08 -05:00
Mofei Zhang e2698a3124 [rhir] Add printDeps for debugging reactive dependency trees 2023-03-06 16:33:07 -05:00
Mofei Zhang e68ea941e6 [rhir] Promote conditional dependencies accessed by every cfg path
--- 

Implementation details summarized in comments. 

Overall, we want to calculate a `ReactiveDependencyTree` for every conditional 
block. If we know that conditional blocks are exhaustive (e.g. all CFG paths 
calculates a tree), we can take `intersection(depsFromEachBlock)` and add this 
to the parent Reactive + conditional scope `parentDeps = union(parentDeps, 
intersection(...))`. 

We use trees instead of individual deps here because we can still derive 
unconditional accesses. 

e.g. 

``` 

let x = {}; 

// props.a is an unconditional access here 

if (foo(other)) { 

x.a = props.a.b; 

} else { 

x.b = props.a.c; 

} 

```
2023-03-06 16:33:07 -05:00
Mofei Zhang 25bfe728aa [rhir] move DeriveMinimalDependencies to its own file
--- 

Small refactor of reactive dependency logic, no behavioral change. 

- Moves ReactiveDependencyTree logic into `DeriveMinimalDependencies`. 

- this moves hides most helper functions + types 🥳 

- made `ReactiveDependencyTree` a class 

- Changes `#dependencies` type: `Set<ReactiveScopeDep>` -> 
`ReactiveDependencyTree` 

- instead of collecting all dependencies into a tree in the end, we now eagerly 
join dependencies into the tree on `visitDep` 

- this is needed for the next PR in the stack, which relies on incremental 
merging
2023-03-06 16:33:06 -05:00
Andrew Clark 6e1756a5a9 Move suspended render logic to ensureRootIsScheduled (#26328)
When the work loop is suspended, we shouldn't schedule a new render task
until the promise has resolved. When I originally implemented this, I
wasn't sure where to put this logic — `ensureRootIsScheduled` is the
more natural place for it, but that's also a really hot path, so I chose
to do it elsewhere, and left a TODO to reconsider later.

Now it's later. I'm working on a refactor to move the
`ensureRootIsScheduled` call to always happen in a microtask, so that if
there are multiple updates/pings in a single event, they get batched
into a single operation. Which means I can put the logic in that
function where it belongs.
2023-03-06 12:27:48 -05:00
Andrew Clark 1528c5ccdf SchedulerMock.unstable_yieldValue -> SchedulerMock.log (#26312)
(This only affects our own internal repo; it's not a public API.)

I think most of us agree this is a less confusing name. It's possible
someone will confuse it with `console.log`. If that becomes a problem we
can warn in dev or something.
2023-03-06 11:09:07 -05:00
Jan Kassens 4bbac04cd3 Upgrade Flow to 0.201 (#26326)
Small Flow upgrade to keep us current.
2023-03-06 10:33:22 -05:00
Rubén Norte eb616a12f1 Extract duplicated methods in Fabric and the legacy renderer to a shared module (#26319)
## Summary

The following methods have exactly the same implementation on Fabric and
the legacy renderer:
* `findHostInstance_DEPRECATED`
* `findNodeHandle`
* `dispatchCommand`
* `sendAccessibilityEvent`

This just extracts those functions to a common module so they're easier
to change (no need to sync changes in 2 files).

## How did you test this change?

Existing tests (this is a refactor).
2023-03-06 10:51:59 +00:00
Andrew Clark 49f7410467 Fix: Infinite act loop caused by wrong shouldYield (#26317)
Based on a bug report from @bvaughn.

`act` should not consult `shouldYield` when it's performing work,
because in a unit testing environment, I/O (such as `setTimeout`) is
likely mocked. So the result of `shouldYield` can't be trusted.

In the regression test, I simulate the bug by mocking `shouldYield` to
always return `true`. This causes an infinite loop in `act`, because it
will keep trying to render and React will keep yielding.
2023-03-05 20:24:39 -05:00
Sebastian Markbåge 106ea1c584 Support Iterables in Flight (#26313)
We support any super type of anything that we can serialize. Meaning
that as long as the Type that's passed through is less precise, it means
that we can encoded it as any subtype and therefore the incoming type
doesn't have to be the subtype in that case. Basically, as long as
you're only passing through an `Iterable<T>` in TypeScript, then you can
pass any `Iterable<T>` and we'll treat it as an array.

For example we support Promises *and* Thenables but both are encoded as
Promises.

We support Arrays and since Arrays are also Iterables, we can support
Iterables.

For @wongmjane
2023-03-05 13:18:54 -05:00
Hendrik LiebauandSebastian Markbage f905da2276 [Flight] Send server reference error chunks to the client (#26293)
Previously when a called server reference function was rejected, the
emitted error chunk was not flushed, and the request was not properly
closed.

Co-authored-by: Sebastian Markbage <sebastian@calyptus.eu>
2023-03-04 22:56:19 -05:00
Sebastian Markbåge e0241b6600 Simplify Webpack References by encoding file path + export name as single id (#26300)
We always look up these references in a map so it doesn't matter what
their value is. It could be a hash for example.

The loaders now encode a single $$id instead of filepath + name.

This changes the react-client-manifest to have a single level. The value
inside the map is still split into module id + export name because
that's what gets looked up in webpack.

The react-ssr-manifest is still two levels because that's a reverse
lookup.
2023-03-04 19:51:34 -05:00
Andrew Clark 25685d8a90 Codemod tests to waitFor pattern (9/?) (#26309)
This converts some of our test suite to use the `waitFor` test pattern,
instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of
these changes are automated with jscodeshift, with some slight manual
cleanup in certain cases.

See #26285 for full context.
2023-03-04 18:06:20 -05:00
Andrew Clark 64dde70827 Codemod tests to waitFor pattern (8/?) (#26308)
This converts some of our test suite to use the `waitFor` test pattern,
instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of
these changes are automated with jscodeshift, with some slight manual
cleanup in certain cases.

See #26285 for full context.
2023-03-04 18:04:43 -05:00
Andrew Clark 3cb5afb82e Codemod tests to waitFor pattern (7/?) (#26307)
This converts some of our test suite to use the `waitFor` test pattern,
instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of
these changes are automated with jscodeshift, with some slight manual
cleanup in certain cases.

See #26285 for full context.
2023-03-04 18:03:24 -05:00
Andrew Clark e98695db91 Codemod tests to waitFor pattern (6/?) (#26305)
This converts some of our test suite to use the `waitFor` test pattern,
instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of
these changes are automated with jscodeshift, with some slight manual
cleanup in certain cases.

See #26285 for full context.
2023-03-04 11:45:26 -05:00
Andrew Clark 9a52cc8bcd Convert ReactLazy-test to waitFor pattern (#26304)
I'm in the process of codemodding our test suite to the waitFor pattern.
See #26285 for full context.

This module required a lot of manual changes so I'm doing it as its own
PR. The reason is that most of the tests involved simulating an async
import by wrapping them in `Promise.resolve()`, which means they would
immediately resolve the next time the microtask queue was flushed. I
rewrote the tests to resolve the simulated import explicitly.

While converting these tests, I also realized that the `waitFor` helpers
weren't properly waiting for the entire microtask queue to recursively
finish — if a microtask schedules another microtask, the subsequent one
wouldn't fire until after `waitFor` had resolved. To fix this, I used
the same strategy as `act` — wait for a real task to finish before
proceeding, such as a message event.
2023-03-04 11:44:41 -05:00
mofeiZ 03462cfc7a [Fizz] External runtime: fix bug in processing existing elements (#26303)
## Summary
Fix bug in how the Fizz external runtime processes existing template
elements.

Bug:
- getElementsByTagName returns a HTMLCollection, which is live.
- while iterating over an HTMLCollection, we call handleNode which
removes nodes

Fix:
- Call Array.from to copy children of `document.body` before processing.
- We could use `querySelectorAll` instead, but that is likely slower due
to reading more nodes.

## How did you test this change?

Did ad-hoc testing on Facebook home page by commenting out the mutation
observer and adding the following.
```javascript
  window.addEventListener('DOMContentLoaded', function () {
    handleExistingNodes(document.body);
  });
```
2023-03-04 11:26:59 -05:00
Andrew Clark faacefb4d0 Codemod tests to waitFor pattern (4/?) (#26302)
This converts some of our test suite to use the `waitFor` test pattern,
instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of
these changes are automated with jscodeshift, with some slight manual
cleanup in certain cases.

See #26285 for full context.
2023-03-03 21:24:22 -05:00