Commit Graph

19445 Commits

Author SHA1 Message Date
Mike Vitousek bbc7deabdc Update base for Update on "[compiler] Initialize abstract values of places in InferReferenceEffects"
Test Plan:
In this diff, we now populate the abstractValue field of places during the InferReferenceEffects pass. The value we populate it with is the same value that we use internally in this pass, but it now will remain accessible to downstream phases as part of the Place.

For phis specifically, we need to do a bit of extra work to compute the appropriate value for the phi, since InferReferenceEffects currently doesn't need to infer a value for phis themselves, only values downstream of them. However, the value we compute should correspond to the value available downstream of the phi.

This changes the error message for one todo test case, because we now are querying for the valueKind earlier in the pass, and hitting an invariant violation as a result of that rather than a later invariant violation.

[ghstack-poisoned]
2024-09-16 11:09:08 -07:00
Mike Vitousek 6a77fd2ff7 [compiler] Add nullable abstract value field to places and phis
Test Plan:
This PR starts the process of tracking abstract values (and therefore value kinds) on a per-place basis and persisting that into the place data structure. Here, we simply add a nullable field for abstract values to all places and phis--it will be populated in the next PR.

[ghstack-poisoned]
2024-09-16 10:54:44 -07:00
Mike Vitousek e78c9362c0 [compiler] Allow all hooks to take callbacks which access refs, but ban hooks from taking direct ref value arguments
Summary:
This brings the behavior of ref mutation within hook callbacks into alignment with the behavior of global mutations--that is, we allow all hooks to take callbacks that may mutate a ref. This is potentially unsafe if the hook eagerly calls its callback, but the alternative is excessively limiting (and inconsistent with other enforcement).

This also bans *directly* passing a ref.current value to a hook, which was previously allowed.

ghstack-source-id: e66ce7123e
Pull Request resolved: https://github.com/facebook/react/pull/30917
2024-09-16 10:53:34 -07:00
Mike Vitousek 1e68a0a3ae [compiler] Improve handling of refs
Summary:
This change expands our handling of refs to build an understanding of nested refs within objects and functions that may return refs. It builds a special-purpose type system within the ref analysis that gives a very lightweight structural type to objects and array expressions (merging the types of all their members), and then propagating those types throughout the analysis (e.g., if `ref` has type `Ref`, then `{ x: ref }` and `[ref]` have type `Structural(value=Ref)` and `{x: ref}.anything` and `[ref][anything]` have type `Ref`).

This allows us to support structures that contain refs, and functions that operate over them, being created and passed around during rendering without at runtime accessing a ref value.

The analysis here uses a fixpoint to allow types to be fully propagated through the system, and we defend against diverging by widening the type of a variable if it could grow infinitely: so, in something like
```
let x = ref;
while (condition) {
  x = [x]
}
```
we end up giving `x` the type `Structural(value=Ref)`.

