- Add a `loc` to the `while` terminal node.
- Move location data for assignments 1 level higher as that seems to work better
in the generated code (not tested with actual debugger yet though, we'll
probably want to look at this more closely.
This PR adds a new GitHub action to commit build artifacts for Facebook
into a new protected branch. This will later be used to setup an
automatic sync to www.
The hacky spinloop is meant to be a temporary implementation until we
can support running a script internally on top of the synced diff
(coming soon). A GitHub token is otherwise required if we want to setup
a pub/sub between cirleci and github but it's not straightforward to
provision one for our org. So this workaround should do for now since we
won't keep it around for too long.
Example of it running and creating a commit on the `builds/facebook-www`
branch:
https://github.com/facebook/react/actions/runs/3516958576/jobs/5894251359
Refactors Codegen to extract the core "visit IR as a tree" logic separately from
the code to emit JS:
* `HIRTreeVisitor` is a new helper that visits the HIR as a tree. You call
`visitTree(ir, yourVisitor)` and it drives visiting of the IR, tracking blocks
and scopes and calling methods as appropriate.
* `Codegen` is now implemented as a Visitor implementation. For example
`enterBlock()` creates an empty `Array<t.Statement>`, `leaveBlock()` wraps that
in a `t.BlockStatement`, etc.
* `printHIRTree()` is a new IR printer (implemented as a visitor) that prints
the HIR in tree form, so it retains the original shape of the code but with each
block replaced with its IR equivalent.
The new pretty printed scopes "syntax" breaks mermaid labels because the `@`
character seems to be reserved. This wraps them all as a string so they work
again.
Expands InferReactiveScopeVariables to update the mutableRange of all
identifiers to be the range of its scope. The result is that all identifiers in
a given scope will have the same range, whose start is the minimum of the
identifiers range starts, and end is the maximum.
<!--
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 debug-test --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
<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->
For more context: https://github.com/facebook/react/pull/25692
Based on https://github.com/facebook/react/pull/25695. This PR adds the
`SyncHydrationLane` so we rewind on sync updates during selective
hydration. Also added tests for ContinuouseHydration and
DefaultHydration lanes.
## How did you test this change?
<!--
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.
-->
yarn test
Now that hook state is preserved while the work loop is suspended, we
don't need to track the thenable state in the work loop. We can track
it alongside the rest of the hook state.
This is a nice simplification and also aligns better with how it works
in Fizz and Flight.
The promises will still be cleared when the component finishes rendering
(either complete or unwind). In the future, we could stash the promises
on the fiber and reuse them during an update. However, this would only
work for `use` calls that occur before an prop/state/context is
processed, because `use` calls can only be assumed to execute in the
same order if no other props/state/context have changed. So it might not
be worth doing until we have finer grained memoization.
Now that hook state is preserved while the work loop is suspended, we
don't need to track the thenable state in the work loop. We can track
it alongside the rest of the hook state.
Before deleting the thenable state variable from the work loop, I need
to remove the other places where it's referenced.
One of them is `isThenableStateResolved`. This grabs the last thenable
from the array and checks if it has resolved.
This was a pointless indirection anyway. The thenable is already stored
as `workInProgressThrownValue`. So we can check that directly.
When a component suspends, under some conditions, we can wait for the
data to resolve and replay the component without unwinding the stack or
showing a fallback in the interim. When we do this, we reuse the
promises that were unwrapped during the previous attempts, so that if
they aren't memoized, the result can still be used.
We should do the same for all hooks. That way, if you _do_ memoize an
async function call with useMemo, it won't be called again during the
replay. This effectively gives you a local version of the functionality
provided by `cache`, using the normal memoization patterns that have
long existed in React.
Currently, if you call setState in render, you must render the exact
same hooks as during the first render pass.
I'm about to add a behavior where if something suspends, we can reuse
the hooks from the previous attempt. That means during initial render,
if something suspends, we should be able to reuse the hooks that were
already created and continue adding more after that. This will error
in the current implementation because of the expectation that every
render produces the same list of hooks.
In this commit, I've changed the logic to allow more hooks to be added
when replaying. But only during a mount — if there's already a current
fiber, then the logic is unchanged, because we shouldn't add any
additional hooks that aren't in the current fiber's list. Mounts are
special because there's no current fiber to compare to.
I haven't change any other behavior yet. The reason I've put this into
its own step is there are a couple tests that intentionally break the
Hook rule, to assert that React errors in these cases, and those happen
to be coupled to the behavior. This is undefined behavior that is always
accompanied by a warning and/or error. So the change should be safe.
When replaying a suspended function components, we want to reuse the
hooks that were computed during the original render.
Currently we reset the state of the hooks right after the component
suspends (or throws an error). This is too early because it doesn't
give us an opportunity to wait for the promise to resolve.
This refactors the work loop to reset the hooks right before unwinding
instead of right after throwing. It doesn't include any other changes
yet, so there should be no observable behavioral change.
Before suspending, check if there are other pending updates that might
possibly unblock the suspended component. If so, interrupt the current
render and switch to working on that.
This logic was already implemented for the old "throw a Promise"
Suspense but has to be replicated for `use` because it suspends the
work loop much earlier.
I'm getting a little anxious about the divergence between the two
Suspense patterns. I'm going to look into enabling the new behavior for
the old pattern so that we can unify the implementations.
When an update flows into a dehydrated boundary, React cannot apply the
update until the boundary has finished hydrating. The way this currently
works is by scheduling a slightly higher priority task on the boundary,
using a special lane that's reserved only for this purpose. Because the
task is slightly higher priority, on the next turn of the work loop, the
Scheduler will force the work loop to yield (i.e. shouldYield starts
returning `true` because there's a higher priority task).
The downside of this approach is that it only works when time slicing is
enabled. It doesn't work for synchronous updates, because the
synchronous work loop does not consult the Scheduler on each iteration.
We plan to add support for selective hydration during synchronous
updates, too, so we need to model this some other way.
I've added a special internal exception that can be thrown to force the
work loop to interrupt the work-in-progress tree. Because it's thrown
from a React-only execution stack, throwing isn't strictly necessary —
we could instead modify some internal work loop state. But using an
exception means we don't need to check for this case on every iteration
of the work loop. So doing it this way moves the check out of the fast
path.
The ideal implementation wouldn't need to unwind the stack at all — we
should be able to hydrate the subtree and then apply the update all
within a single render phase. This is how we intend to implement it in
the future, but this requires a refactor to how we handle "stack"
variables, which are currently pushed to a per-render array. We need to
make this stack resumable, like how context works in Flight and Fizz.
This completes the implementation of InferReactiveScopeVariables, adding support
for phi nodes. Example:
```javascript
let x$0 = null;
mutate(x$0);
if (cond) {
x$1 = a;
mutate(x$1)
} else {
x$2 = b;
}
x$3 = phi(x$1, x$2);
mutate(x$3);
```
We now add x$1, x$2, and x$3 to the same reactive scope. This reflects the fact
that x$3 cannot be computed without also computing both x$1 and x$2. Note that
x$3 can never be x$0, so x$0 is _not_ added to the same scope. This allows us to
take advantage of SSA form to note that _some_ instances of an identifier really
are distinct.
This improves the error message a bit and ensures that we recommend
putting the key first, not last, which ensures that the faster
`jsx-runtime` is used.
This only affects the modern "automatic" JSX transform.
There's no option to output the results of the test262 harness in silent mode so
every pass and failure outputs multiple lines to stdout. Since there are many
thousands of tests this results in unusable log files that are over 200k lines
long. This PR redirects stdout to a tmp file and then we reformat the result
into a small JSON object, grouped by the failure message with count.
Example:
```json [ { "pass": false, "data": { "message": "Expected no
error, got Error: TODO: Support complex object assignment", "count": 66
} }, { "pass": false, "data": { "message": "Expected no
error, got Error: TODO: lowerExpression(FunctionExpression)", "count": 4
} }, { "pass": false, "data": { "message": "Expected no
error, got Error: TODO: lowerExpression(UnaryExpression)", "count": 6
} }, { "pass": false, "data": { "message": "Expected no error,
got Error: todo: lower initializer in ForStatement", "count": 28 }
}, { "pass": false, "data": { "message": "Expected no error, got
Invariant Violation: Expected value for identifier `15` to be initialized.",
"count": 14 } }, { "pass": false, "data": { "message":
"Expected no error, got Invariant Violation: `var` declarations are not
supported, use let or const", "count": 76 } }, { "pass": true,
"data": { "message": null, "count": 1 } } ] ```
This micro-optimization never made sense and less so now that they're
rare.
This still initializes the class with a shared immutable object in the
constructor - which is also what createClass() does.
Then we override it during mount. This is done in case someone messes up
the initialization of the super() constructor for example, which was
more common in polyfills.
This change means that if a ref is initialized during the constructor
itself it wouldn't be lazily initialized but that's not user code that
does it, it's React so that shouldn't happen.
This makes string refs codemoddable as described in.
https://github.com/facebook/react/pull/25334
Some old environments like IE11 or very old versions of jsdom are
missing `getRootNode()`. Use feature detection to fall back to
`ownerDocuments` in these environments that also won't be supporting
shadow DOM anyway.
Add a new workflow to run the test262 tests on commits to main but not in pull
requests. This is to keep this test non-blocking on PRs but lets us track pass
rates over time
This just removes the error but the underlying issue is still there, and
it's likely that the best course of action is to not update in effects
and to wrap most updates in startTransition. However, that's more of a
performance concern which is not something we generally do even in
recoverable errors since they're less actionable and likely belong in
another channel. It is also likely that in many cases this happens so
rarely because you have to interact quickly enough that it can often be
ignored.
After changes to other parts of the model, this only happens for
sync/discrete updates. There are three scenarios that can happen:
- We replace a server rendered fallback with a client rendered fallback.
Other than this potentially causing some flickering in the loading
state, it's not a big deal.
- We replace the server rendered content with a client side fallback if
this suspends on the client. This is in line with what would happen
anyway. We will loose state of forms which is not intended semantics.
State and animations etc would've been lost anyway if it was client-side
so that's not a concern.
- We replace the server rendered content with a client side rendered
tree and lose selection/state and form state. While the content looks
the same, which is unfortunate.
In most scenarios it's a bad loading state but it's the same scenario as
flushing sync client-side. So it's not so bad.
The big change here is that we consider this a bug of React that we
should fix. Therefore it's not actionable to users today because it
should just get fixed. So we're removing the error early. Although
anyone that has fixed these issues already are probably better off for
it.
To fix this while still hydrating we need to be able to rewind a sync
tree and then replay it.
@tyao1 is going to add a Sync hydration lane. This is will allow us to
rewind the tree when we hit this state, and replay it given the previous
Context, hydrate and then reapply the update. The reason we didn't do
this originally is because it causes sync mode to unwind where as for
backwards compatibility we didn't want to cause that breaking semantic -
outside Suspense boundaries - and we don't want that semantic longer
term. We're only do this as a short term fix.
We should also have a way to leave a partial tree in place. If the sync
hydration lane suspends, we should be able to switch to a client side
fallback without throwing away the state of the DOM and then hydrate
later.
We now know how we want to fix this longer term. We're going to move all
Contexts into resumable trees like what Fizz/Flight does. That way we
can leave the original Context at the hydration boundaries and then
resume from there. That way the rewinding would never happen even in the
existence of a sync hydration lane which would only apply locally to the
dehydrated tree.
So the steps are 1) remove the error 2) add the sync hydration lane with
rewinding 3) Allow hiding server-rendered content while still not
hydrated 4) add resumable contexts at these boundaries.
Fixes#25625 and #24959.
Adds a new pass `InferReactiveScopeVariables` which determines the sets of
variables (by Identifier) which "construct together" and belong in the same
reactive scope. Concretely, `Identifier` gets a new property `scope: ScopeId`,
and this pass assigns each identifier a ScopeId value. The algorithm iterates
over all instructions in all blocks (in a single pass) and builds up disjoint
sets of identifiers that appear as mutable operands in the same instruction.
The algorithm is relatively simple (especially since I had already implemented a
union-find data structure): however looking at some examples reinforced that
other planned todos around alias analysis are really important. We also have to
think more about what "mutable lifetime" means in the context of SSA: currently
variables that are reassigned (but never "mutated", eg bc they're assigned a
value type) never appear as mutable.
Just realized we can run all tests without encountering the arg limit if a
string is passed in.
This is much better because the test runner will count all tests in the parent
test directory rather than run the tests in each subdirectory
Noticed this while running test262 tests that many variables were throwing an
invariant for being undefined. This includes things like the special `arguments`
object, a global `assert` function used by test262, etc.
- Adds a shallow git submodule for test262 as the tests aren't available as an
npm module - To run all tests: `yarn test262:all`. Note that this chunks up the
tests by test262 folder as there are over 50k+ tests and the test harness only
accepts arrays of filepaths which exceeds arg limits - To run a specific test:
`yarn test262 test262/test/folder/file.js`. You can also pass globs which
expand into an array of filepaths: `yarn test262 test262/test/folder/**/*.js` -
More instructions for the test-harness can be found here:
https://github.com/bterlson/test262-harness
I noticed on @kassens's #771 that despite running LeaveSSA there are still cases
where we still reassign to a unique identifier: functions that have reassignment
but no phi nodes, such as:
```javascript
function foo() {
let x$1 = 0;
x$2 = x$1 + 1;
}
```
Here SSA form rewrote the second statement's LHS, but bc there's no phi node we
can't recover what the original was supposed to be (`x = x + 1`). This was my
oversight when suggesting the simpler LeaveSSA algorithm, it works for
eliminating phis but not other reassignments. The only alternative to removing
SSA form is to add assignment statements, which we obviously don't want to do
since that generates bloat.
This PR addresses the issue by adding an additional, optional property to
`Identifier` called `preSsaId` that starts off null. When entering SSA we save
the original id in this property and update id to a new SSA value. LeaveSSA does
the inverse, setting id = preSsaId and nulling out the latter. This means that
an identifier can always be uniquely identified by its `id` value at any point
in the compiler, while it's trivial to correctly undo SSA form.
```typescript
type Identifier = {
// Unique value for each original identifier
id: IdentifierId;
// The original, un-mangled variable name if this was a variable present in the
source (null if it's generated)
name: string | null;
// When in SSA mode, this is set to the original, pre-SSA `id` value
preSsaId: IdentifierId | null;
}
```
Implements assignment expressions with operators other than `=` (such as `+=`)
by lowering to an assignment.
I think this isn't fully correct for something like `a.b.c += 1`, but it seems
like there's more gaps in object accesses.
- Get rid of `indent` as it was making the code hard to read - Remove
unnecessary 2nd iteration over blocks - Remove extra newline between the bb
subgraphs and the jumps section - Remove trailing spaces - Remove newlines
between each subgraph and jump
We might need to revisit the tabs vs. options split, but for now this just adds
a checkbox toggle that outputs codegenned JS instead of HIR in the HIR tab. Open
to ideas to organize this in the future...
## Summary
This PR is to fix a bug: an "element cannot be found" error when
hydrating Server Components
### The problem
<img width="1061" alt="image"
src="https://user-images.githubusercontent.com/1001890/201206046-ac32a5e3-b08a-4dc2-99f4-221dad504b28.png">
To reproduce:
1. setting up a vercel next.js 13 playground locally
https://github.com/vercel/app-playground
2. visit http://localhost:3000/loading
3. click "electronics" button to navigate to
http://localhost:3000/loading/electronics to trigger hydrating
4. inspect one of the skeleton card UI from React DevTools extension
### The root cause & fix
This bug was introduced in #22527. When syncing reconciler changes, the
value of `Hydrating` was copied from another variable `Visibility` (one
more zero in the binary number).
To avoid this kind of issue in the future, a new file `ReactFiberFlags`
is created following the same format of the one in reconciler, so that
it's easier to sync the number without making mistakes.
The reconciler fiber flag file is also updated to reflect which of the
flags are used in devtools
## How did you test this change?
I build it locally and the bug no longer exist on
http://localhost:3000/loading