Commit Graph
19463 Commits
Author SHA1 Message Date
Jan Kassens 16430247a4 [playground] remove unused dependency next-transpile-modules (#768) 2022-11-11 12:27:28 -05:00
Jan Kassens 3fd88b7f6f [playground] fixup for TS error 2022-11-11 12:18:18 -05:00
Jan Kassens b9ffb59478 [playground] remove multi-file support and preview (#767) 2022-11-11 12:11:08 -05:00
Lauren Tan 3b3107fb3e Add Mermaid visualizations for CFG into fixtures
This PR adds a new section to fixture tests, which renders the HIR into 

a visualization using mermaid.js syntax which can then be embedded 

directly into markdown. 

The nice thing about the mermaid syntax is that it's quite readable, so 

if desired we could replace the current basic block textual output with 

the mermaid block. I'm opting to append it for now and wait for feedback 

if we want to keep both or replace. 

To view the graphs in your editor, download an extension that 

adds mermaid.js support: 
https://mermaid-js.github.io/mermaid/#/integrations?id=editor-plugins. In vscode 
you can use this plugin by right clicking on "Open Preview" on any expect.md 
file. No extra dependencies are required for GitHub which should have builtin 
support for mermaid in markdown
2022-11-10 17:09:28 -05:00
Jan Kassens 779947520d [playground] new HIR tab
Instead of hacking into the babel plugin passes, this now takes a new approach 
for the HIR tab: 

- The different passes are exported from the babel plugin (for simplicity) 

- The tab actually runs the compiler steps based on local config in the tab. 

- Re-purposed the CompilerFlags component to configure what passes to run. This 
is currently mostly causing different errors, but could be useful going forward 
as a direction.
2022-11-10 15:45:51 -05:00
Jan Kassens 5e86e6a9c8 [playground] refactor output tabs
Removes a bit of indirection and opens the way to more easily customize specific 
tabs.
2022-11-10 14:32:40 -05:00
Jan Kassens 21b6112238 Fix yarn dev to run tsc watch mode
The `--watch` argument forwarding wasn't working anymore because `yarn build` 
doesn't have the `tsc` command at the end anymore. There's maybe something that 
can be done to forward the watch argument, just call `tsc --watch` directly.
2022-11-10 11:30:45 -05:00
Samuel Susla d1e35c7039 Don't disappear layout effects unnecessarily (#25660)
Nested Offscreens can run into a case where outer Offscreen is revealed
while inner one is hidden in a single commit. This is an edge case that
was previously missed. We need to prevent call to disappear layout
effects.

When we go from state:
```jsx
<Offscreen mode={'hidden'}> // outer offscreen
  <Offscreen mode={'visible'}> // inner offscreen
    {children}
  </Offscreen>
</Offscreen>
```

To following. Notice that visibility of each offscreen flips.

```jsx
<Offscreen mode={'visible'}> // outer offscreen
  <Offscreen mode={'hidden'}> // inner offscreen
    {children}
  </Offscreen>
</Offscreen>
```

Inner offscreen must not call
`recursivelyTraverseDisappearLayoutEffects`.
Check unit tests for an example of this.
2022-11-10 14:49:30 +00:00
Joe Savona f57bcacced fix for while (...) { break } edge case
Small adjustment to the previous PR for a special case: 

```javascript 

while (cond) { 

break; 

} 

``` 

The loop body is an indirection to the fallthrough, so shrink() collapses that 
and makes the while.loop === while.fallthrough. We now detect that this is the 
case in codegen and correctly emit a `break` rather than trying to write the 
fallthrough block inside the loop.
2022-11-09 20:52:08 -08:00
Joe Savona 656b8c9a0e While terminal and codegen
Adds a new 'while' terminal variant, which will be a model for other loop 
terminals, and adds support for the entire compilation pipeline through codegen. 
To understand the structure of the terminal consider this input: 

```javascript 

let x = 0; 

while (x) { 

x = foo(x); 

} 

return x; 

``` 

We currently lower this to ifs and gotos: 

``` 

bb0: precursor to loop 

let x = 0; 

goto(break) bb1; // <-- **The new terminal replaces this** 

bb1: test block, whether to (re-)enter the loop 

if (x) consequent=bb2 alternate=bb3; 

bb2: loop body 

x = foo(x); 

goto(continue) bb1; 

bb3: fallthrough after the loop 

return x 

``` 

This representation correctly models the semantics of while statements, but 
loses the high-level information that there was a loop. The new 'while' terminal 
replaces the first 'goto(break) bb1'. Conceptually, the 'while' terminal means 
"enter the starting point of a while loop". In this example the terminal would 
look like this: 

``` 

{ 

kind: 'while', 

testBlock: 'bb1', // the basic block that checks whether to enter the loop or 
not 

loop: 'bb2', // the block containing the loop body 

fallthrough: 'bb3' // the block that goes after the loop 

} 

``` 

Most passes will only look at 'testBlock', ie they will treat this terminal as a 
simple goto:testBlock. However, codegen uses the full information in the 
terminal to reconstruct the loop. My previous PR, #755, added a mechanism to be 
smart about when to emit or not emit `break` statements; this PR improves upon 
that to accurately emit the minimal break and continue statements: ie omitting 
entirely where they are extraneous, emitting unlabeled break/continue when 
sufficient, and falling back to labeled break/continue only where strictly 
necessary. The logic is very much analogous to IR construction.
2022-11-09 16:39:50 -08:00
Lauren Tan 90aa627c6b Add LeaveSSA pass to DumpHIRPass 2022-11-09 17:31:00 -05:00
Lauren Tan e64ef9e1e0 Extract eachBlockOperand to its own visitor
Follow up for #757: 

- Adds a new visitor which iterates over every Place within a BasicBlock - 
Remove unused entryBlock binding - Comments
2022-11-09 16:37:14 -05:00
Lauren Tan 74e6ffc4a2 Leave SSA form
Alternative approach to #750. We now store the original identifier on the Phi 
node, then rewrite every BasicBlock's identifiers to reference the original id 
instead of the SSA'd id. 

This solves the shadowing problem and also lets us omit adding copies of 
instructions.
2022-11-09 15:12:04 -05:00
Joe Savona ea1a18ec16 Emit labeled ifs/switch/break; gen each block exactly once
The approach is very similar to what BuildHIR does to resolve break and continue 
targets during IR construction: 

* We annotate goto targets as either a break or a continue (during HIR 
construction). This is necessary to reconstruct the right kind in codegen. 

* Codegen continues to work by traversing the IR as if it were a tree, relying 
on the `fallthrough` branches of if/switch to be able to visit the 
consequent/alternate recursively and then emit the fallthrough branch. 

* We track a Set of blocks that are scheduled to be emitted by some parent in 
the tree. Nested ifs may all have the same fallthrough branch, which we only 
want to emit once. This set helps us to know that a parent is already going to 
emit some block, such that children can skip it. 

* We also keep a stack of break targets that are in scope, and use this to 
convert gotos appropriately, as either a break, continue, or nothing at all (for 
example a switch case that falls through has no explicit syntax to model this 
fall-through, the only option is to emit nothing for the goto). 

* Then, if/switch have to carefully check whether each branch should be emitted 
or not. For example, if the alternate is already scheduled to be emitted (by a 
parent), then we emit a block with a break statement instead. 

* Switch in particular is tricky, because we need to know that subsequent cases 
are scheduled, but only for preceding blocks. So we visit the cases in reverse 
order (not surprisingly, we do the same thing during IR construction for similar 
reasons!). 

The bookkeeping is a bit finicky but this works reliably. There are some cases 
where we could try to emit an unlabeled break instead of a labeled break, or 
avoid emitting a label at all (if nothing will explicitly break to that label), 
but overall the generated code is readable enough that i'm inclined to ship and 
iterate. I'm open to feedback though, as always!
2022-11-08 21:14:10 -08:00
Joe Savona 22d1481125 Add and use mapInstructionOperands() 2022-11-08 14:46:03 -08:00
Joe Savona afabaa0af3 Add and use mapTerminalOperands() 2022-11-08 14:45:59 -08:00
Joe Savona 2a049044bb Add and use eachTerminalSuccessor() 2022-11-08 14:45:56 -08:00
Joe Savona e665f6b877 Cleanup in advance of reworking codegen 2022-11-08 14:45:53 -08:00
Jan Kassens e336ea44ed [playground] include HIR output (#756) 2022-11-09 13:39:50 -05:00
Aurélien Chivot-Buhler 1e3e30dae2 Fix useSyncExternalStore dropped update when state is dispatched in render phase (#25578)
Fix https://github.com/facebook/react/issues/25565
2022-11-08 11:25:42 +01:00
Lauren Tan f8f4dc4b8b s/SSAify/Enter SSA/
Rename this existing pass to make more sense with the next PR
2022-11-07 18:34:45 -05:00
Jan Kassens 5bd3a39d86 Implement HIR visitors (#747) 2022-11-08 09:55:35 -05:00
Jan Kassens 7c094af34e [HIR] fix missing read reference to switch test value
Unless I'm mistaken, the switch operand is missing here.
2022-11-07 16:38:58 -05:00
Mengdi Chen 18dff7990a [DevTools] add support for HostSingleton & HostResource (#25616)
## Summary

This is to support two new reconciler work tags `HostSingleton` and
`HostResource` introduced in PRs #25243 #25426. The behavior is
described below.
I also renamed an option in components settings from an internal concept
"host" to more understood "dom nodes"

## How did you test this change?

Tested on the latest Vercel playground app
https://github.com/vercel/app-playground/

Before the change, devtools cannot show correct display name for these
new elements. Also, some unnecessary internal details are exposed to
users.
<img width="1395" alt="image"
src="https://user-images.githubusercontent.com/1001890/199578181-c4e4ea74-baa1-4507-83d0-91a62ad7de5f.png">

After the change, the display names are correctly shown and the "state"
would always be hidden in the detail view.
<img width="1417" alt="image"
src="https://user-images.githubusercontent.com/1001890/199578442-adc1951d-7d5b-4b84-ad64-85bcf7a8ebcc.png">

These elements will also be hidden just like other native dom elements
(e.g. `<div>`)
<img width="836" alt="image"
src="https://user-images.githubusercontent.com/1001890/199578598-2dfacf64-ddc9-42b5-a246-dd0b09f629af.png">
2022-11-07 15:23:17 -05:00
Jan Kassens 5d81fdeb5f [easy] use yield* in eachInstructionOperand
Noticed we could simplify this code a bit using `yield*`.
2022-11-07 15:10:49 -05:00
Sathya Gunasekaran 69cce3d51f [hir][be] Move collectInputs to HIRBuilder
This seems like a better place to put helper methods.
2022-11-07 18:45:29 +00:00
Lauren Tan 4d9022a17c Remove early SSA phi optimzation
Reverts #726 which added an early optimization to the SSAify pass in skipping 
over phi creation if only one unique operand. This is no longer necessary with 
the addition of a phi elimination pass added in #739.
2022-11-07 12:25:50 -05:00
Joseph Savona 3a200fa4a1 Pass to eliminate redundant phis
This is an alternate take on phi elimination to the one we pursued over VC w 
@poteto driving. This version exploits the RPO ordering of blocks to do phi 
elimination in a single pass when there are no loops, and to minimize repeated 
visits when there are loops. The main difference is when redundant phis are 
removed. Rather than eagerly walking through the CFG for each pruned phi to 
rewrite its uses, we build up a mapping of rewritten identifiers. As we walk 
through subsequent instructions, we rewrite each place based on that mapping. We 
continue cycling through the blocks so long as a given iteration *both* added 
new rewrites (meaning there may be subsequent uses to rewrite) *and* there are 
back-edges. With no loops this results in a single visit of each block and of 
each instruction, but even with loops this is bounded.
2022-11-07 08:37:19 -08:00
Samuel Susla 4bd245e9ee Do not unmount layout effects if ancestor Offscreen is hidden (#25628)
This is a follow up on https://github.com/facebook/react/pull/25592

There is another condition Offscreen calls
`recursivelyTraverseDisappearLayoutEffects` when it shouldn't. Offscreen
may be nested. When nested Offscreen is hidden, it should only unmount
layout effects if it meets following conditions:
1. This is an update, not first mount.
2. This Offscreen was hidden before.
3. No ancestor Offscreen is hidden.

Previously, we were not accounting for the third condition.
2022-11-04 13:31:07 +00:00
Andrew Clark df61e708c8 Remove check in renderDidSuspendDelayIfPossible (#25630)
I don't think we need this anymore. It was added originally because
RootSuspended would take priority over RootSuspendedWithDelay. But we've
since changed it: any "bad" fallback state is permitted to block a
"good" fallback state.

The other status flags that this check used to account for are
RootDidNotComplete and RootFatalErrored:

- RootFatalErrored is like an invariant violation, it means something
went really wrong already and we can't recover from it
- RootCompleted and RootDidNotComplete are only set at the very end of
the work loop, there's no way for renderDidSuspendDelayIfPossible to
sneak in after that (at least none that I can think of — it's only
called from the render phase)

So I think we can just delete this.

It's entirely possible there's some scenario I haven't considered,
though, which is why I'm submitting this change as its own PR. To
preserve the ability to bisect to it later.
2022-11-03 16:15:55 -04:00
mofeiZ 1a08f1478d [ServerRenderer] Move fizz external runtime implementation to react-dom-bindings (#25617)
<!--
  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
-->

Following
[comment](https://github.com/facebook/react/pull/25437#discussion_r1010944983)
in #25437 , the external runtime implementation should be moved from
`react-dom` to `react-dom-bindings`.

I did have a question here:
I set the entrypoint to `react-dom/unstable_server-external-runtime.js`,
since a.) I was following #25436 as an example and b.)
`react-dom-bindings` was missing a `README.md` and `npm/`. This also
involved adding the external runtime to `package.json`.
However, the external runtime isn't really a `react-dom` entrypoint. Is
this change alright, or should I change the bundling code instead?
## 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.
-->
2022-11-03 11:15:29 -04:00
Andrew Clark 1a902623a8 Unwrap sync resolved thenables without suspending (#25615)
If a thenable resolves synchronously, `use` should unwrap its result
without suspending or interrupting the component's execution.
2022-11-02 13:53:23 -04:00
Josh Story 4ea063b56f refactor isHostResourceType to not receive the context from reconciler and not leak types (#25610)
type validateDOMNesting
move `isHostResourceType` to ReactDOMHostConfig
type `AncestorInfo`
refactor `resourceFormOnly` into `ancestorInfo.containerTagInScope`
provide hostContext from reconciler
2022-11-01 17:14:46 -07:00
Sebastian Markbåge 8e69bc45aa Make host context use null as empty and only error in dev (#25609)
Makes it slightly more blazing.

No host config actually uses null as any useful and we use this as a
placeholder value anyway. It's also better for perf since it doesn't let
two different hidden classes pass around. It's also a magic place holder
that triggers error if we do try to access anything from it.
2022-11-01 19:02:37 -04:00
Josh Story 5f7ef8c4cb [Float] handle resource Resource creation inside svg context (#25599)
`title` is a valid element descendent of `svg`. this PR adds a
prohibition on turning titles in svg into Resources.

This PR also adds additional warnings if you render something that is
almost a Resource inside an svg.
2022-11-01 14:57:45 -07:00
Andrew Clark 36426e6cb6 Allow uncached IO to stablize (#25561)
Initial draft. I need to test this more.

If a promise is passed to `use`, but the I/O isn't cached, we should
still be able to unwrap it.

This already worked in Server Components, and during SSR.

For Fiber (in the browser), before this fix the state would get lost
between attempts unless the promise resolved immediately in a microtask,
which requires IO to be cached. This was due to an implementation quirk
of Fiber where the state is reset as soon as the stack unwinds. The
workaround is to suspend the entire Fiber work loop until the promise
resolves.

The Server Components and SSR runtimes don't require a workaround: they
can maintain multiple parallel child tasks and reuse the state
indefinitely across attempts. That's ideally how Fiber should work, too,
but it will require larger refactor.

The downside of our approach in Fiber is that it won't "warm up" the
siblings while you're suspended, but to avoid waterfalls you're supposed
to hoist data fetches higher in the tree regardless. But we have other
ideas for how we can add this back in the future. (Though again, this
doesn't affect Server Components, which already have the ideal
behavior.)
2022-11-01 15:00:34 -04:00
mofeiZ 6883d79445 [ServerRenderer] Setup for adding data attributes streaming format (#25567) 2022-11-01 12:04:50 -04:00
Samuel Susla ab075a2324 Do not unmount layout effects on initial Offscreen mount (#25592)
`wasHidden` is evaluted to false if `current` is null. This means
Offscreen has never been shown but this code assumes it is going from
'visible' to 'hidden' and unmounts layout effects.
To fix this, only unmount layout effects if `current` is not null.

I'm not able to repro this problem or write unit test for it. I see this
crash bug in production test.
The problem with repro is that if Offscreen starts as hidden, it's
render is deferred and current is no longer null.
2022-11-01 15:11:28 +00:00
Sebastian Markbåge 765805bf88 Fix type check for null (#25595) 2022-10-31 17:29:01 -04:00
Andrew Clark 2ac77aab94 Clean up vestige of useOpaqueIdentifier (#25587)
Found some code that was left over from the experimental
useOpaqueIdentifier hook, which we eventually replaced with useId.

This deletes it.
2022-10-29 20:46:54 -04:00
Andrew Clark bdd3d0807c Extract logic for detecting bad fallback to helper
Pure refactor, no change in behavior.

Extracts the logic for detecting whether a suspended component will
result in a "bad" Suspense fallback into a helper function. An example
of a bad Suspense fallback is one that causes already-visible content
to disappear.

I want to reuse this same logic in the work loop, too.
2022-10-28 17:19:45 -07:00
Andrew Clark 952dfff3f1 Split suspended work loop logic into separate functions
Refactors the logic for handling when the work loop is suspended into
separate functions for replaying versus unwinding. This allows us to
hoist certain checks into the caller.

For example, when rendering due to flushSync, we will always unwind the
stack without yielding the microtasks.

No intentional behavior change in this commit.
2022-10-28 17:19:45 -07:00
Andrew Clark d2c0ab10de In work loop, add enum of reasons for suspending
This is a pure refactor, no change to behavior.

When a component throws, the work loop can handle that in one of several
ways — unwind immediately, wait for microtasks, and so on. I'm about
to add another one, too. So I've changed the variable that tracks
whether the work loop is suspended from a boolean
(workInProgressIsSuspended) to an enum (workInProgressSuspendedReason).
2022-10-28 17:19:45 -07:00
Andrew Clark 5450dd4098 Strict Mode: Reuse memoized result from first pass (#25583)
In Strict Mode, during development, user functions are double invoked to
help detect side effects. Currently, the way we implement this is to
completely discard the first pass and start over. Theoretically this
should be fine because components are idempotent. However, it's a bit
tricky to get right because our implementation (i.e. `renderWithHooks`)
is not completely idempotent with respect to internal data structures,
like the work-in-progress fiber. In the past we've had to be really
careful to avoid subtle bugs — for example, during the initial mount,
`setState` functions are bound to the particular hook instances that
were created during that render. If we compute new hook instances, we
must also compute new children, and they must correspond to each other.

This commit addresses a similar issue that came up related to `use`:
when something suspends, `use` reuses the promise that was passed during
the first attempt. This is itself a form of memoization. We need to be
able to memoize the reactive inputs to the `use` call using a hook (i.e.
`useMemo`), which means, the reactive inputs to `use` must come from the
same component invocation as the output.

The solution I've chosen is, rather than double invoke the entire
`renderWithHook` function, we should double invoke each individual user
function. It's a bit confusing but here's how it works:

We will invoke the entire component function twice. However, during the
second invocation of the component, the hook state from the first
invocation will be reused. That means things like `useMemo` functions
won't run again, because the deps will match and the memoized result
will be reused.

We want memoized functions to run twice, too, so account for this, user
functions are double invoked during the *first* invocation of the
component function, and are *not* double invoked during the second
incovation:

- First execution of component function: user functions are double
invoked
- Second execution of component function (in Strict Mode, during
development): user functions are not double invoked.

It's hard to explain verbally but much clearer when you run the test
cases I've added.
2022-10-28 17:46:20 -04:00
Andrew Clark d2a0176a13 Detect and warn if use(promise) is wrapped with try/catch block (#25543)
The old (unstable) mechanism for suspending was to throw a promise. The
purpose of throwing is to interrupt the component's execution, and also
to signal to React that the interruption was caused by Suspense as
opposed to some other error.

A flaw is that throwing is meant to be an implementation detail — if
code in userspace catches the promise, it can lead to unexpected
behavior.

With `use`, userspace code does not throw promises directly, but `use`
itself still needs to throw something to interrupt the component and
unwind the stack.

The solution is to throw an internal error. In development, we can
detect whether the error was caught by a userspace try/catch block and
log a warning — though it's not foolproof, since a clever user could
catch the object and rethrow it later.

The error message includes advice to move `use` outside of the try/catch
block.

I did not yet implement the warning in Flight.
2022-10-28 17:46:03 -04:00
Sebastian Markbåge cf3932be5c Remove old react-fetch, react-fs and react-pg libraries (#25577)
To avoid confusion. We are patching `fetch`, and only `fetch`, for a
small fix scoped to react renders elsewhere, but this code is not it.

This code was for the strategy used in the original [React Server
Components demo](https://github.com/reactjs/server-components-demo).
Which [we
announced](https://reactjs.org/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022.html)
that we're moving away from in favor of [First class support for
promises and async/await](https://github.com/reactjs/rfcs/pull/229).

We might explore using these package for other instrumentation in the
future but not now and not like this.
2022-10-27 17:52:53 -04:00
Sebastian Markbåge 28a574ea8f Try assigning fetch to globalThis if global assignment fails (#25571)
In case it's a more modern yet rigid environment.
2022-10-27 02:43:17 -04:00
Josh Story 09def5990b [Float] handle noscript context for Resources (#25559)
stacked on https://github.com/facebook/react/pull/25569

On the client noscript already never renders children so no resources
will be extracted from this context. On the server we now track if we
are in a noscript context and turn off Resource semantics in this scope
2022-10-26 23:11:31 -07:00
Josh Story 17204056d5 [Float] fix coordination of resource identity and hydration (#25569)
there are a few bugs where dom representations from SSR aren't
identified as Resources when they should be.

There are 3 semantics

Resource -> hoist to head, deduping, etc...
hydratable Component -> SSR'd and hydrated in place
non-hydratable Component -> never SSR'd, never hydrated, always inserted
on the client

this last category is small
(non stylesheet) links with onLoad and/or onError
async scripts with onLoad and/or onError

The reason we have this distinction for now is we need every SSR'd async
script to be assumable to be a Resource. we don't currently encode
onLoad on the server and so we couldn't otherwise tell if an async
script is a Resource or is an async script with an onLoad which would
not be a resource. To avoid this ambiguity we never emit the scripts in
SSR and assume they need to be inserted on the client.

We can explore changes to these semantics in the future or possibly
encode some identifier when we want to opt out of resource semantics but
still SSR the link or script.
2022-10-26 19:53:29 -07:00
Robert Balicki fecc288b7d [react devtools] Device storage support (#25452)
# Summary
* This PR adds support for persisting certain settings to device
storage, allowing e.g. RN apps to properly patch the console when
restarted.
* The device storage APIs have signature `getConsolePatchSettings()` and
`setConsolePatchSettings(string)`, in iOS, are thin wrappers around the
`Library/Settings` turbomodule, and wrap a new TM that uses the `SharedPreferences` class in Android.
* Pass device storage getters/setters from RN to DevTools'
`connectToDevtools`. The setters are then used to populate values on
`window`. Later, the console is patched using these values.
* If we receive a notification from DevTools that the console patching
fields have been updated, we write values back to local storage.
* See https://github.com/facebook/react-native/pull/34903

# How did you test this change?
Manual testing, `yarn run test-build-devtools`, `yarn run prettier`,
`yarn run flow dom`

## Manual testing setup:

### React DevTools Frontend
* Get the DevTools frontend in flipper:
* `nvm install -g react-devtools-core`, then replace that package with a
symlink to the local package
  * enable "use globally installed devtools" in flipper
* yarn run start in react-devtools, etc. as well

### React DevTools Backend
* `yarn run build:backend` in react-devtools-core, then copy-paste that
file to the expo app's node_modules directory

### React Native
* A local version of React Native can be patched in by modifying an expo
app's package.json, as in `"react-native":
"rbalicki2/react-native#branch-name"`

# Versioning safety
* There are three versioned modules to worry about: react native, the
devtools frontend and the devtools backend.
* The react devtools backend checks for whether a `cachedSettingsStore`
is passed from react native. If not (e.g. if React Native is outdated),
then no behavior changes.
* The devtools backend reads the patched console values from the cached
settings store. However, if nothing has been stored, for example because
the frontend is outdated or has never synced its settings, then behavior
doesn't change.
* The devtools frontend sends no new messages. However, if it did send a
new message (e.g. "store this value at this key"), and the backend was
outdated, that message would be silently ignored.
2022-10-25 13:25:33 +11:00