ghstack-source-id: afb0b0cb01
Pull Request resolved: https://github.com/facebook/react/pull/30902
2024-09-16 10:53:32 -07:00
Mike Vitousek c8a7cab13f [compiler] Fix issue where second argument of all functions was considered to be a ref
ghstack-source-id: 1817f3b816
Pull Request resolved: https://github.com/facebook/react/pull/30912
2024-09-16 10:53:29 -07:00
Pieter De Baets 26855e4680 [react-native] Fix misleading crash when view config is not found (#30970)
## Summary

When a view config can not be found, it currently errors with
`TypeError: Cannot read property 'bubblingEventTypes' of null`. Instead
invariant at the correct location and prevent further processing of the
null viewConfig to improve the error logged.

## How did you test this change?

Build and run RN playground app referencing an invalid native view
through `requireNativeComponent`.
2024-09-16 17:51:00 +01:00
Ruslan Lesiutin 9f4e4611ea fix: add Error prefix to Error objects names (#30969)
This fixes printing Error objects in Chrome DevTools.

I've observed that Chrome DevTools is not source mapping and linkifying
URLs, when was running this on larger apps. Chrome DevTools talks to V8
via Chrome DevTools protocol, every object has a corresponding
[`RemoteObject`](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject).

When Chrome DevTools sees that Error object is printed in the console,
it will try to prettify it. `description` field of the corresponding
`RemoteObject` for the `Error` JavaScript object is a combination of
`Error` `name`, `message`, `stack` fields. This is not just a raw
`stack` field, so our prefix for this field just doesn't work. [V8 is
actually filtering out first line of the `stack` field, it only keeps
the stack frames as a string, and then this gets prefixed by `name` and
`message` fields, if they are
available](https://source.chromium.org/chromium/chromium/src/+/main:v8/src/inspector/value-mirror.cc;l=252-311;drc=bdc48d1b1312cc40c00282efb1c9c5f41dcdca9a?fbclid=IwZXh0bgNhZW0CMTEAAR1tMm5YC4jqowObad1qXFT98X4RO76CMkCGNSxZ8rVsg6k2RrdvkVFL0i4_aem_e2fRrqotKdkYIeWlJnk0RA).
As an illustration, this:
```
const fakeError = new Error('');
fakeError.name = 'Stack';
fakeError.stack = 'Error Stack:' + stack;
```

will be formatted by `V8` as this `RemoteObject`:
```
{
  ...
  description: 'Stack: ...',
  ...
}
```

Notice that there is no `Error` prefix, that was previously added.
Because of this, [Chrome DevTools won't even try to symbolicate the
stack](https://github.com/ChromeDevTools/devtools-frontend/blob/ee4729d2ccdf5c6715ee40e6697f5464829e3f9a/front_end/panels/console/ErrorStackParser.ts#L33-L35),
because it doesn't have such prefix.
2024-09-16 17:43:40 +01:00
Sebastian Markbåge f2df5694f2 [Fiber] Log Component Renders to Custom Performance Track (#30967)
Stacked on #30960 and #30966. Behind the enableComponentPerformanceTrack
flag.

This is the first step of performance logging. This logs the start and
end time of a component render in the passive effect phase. We use the
data we're already tracking on components when the Profiler component or
DevTools is active in the Profiling or Dev builds. By backdating this
after committing we avoid adding more overhead in the hot path. By only
logging things that actually committed, we avoid the costly unwinding of
an interrupted render which was hard to maintain in earlier versions.

We already have the start time but we don't have the end time. That's
because `actualStartTime + actualDuration` isn't enough since
`actualDuration` counts the actual CPU time excluding yields and
suspending in the render.

Instead, we infer the end time to be the start time of the next sibling
or the complete time of the whole root if there are no more siblings. We
need to pass this down the passive effect tree. This will mean that any
overhead and yields are attributed to this component's span. In a follow
up, we'll need to start logging these yields to make it clear that this
is not part of the component's self-time.

In follow ups, I'll do the same for commit phases. We'll also need to
log more information about the phases in the top track. We'll also need
to filter out more components from the trees that we don't need to
highlight like the internal Offscreen components. It also needs polish
on colors etc.

Currently, I place the components into separate tracks depending on
which lane currently committed. That way you can see what was blocking
Transitions or Suspense etc. One problem that I've hit with the new
performance.measure extensions is that these tracks show up in the order
they're used which is not the order of priority that we use. Even when
you add fake markers they have to actually be within the performance run
since otherwise the calls are noops so it's not enough to do that once.

However, I think this visualization is actually not good because these
trees end up so large that you can't see any other lanes once you expand
one. Therefore, I think in a follow up I'll actually instead switch to a
model where Components is a single track regardless of lane since we
don't currently have overlap anyway. Then the description about what is
actually rendering can be separate lanes.

<img width="1512" alt="Screenshot 2024-09-15 at 10 55 55 PM"
src="https://github.com/user-attachments/assets/5ca3fa74-97ce-40c7-97f7-80c1dd7d6470">

<img width="1512" alt="Screenshot 2024-09-15 at 10 56 27 PM"
src="https://github.com/user-attachments/assets/557ad65b-4190-465f-843c-0bc6cbb9326d">
2024-09-16 11:45:50 -04:00
Sebastian Markbåge ee1a403a30 [Fiber] Move Profiler onPostCommit processing of passive effect durations to plain passive effect (#30966)
We used to queue a separate third passive phase to invoke onPostCommit
but this is unnecessary. We can just treat it as a plain passive effect.
This means it is interleaved with other passive effects but we only need
to know the duration of the things below us which is already done at
this point.

I also extracted the user space call to onPostCommit into
ReactCommitEffects. Same as onCommit. It's now covered by
runWithFiberInDEV and catches.
2024-09-16 11:10:05 -04:00
Sebastian Markbåge 0eab377a96 Add enableComponentPerformanceTrack Flag (#30960)
This flag will be used to gate a new timeline profiler that's integrate
with the Performance Tab and the new performance.measure extensions in
Chrome.

It replaces the existing DevTools feature so this disables
enableSchedulingProfiler when it is enabled since they can interplay in
weird ways potentially.

This means that experimental React now disable scheduling profiler and
enables this new approach.
2024-09-16 11:09:40 -04:00
Ruslan Lesiutin 8cf64620c7 fix[rdt/fiber/renderer.js]: getCurrentFiber can be injected as null (#30968)
In production artifacts for `18.x.x` `getCurrentFiber` can actually be
injected as `null`. Updated `getComponentStack` and `onErrorOrWarning`
implementations to support this.

![Screenshot 2024-09-16 at 10 52
00](https://github.com/user-attachments/assets/a0c773aa-ebbf-4fd5-95c4-cac3cc0c203f)
2024-09-16 14:47:57 +01:00
Josh Story fc5ef50da8 [Flight] Start initial work immediately (#30961)
In a past update we made render and prerender have different work
scheduling behavior because these methods are meant to be used in
differeent environments with different performance tradeoffs in mind.
For instance to prioritize streaming we want to allow as much IO to
complete before triggering a round of work because we want to flush as
few intermediate UI states. With Prerendering there will never be any
intermediate UI states so we can more aggressively render tasks as they
complete.

One thing we've found is that even during render we should ideally kick
off work immediately. This update normalizes the intitial work for
render and prerender to start in a microtask. Choosing microtask over
sync is somewhat arbitrary but there really isn't a reason to make them
different between render/prerender so for now we'll unify them and keep
it as a microtask for now.

This change also updates pinging behavior. If the request is still in
the initial task that spawned it then pings will schedule on the
microtask queue. This allows immediately available async APIs to resolve
right away. The concern with doing this for normal pings is that it
might crowd out IO events but since this is the initial task there would
be IO to already be scheduled.
2024-09-14 09:26:01 -07:00
春希与子晴 b75cc078c5 Fix nodeName to UPPERCASE in insertStylesheetIntoRoot (#28255)
## Summary

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

<img width="518" alt="image"
src="https://github.com/facebook/react/assets/18693190/6d12df76-7dae-403b-b486-4940992abe8d">

The condition `node.nodeName === 'link'` is always `false`, because
`node.nodeName` is Uppercase in specification. And the condition
`node.nodeName === 'LINK'` is unnecessary, because Fizz hoists tags when
it's `media` attribute is `"not all"`, whether it is a `link` or a
`style` (line 36):


https://github.com/facebook/react/blob/18cbcbf783377c5a22277a63ae41af54504502e0/packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime.js#L30-L44


https://github.com/facebook/react/blob/18cbcbf783377c5a22277a63ae41af54504502e0/packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetInlineSource.js#L30-L44
2024-09-14 08:18:27 -07:00
Sebastian Markbåge 3d95c43b89 [Fiber] Profiler - Use two separate functions instead of branch by flag (#30957)
Nit: I don't trust flags in hot code. While it can take somewhat longer
to compile two functions and JIT them. After that they don't need to
check branches. Also makes it clearer the purpose.
2024-09-13 21:51:52 -04:00
Josh Story 6774caa379 [Flight] properly track pendingChunks when changing environment names (#30958)
When the environment name changes for a chunk we issue a new debug chunk
which updates the environment name. This chunk was not beign included in
the pendingChunks count so the count was off when flushing
2024-09-13 15:55:42 -07:00
Hendrik Liebau 5deb78223a [Flight] Respect async flag in client manifest (#30959)
In #26624, the ability to mark a client reference module as `async` in
the React client manifest was removed because it was not utilized by
Webpack, neither in `ReactFlightWebpackPlugin` nor in Next.js. However,
some bundlers and frameworks are sophisticated enough to properly handle
and identify async ESM modules (e.g., client component modules with
top-level `await`), most notably Turbopack in Next.js. Therefore, we
need to consider the `async` flag in the client manifest when resolving
the client reference metadata on the server. The SSR manifest cannot
override this flag, meaning that if a module is async, it must remain
async in all client environments.

x-ref: https://github.com/vercel/next.js/pull/70022
2024-09-13 16:33:05 -04:00
ling1726 d9c4920e8b fix: restore selection should consider the window of the container (#30951)
## Summary


Fixes #30864 

Before this PR the active elemen was always taken from the global
`window`. This is incorrect if the renderer is in one window rendering
into a container element in another window. The changes in this PR adds
another code branch to use a `defaultView` of the container element if
it exists so that `restoreSelection` after a commit will actually
restore to the correct window.

## How did you test this change?

I patched these changes to the repro repo in the linked issue #39864
https://github.com/ling1726/react-child-window-focus-repro/blob/master/patches/react-dom%2B18.3.1.patch.

I followed the same repro steps in the linked issue and and could not
repro the reported problem. Attaching screen recordings below:

Before
![focus
repro](https://github.com/user-attachments/assets/81c4b4f9-08b5-4356-8251-49b909771f3f)

After

![after](https://github.com/user-attachments/assets/84883032-5558-4650-9b9a-bd4d5fd9cb13)
2024-09-13 13:29:40 -07:00
Jan Kassens d3d4d3a46b Call cleanup of insertion effects when hidden (#30954)
Insertion effects do not unmount when a subtree is removed while
offscreen.

Current behavior for an insertion effect is if the component goes

- *visible -> removed:* calls insertion effect cleanup
- *visible -> offscreen -> removed:* insertion effect cleanup is never
called

This makes it so we always call insertion effect cleanup when removing
the component.

Likely also fixes https://github.com/facebook/react/issues/26670

---------

Co-authored-by: Rick Hanlon <rickhanlonii@fb.com>
2024-09-13 16:18:14 -04:00
Mike Vitousek 633a0fe536 [compiler] Factor out function effects from reference effects
Summary:
This PR performs a major refactor of InferReferenceEffects to separate out the work on marking places with Effects from inferring FunctionEffects. The behavior should be identical after this change (see [internal sync](https://www.internalfb.com/intern/everpaste/?handle=GN74VxscnUaztTYDAL8q0CRWBIxibsIXAAAB)) but the FunctionEffect logic should be easier to work with.

These analyses are unfortunately still deeply linked--the FunctionEffect analysis needs to reason about the "current" value kind for each point in the program, while the InferReferenceEffects algorithm performs global updates on the state of the program (e.g. freezing). In the future, it might be possible to make these entirely separate passes if we store the ValueKind directly on places.

For the most part, the logic of reference effects and function effects can be cleanly separated: for each instruction and terminal, we visit its places and infer their effects, and then we visit its places and infer any function effects that they cause. The biggest wrinkle here is that when a transitive function freeze operation occurs, it has to happen *after* inferring the function effects on the place, because otherwise we may convert a value from Context to Frozen, which will cause the ContextualMutation function effect to be converted to a ReactMutation effect too early. This can be observed in a case like this:

```
export default component C() {
  foo(() => {
    const p = {};
    return () => {
      p['a'] = 1
    };
  });
}
```
Here when the outer function returns the inner function, it freezes the inner function which transitively freezes `p`. But before that freeze happens, we need to replay the ContextualMutation on the inner function to determine that the value is mutable in the outer context. If we froze `p` first, we would instead convert the ContextualMutation to a ReactMutation and error.

To handle this, InferReferenceEffects now delays the exection of the freezeValue action until after it's called the helper functions that generate function effects. So the order of operations on a given place is now

set effect --> generate function effects --> transitively freeze dependencies, if applicable

ghstack-source-id: 21cb50c140
Pull Request resolved: https://github.com/facebook/react/pull/30920
2024-09-13 12:38:17 -07:00
Sebastian Markbåge 94e4acaa14 [Fiber] Set profiler values to doubles (#30942)
At some point this trick was added to initialize the value first to NaN
and then replace them with zeros and negative ones.

This is to address the issue noted in
https://github.com/facebook/react/issues/14365 where these hidden
classes can be initialized to SMIs at first and then deopt when we
realize they're actually doubles.

However, this fix has been long broken and has deopted the profiling
build for years because closure compiler optimizes out the first write.

I'm not sure because I haven't A/B-tested this in the JIT yet but I
think we can use negative zero and -1.1 as the initial values instead
since they're not simple integers. Negative zero `===` zero (but not
Object.is) so is the same as far as our code is concerned. The negative
value is just `< 0` comparisons.
2024-09-13 12:27:00 -04:00
Mofei Zhang 206df66e70 [compiler][rewrite] PropagateScopeDeps hir rewrite
Resubmission of #30079 -- core logic unchanged, but needed to rebase past #30573

### Quick background
#### Temporaries

The compiler currently treats temporaries and named variables (e.g. `x`) differently in this pass.
- named variables may be reassigned (in fact, since we're running after LeaveSSA, a single named identifier's IdentifierId may map to multiple `Identifier` instances -- each with its own scope and mutable range)
- temporaries are replaced with their represented expressions during codegen. This is correct (mostly correct, see #29878) as we're careful to always lower the correct evaluation semantics. However, since we rewrite reactive scopes entirely (to if/else blocks), we need to track temporaries that a scope produces in `ReactiveScope.declarations` and later promote them to named variables.
In the same example, $4, $5, and $6 need to be promoted: $2 ->`t0`,  $5 ->`t1`, and $6 ->`t2`.
```js
[1] $2 = LoadGlobal(global) foo
[2] $3 = LoadLocal bar$1
scope 0:
  [3] $4 = Call $2(<unknown> $3)
scope 1:
  [4] $5 = Object {  }
scope 2:
  [5] $6 = Object { a: $4, b: $5 }
[6] $8 = StoreLocal Const x$7 = $6
```

#### Dependencies
`ReactiveScope.dependencies` records the set of (read-only) values that a reactive scope is dependent on. This is currently limited to just variables (named variables from source and promoted temporaries) and property-loads.
All dependencies we record need to be hoistable -- i.e. reordered to just before the ReactiveScope begins. Not all PropertyLoads are hoistable.

In this example, we should not evaluate `obj.a.b` without before creating x and checking `objIsNull`.
```js
// reduce-reactive-deps/no-uncond.js
function useFoo({ obj, objIsNull }) {
  const x = [];
  if (isFalse(objIsNull)) {
    x.push(obj.a.b);
  }
  return x;
}
```

While other memoization strategies with different constraints exist, the current compiler requires that `ReactiveScope.dependencies` be re-orderable to the beginning of the reactive scope. But.. `PropertyLoad`s from null values will throw `TypeError`. This means that evaluating hoisted dependencies should throw if and only if the source program throws. (It is also a bug if source throws and compiler output does not throw. See https://github.com/facebook/react-forget/pull/2709)

---
### Rough high level overview
1. Pass 1
Walk over instructions to gather every temporary used outside of its defining scope (same as ReactiveFunction version). These determine the sidemaps we produce, as temporaries used outside of their declaring scopes get promoted to named variables later (and are not considered hoistable rvals).
2. Pass 2 (collectTemporariesSidemap)
Walk over instructions to generate a sidemap of temporary identifier -> named variable and property path (e.g. `$3 -> {obj: props, path: ["a", "b"]}`)
2. Pass 2 (collectHoistablePropertyLoads)
  a. Build a sidemap of block -> accessed variables and properties (e.g. `bb0 -> [ {obj: props, path: ["a", "b"]} ]`)
  b. Propagate "non-nullness" i.e. variables and properties for which we can safely evaluate `PropertyLoad`.
  A basic block can unconditionally read from identifier X if any of the following applies:
    - the block itself reads from identifier X
    - all predecessors of the block read from identifier X
    - all successors of the block read from identifier X
4. Pass 3: (collectDependencies)
Walks over instructions again to record dependencies and declarations, using the previously produced sidemaps. We do not record any control-flow here
5. Merge every scope's recorded dependencies with the set of hoistable PropertyLoads

Tested by syncing internally and (1) checking compilation output differences ([internal link](https://www.internalfb.com/intern/everpaste/?handle=GPCfUBt_HCoy_S4EAJDVFJyJJMR0bsIXAAAB)), running internally e2e tests ([internal link](https://fburl.com/sandcastle/cs5mlkxq))

---
### Followups:
1. Rewrite function expression deps
This change produces much more optimal output as the compiler now uses the function CFG to understand which variables / paths are assumed to be non-null. However, it may exacerbate [this function-expr hoisting bug](https://github.com/facebook/react/blob/main/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-hoisting-functionexpr.tsx). A short term fix here is to simply call some form of `collectNonNullObjects` on every function expression to find hoistable variable / paths. In the longer term, we should refactor out `FunctionExpression.deps`.

2. Enable optional paths
(a) don't count optional load temporaries as dependencies (e.g. `collectOptionalLoadRValues(...)`).
(b) record optional paths in both collectHoistablePropertyLoads and dependency collection

ghstack-source-id: 2507f6ea75
Pull Request resolved: https://github.com/facebook/react/pull/30894
2024-09-12 17:40:57 -04:00
Mofei Zhang 5ac4034e14 [compiler] Fork fixtures for enablePropagateDepsInHIR
- flip `enablePropagateDepsInHIR` to off by default
- fork fixtures which produce compilation differences in #30894 to separate directory `propagate-scope-deps-hir-fork`, to be cleaned up when we remove this flag

ghstack-source-id: 7d5b8dc297
Pull Request resolved: https://github.com/facebook/react/pull/30949
2024-09-12 17:40:57 -04:00
Mofei Zhang f6dcce5199 [compiler][ez] Add entrypoints to ssa fixtures
Adds evaluator support for a few compiler test fixtures

ghstack-source-id: 202654992a
Pull Request resolved: https://github.com/facebook/react/pull/30948
2024-09-12 17:40:57 -04:00
Sebastian Markbåge dff50825c6 [Flight] Track owner/stack where the Flight Client reads as the root (#30933)
This means that the owner of a Component rendered on the remote server
becomes the Component on this server.

Ideally we'd support this for the Client side too. In particular Fiber
but currently ReactComponentInfo's owner is typed as only supporting
other ReactComponentInfo and it's a bigger lift to support that.
2024-09-12 17:19:34 -04:00
Sebastian Markbåge 89b445709d Enable lazy context propagation (#30935)
Last I heard this was great so not sure there are any more blockers to
just include it in 19?
2024-09-12 13:01:56 -04:00
Josh Story 94e652d505 disable enableSiblingPrerendering in experimental channel (#30952)
Disables `enableSiblingPrerendering` in the experimental builds until
the feature is tested at Meta first.
2024-09-12 09:33:20 -07:00
Sebastian Markbåge 473522093d [Fizz] Add resumeAndPrerender to Static Rendering (#30950)
This is only in the same experimental exports as `resume`. Useful with
Postpone/Halt.

We already have `prerender()` to create a partial tree with postponed
state. We also have `resume()` to dynamically resume such a tree.

This lets you do a new prerender by resuming an already existing
postponed state. Basically creating a chain of preludes. The next
prelude would include the scripts to patch up the document.

This mostly just works since both prerender and resume are already
implemented using the same code so we just enable both at the root. I'm
sure we'll find some edge cases since this wasn't considered when it was
first written but so far I've only found an unrelated existing bug with
`keyPath` fixed here.
2024-09-12 10:51:01 -04:00
Ruslan Lesiutin bb6b86ed59 refactor[react-devtools]: initialize renderer interface early (#30946)
The current state is that `rendererInterface`, which contains all the
backend logic, like generating component stack or attaching errors to
fibers, or traversing the Fiber tree, ..., is only mounted after the
Frontend is created.

For browser extension, this means that we don't patch console or track
errors and warnings before Chrome DevTools is opened.

With these changes, `rendererInterface` is created right after
`renderer` is injected from React via global hook object (e. g.
`__REACT_DEVTOOLS_GLOBAL_HOOK__.inject(...)`.

Because of the current implementation, in case of multiple Reacts on the
page, all of them will patch the console independently. This will be
fixed in one of the next PRs, where I am moving console patching to the
global Hook.

This change of course makes `hook.js` script bigger, but I think it is a
reasonable trade-off for better DevX. We later can add more heuristics
to optimize the performance (if necessary) of `rendererInterface` for
cases when Frontend was connected late and Backend is attempting to
flush out too many recorded operations.

This essentially reverts https://github.com/facebook/react/pull/26563.
2024-09-12 13:59:29 +01:00
Andrew Clark d6cb4e7713 Start prerendering Suspense retries immediately (#30934)
When a component suspends and is replaced by a fallback, we should start
prerendering the fallback immediately, even before any new data is
received. During the retry, we can enter prerender mode directly if
we're sure that no new data was received since we last attempted to
render the boundary.

To do this, when completing the fallback, we leave behind a pending
retry lane on the Suspense boundary. Previously we only did this once a
promise resolved, but by assigning a lane during the complete phase, we
will know that there's speculative work to be done.

Then, upon committing the fallback, we mark the retry lane as suspended
— but only if nothing was pinged or updated in the meantime. That allows
us to immediately enter prerender mode (i.e. render without skipping any
siblings) when performing the retry.
2024-09-11 11:41:54 -04:00
Sebastian Markbåge 1bb056363c [Fizz] Use RequestInstance constructor for resuming (#30947)
We added enough fields to need a constructor instead of inline object in
V8.

We didn't update the resumeRequest path though so it wasn't using the
constructor and had a different hidden class.
2024-09-11 11:35:01 -04:00
Ruslan Lesiutin 344bc8128b refactor[Agent/Store]: Store to send messages only after Agent is initialized (#30945)
Both for browser extension, and for React Native (as part of
`react-devtools-core`) `Store` is initialized before the Backend (and
`Agent` as a part of it):

https://github.com/facebook/react/blob/bac33d1f82d9094b45d6f662dd7fa895abab8bce/packages/react-devtools-extensions/src/main/index.js#L111-L113

Any messages that we send from `Store`'s constructor are ignored,
because there is nothing on the other end yet. With these changes,
`Agent` will send `backendInitialized` message to `Store`, after which
`getBackendVersion` and other events will be sent.

Note that `isBackendStorageAPISupported` and `isSynchronousXHRSupported`
are still sent from `Agent`'s constructor, because we don't explicitly
ask for it from `Store`, but these are used.

This the pre-requisite for fetching settings and unsupported renderers
reliably from the Frontend.
2024-09-11 15:10:13 +01:00
Sebastian Markbåge bac33d1f82 [Flight] Unwrap lazy before reading the value (#30938)
This is important if the lazy is at the root of the chunk. I don't have
a unit test for it but @gnoff has a repro.

It also shouldn't unwrap the last value since that's the one we're
referencing.

This was already done correctly by @unstubbable in waitForReference so
this just aligns with that.
2024-09-10 19:42:19 -04:00
Sebastian Markbåge a5a7f1063e [Fiber] Don't call performance.now() twice in a row (#30936) 2024-09-10 16:46:35 -04:00
Andrew Clark 66cf2cfc8a Prerender during same pass if blocked anyway (#30879)
If something suspends in the shell — i.e. we won't replace the suspended
content with a fallback — we might as well prerender the siblings during
the current render pass, instead of spawning a separate prerender pass.

This is implemented by setting the "is prerendering" flag to true
whenever we suspend in the shell. But only if we haven't already skipped
over some siblings, because if so, then we need to schedule a separate
prerender pass regardless.
2024-09-10 13:23:32 -04:00
Joe Savona d724ba96ff [compiler] Validate type configs for hooks/non-hooks
Alternative to #30868. The goal is to ensure that the types coming out of moduleTypeProvider are valid wrt to hook typing. If something is named like a hook, then it must be typed as a hook (or don't type it).

ghstack-source-id: 3e8b5a0a70
Pull Request resolved: https://github.com/facebook/react/pull/30888
2024-09-10 09:25:28 -07:00
Ruslan Lesiutin 3dfd5d9efb refactor[RendererInterface]: expose onErrorOrWarning and getComponentStack (#30931)
Make `onErrorOrWarning` and `getComponentStack` part of
`rendererInterface`. By doing this, they will be available from the
global hook `rendererInterfaces` Map. This makes them available to be
used by Hook, which soon will be the only one who is doing console
patching.

This is also a pre-requisite for removing `registerRenderer`:

https://github.com/facebook/react/blob/d160aa0fbb1bd2d00ea8c771c551c9cb5b47f1e9/packages/react-devtools-shared/src/backend/console.js#L113-L121
2024-09-10 15:59:40 +01:00
Sebastian Markbåge 63cefa2957 [DevTools] Implement getComponentStack and onErrorOrWarning for replayed Flight logs (#30930)
This adds owner stacks to replayed Server Component logs in environments
that don't support native console.createTask.

<img width="521" alt="Screenshot 2024-09-09 at 8 55 21 PM"
src="https://github.com/user-attachments/assets/261cfaee-ea65-4044-abf0-c41abf358fea">

It also tracks the logs in the global componentInfoToComponentLogsMap
which lets us associate those logs with Server Components when they
later commit into the fiber tree.

<img width="1280" alt="Screenshot 2024-09-09 at 9 31 16 PM"
src="https://github.com/user-attachments/assets/436312a6-f9f4-4add-8129-0fb9b9eb18ee">

I tried to create unit tests for this since it's now wired up
end-to-end. Unfortunately, the complicated testing set up for Flight
requires a complex set of resetting modules which are incompatible with
the complicated test setup in getVersionedRenderImplementation for
DevTools tests.
2024-09-10 10:37:29 -04:00
Sebastian Markbåge d160aa0fbb [DevTools] Use Unicode Atom Symbol instead of Atom Emoji (#30832)
This reverts #19603.

Before:
<img width="724" alt="Screenshot 2024-08-28 at 12 07 29 AM"
src="https://github.com/user-attachments/assets/0613088f-c013-4f1c-92c3-fbdae8c1f109">

After:
<img width="771" alt="Screenshot 2024-08-28 at 12 08 13 AM"
src="https://github.com/user-attachments/assets/eef21bee-d11f-4f0a-9147-053a163f720f">

Consensus seems to be that while the purple on is a bit clearer and
easier to read. The purple is not on brand so it doesn't look like
React. It looks ugly. It's distracting (too eye catching). Taking away
attention from other tabs in an unfair way.

It also gets worse with more tabs added. We plan on both adding another
tab and panes inside other tabs (elements/sources) soon. Each needs to
be marked somehow as part of React but spelling it out is too long.
Putting inside a second tab means two clicks and takes away real-estate
from our extension and doesn't solve the problem with extension panes in
other tabs. We also plan on adding multiple different tracks to the
Performance tab which also needs a name other than just React and
spelling out React as a prefix is too long. The Emoji is too
distracting. So it seems best to uniformly apply the symbol - albeit it
might just look like a dot to many.

Dark mode looks close to on brand:

<img width="1089" alt="Screenshot 2024-08-28 at 12 32 50 AM"
src="https://github.com/user-attachments/assets/7175a540-4241-4c26-9e4d-4d367873af57">
2024-09-10 00:09:42 -04:00
Sebastian Markbåge 6066b8e8e6 [Flight] Reset currentOwnerInDEV (#30929)
Missed this bit.
2024-09-09 22:07:35 -04:00
Sebastian Markbåge 49825c0ffc [Flight] Add react-stack-bottom-frame to console replaying (#30926)
Any time we're creating a stack trace we should have a
react-stack-bottom-frame so we know what to filter out.

This is the same thing we already do for createFakeJSXCallStackInDEV but
we should do that when replaying logs too.
2024-09-09 19:52:05 -04:00
Sebastian Markbåge 0dbacf2041 [DevTools] Improve Layering Between Console and Renderer (#30925)
The console instrumentation should not know about things like Fibers.
Only the renderer bindings should know about that stuff. We can improve
the layering by just moving all that stuff behind a `getComponentStack`
helper that gets injected by the renderer.

This sets us up for the Flight renderer #30906 to have its own
implementation of this function.
2024-09-09 15:33:30 -04:00
Sebastian Markbåge fa3cf509a9 [DevTools] Add Map for Server Component Logs (#30905)
Stacked on #30899.

This adds another map to store Server Components logs. When they're
replayed with an owner we can associate them with a DevToolsInstance.
The replaying should happen before they can mount in Fiber so they'll
always have all logs when they mount. There can be more than one
Instance associated with any particular ReactComponentInfo. It can also
be unmounted and restored later.

One thing that's interesting about these is that when a Server Component
tree refreshes a new set of ReactComponentInfo will update through the
tree and the VirtualInstances will update with new instances. This means
that the old errors/warnings are no longer associated with the
VirtualInstance. I.e. it's not continually appended like updates do for
Fiber backed instances. On the client we dedupe errors/warnings for the
life time of the page. On the server that doesn't work well because it
would mean that when you refresh the page, you miss out on warnings so
we dedupe them per request instead. If we just appended on refresh it
would keep adding them.

If ever add a deduping mechanism that spans longer than a request, we
might need to do more of a merge when these updates.

Nothing actually adds logs to this map yet. That will need an
integration with Flight in a follow up.
2024-09-09 15:12:28 -04:00
Sebastian Markbåge f4b3a1fea2 [DevTools] Delete fiberToFiberInstanceMap (#30900)
Stacked on #30899.

After the rest of the stack this is now unused so we can save time and
memory avoiding to maintain it. 🎉
2024-09-09 15:12:13 -04:00
Sebastian Markbåge 2283d7204c [Flight] Inject Client Into DevTools (#30910)
Stacked on #30906.

Injects the Flight Client into the DevTools hook if it `supportsFlight`.
This only injects in DEV. We could inject it in prod too but so far the
only feature this exposes is only available in DEV anyway. I also only
call `injectIntoDevTools` in the browser builds since we don't really
support DevTools on the server anyway.

The main purpose of this for now is so that DevTools can track the
Server Component owner of replayed logs. This lets us add owner stacks
where `console.createTask` is not natively supported (like Firefox). It
also lets us associate the log with the Server Component in the
Component tree #30905.
2024-09-09 15:11:57 -04:00
Sebastian Markbåge 3cac8cd5a9 [DevTools] Add Flight Renderer (#30906)
This represents a virtual renderer that connects to the Flight Client.
It's virtual in the sense that the actual rendering has already happened
on the server. The Flight Client parses the result. Most of the result
then end up in objects that render into another renderer and that's how
we see most Server Components in DevTools. As part of the client's tree.

However, some things are side-effects that don't really connect to any
particular client renderer. For example preloads() and logs. For those
we need to treat the Flight Client as if it was its own renderer just
like a Fiber renderer or even legacy renderer. We really could support
Fizz and Flight Server as DevTools targets too for example to connect it
to the backend but there's just not much demand for that.

This will initially only be used to track the owners of replayed console
logs but could be expanded to more. For example to send controls to
start profiling on the server. It could also be expanded to build an RSC
payload inspector that is automatically connected.
2024-09-09 15:11:34 -04:00
Sebastian Markbåge e07235b980 [DevTools] Refactor Error / Warning Count Tracking (#30899)
We can simplify this tracking by not having a separate pending set of
logs and the logs tracked per instance and instead we just track the
logs per Fiber. This avoids the need to move it back into the pending
set after unmounts in case a Fiber is reparented.

The main motivation for this is to unify with an upcoming tracking of
logs for Server Components. For those it doesn't make sense to move them
into a per instance set and because the same Server Component - and its
logs - may appear more than once. So no particular instance should steal
it.

The second part of this change is that instead of looking up the
instance from fiber, which requires the fiberToFiberInstanceMap, we
instead look up if a component has any new logs when we traverse it in
the commit phase. After all for a component to have had a log it must
have updated. This is a similar technique to #30897. This technique also
works for Server Components without having to maintain a one to many
relationship from ComponentInfo to VirtualInstance. So it unifies them.

Normally this look up would be fast since the `fiberToComponentsLogs`
set would be empty and so doesn't add any significant weight to the
commit phase. If there's a ton of logs on many different components then
it's not great since it would slow down the commit phase but that's not
what we expect to see so in typical usage, this is better.

There is an unfortunate consequence though which is that
`console.warn/error` in passive effects (i.e. `useEffect`) wouldn't be
picked up because currently we traverse the logs in
`handleCommitFiberRoot` which is too early. If we moved that to
`handlePostCommitFiberRoot` this wouldn't be a problem. In the meantime,
I just detect this and do a brute force flush by walking all mounted
instances if there's a `console.warn/error` inside a passive effect.

If we ever add "owners" to event handlers that are triggered outside the
render/commit phases (like `<div onClick={...}>`) and we want to
associate error/warnings in those, we'd need a different technique to
ensure those get flushed in time.
2024-09-09 15:10:04 -04:00
Sam Zhou e210d08180 [flow] Upgrade Flow to 0.245.2 (#30919)
## Summary

This PR bumps Flow all the way to the latest 0.245.2. 

Most of the suppressions comes from Flow v0.239.0's change to include
undefined in the return of `Array.pop`.

I also enabled `react.custom_jsx_typing=true` and added custom jsx
typing to match the old behavior that `React.createElement` is
effectively any typed. This is necessary since various builtin
components like `React.Fragment` is actually symbol in the React repo
instead of `React.AbstractComponent<...>`. It can be made more accurate
by customizing the `React$CustomJSXFactory` type, but I will leave it to
the React team to decide.

## How did you test this change?

`yarn flow` for all the renderers
2024-09-09 08:41:44 -07:00
Alex Hunt 984ea11d14 [DevTools] Separate RDT Fusebox into single-panel entry points (#30708)
## Summary

Separate function entry points for `react-devtools-fusebox` into
`initializeComponents` and `initializeProfiler`. The motivation behind
this change is to separate these tabs into top-level Chrome DevTools
panels (aligned with web) in React Native.

## How did you test this change?

- Build `react-devtools-fusebox` and load into local
[rn-chrome-devtools-frontend](https://github.com/facebookexperimental/rn-chrome-devtools-frontend)
project with updated call sites.

<img width="1933" alt="image"
src="https://github.com/user-attachments/assets/202d32a1-b8da-4936-b0e1-04875a30f256">

<img width="1949" alt="image"
src="https://github.com/user-attachments/assets/39dbe154-989c-4f76-b103-aa19f07a3b9c">

 Tabs can be separately initialised in individual Chrome DevTools
panels
2024-09-09 08:12:49 -04:00
Mike Vitousek a8fc4b1ef8 [compiler][playground] Fix displayed naming of outlined functions
ghstack-source-id: 20c8e9eeba
Pull Request resolved: https://github.com/facebook/react/pull/30907
2024-09-07 17:50:19 -07:00
Sebastian Markbåge 99cba2b041 [DevTools] Build Updater List from the Commit instead of Map (#30897)
Stacked on #30896.

The problem with the `getUpdatersList` function is that it iterates over
Fibers and then looks up each of those Fibers in the
fiberToFiberInstanceMap which we ideally could get rid of.

However, every time an updater comes into play for a commit it must mean
that something below the updater itself updated and so the updater will
also be cloned which means we'll pass it on the way down when traversing
the tree in the commit.

When we do this traversal, we can just look if the Fiber is in the
updater set and if so add it to the updater list as we go.
2024-09-06 21:59:09 -04:00