Commit Graph

1775 Commits

Author SHA1 Message Date
Lauren Tan 5bf2323880 Fix missing playground snapshot
ghstack-source-id: f2c8a542b525ab8c91beaad6327c128a96d122f6
Pull Request resolved: https://github.com/facebook/react-forget/pull/2827
2024-04-08 11:23:29 -04:00
Lauren Tan 9a6cb24150 [ez] Fix comments to be star blocks
ghstack-source-id: 6a9e844a3ba548d3bf2b7373dc023ad42c2e719b
Pull Request resolved: https://github.com/facebook/react-forget/pull/2826
2024-04-08 10:55:55 -04:00
Lauren Tan 0d0f7c2ad1 Update ValidatePreservedManualMemoization severity to
CannotPreserveMemoization

We do need to fix the error location to point to the "callsite" rather
than the definition of the useMemo callback, but that aside, even if the
error message were perfect, it's not meant to be actionable to the user.

So let's change the severity to CannotPreserveMemoization. This
preserves the validation, but the eslint plugin won't report it.

ghstack-source-id: 722c88922884de05e89030a7b001bd93e0a2a114
Pull Request resolved: https://github.com/facebook/react-forget/pull/2825
2024-04-08 10:55:54 -04:00
Lauren Tan 1f35f7e320 Add new ErrorSeverity.CannotPreserveMemoization
This adds a new category of error where the compiler cannot preserve
memoization exactly how as it was originally authored. We're adding a
new category here because it's not an actionable error, and allows us to
more specifically control whether it's reportable or not.

ghstack-source-id: 9693cd42ca64b980248c6202091bdd4c827e1cd4
Pull Request resolved: https://github.com/facebook/react-forget/pull/2824
2024-04-08 11:00:41 -04:00
Jan Kassens 76bb13cd26 First attempt at making the linter work with advanced TypeScript syntax
First attempt at making the linter work with advanced TypeScript syntax 

Falls back to the babel parser for some advanced syntax like string template 
syntax. 

This is pretty hacky as it doesn't take in any parsing options that are 
configured for the outer ESLint parser, not sure how that could be handled.
2024-04-04 18:31:31 -04:00
Lauren Tan 9a9e03b915 Add playground e2e to ci
This only runs on main since it takes a while to run (about ~4 mins)
2024-04-03 18:12:30 -04:00
Lauren Tan 07a3404c5f Add basic playwright e2e test to playground 2024-04-03 18:12:29 -04:00
Lauren Tan ee24e3fc16 Dogfood uMC polyfill in playground 2024-04-03 18:12:29 -04:00
Joe Savona f33e63838d Update copyrights to reference Meta instead of Facebook
Thanks @zpao!!! This was mostly his work, i just fixed up the last bit.
2024-04-03 08:43:36 -07:00
Joe Savona 0846daaa54 Commit message script captures non-ghstack messages
Tested by manually passing in the commit message from 
https://github.com/facebook/react-forget/commit/1028504dc809d29992954553d34cf6f55a1fefad 
and verifying that it grabbed the description from GH.
2024-04-02 20:55:41 -07:00
Joe Savona 852301828b Remove reactive_ir crate 2024-04-02 21:03:25 -07:00
Joe Savona 8a43e954a2 s/Forget/React Compiler/ in Rust port 2024-04-02 16:49:31 -07:00
Joe Savona 5ec3f02ee5 Rename docs/architecture.md to design_goals per contents 2024-04-02 16:05:11 -07:00
Joe Savona 90dd2084e2 ValidateNoCapitalizedCalls checks capitalized methods 2024-04-03 08:02:40 -07:00
Joe Savona 4c1aae4f3b [be] Cleanup removed directories from prettier script 2024-04-03 08:02:39 -07:00
Joe Savona 94156d7014 Repro for unmemoized value due to assignment to a context variable 2024-04-03 08:02:38 -07:00
Joe Savona 284f776420 [be] Clean up already fixed "bug" fixtures
These are already fixed, renamed to clarify that they are just repros of 
previous bugs
2024-04-02 12:43:44 -07:00
Joe Savona a53f208f5e Fixture for conditional use()
This is mostly to ensure we don't flag this as a conditional hook, but since i'm 
here i made it a proper test.
2024-04-02 11:36:11 -07:00
Joe Savona 8b9edafa95 Support use operator
Implements support for use: 

* Teaches InferReactivePlaces to treat use() result as reactive 

* Teaches FlattenScopesWithHooks to also flatten scopes with use() 

Handles both `use()` and `React.use()`.
2024-04-02 11:28:01 -07:00
Lauren Tan e61f983264 Fix yarn dev 2024-04-02 12:18:12 -04:00
Lauren Tan 5485ed0855 Standardize on banner2 plugin 2024-04-02 12:13:41 -04:00
Lauren Tan f01f74d9e1 Also build react-forget-runtime with rollup
This adds rollup to the runtime and adds a new plugin to add the license banner 
+ inject the `"use no memo"` directive. We need to inject it there as rollup 
currently strips out unknown directives during bundling.
2024-04-02 12:13:40 -04:00
Lauren Tan b4eeae6f5d Share rollup packages across workspace 2024-04-02 12:13:39 -04:00
Lauren Tan 91826d4924 Configure rollup to strip comments and whitespace
For now this configures rollup to strip out comments in DEV builds and 
whitespace. Unfortunately there's no easy way to do this in just terser alone or 
other minifiers/manglers, so I had to add prettier as well to re-format the 
minified code. This does make the build a little bit slower: 

``` before: yarn build  118.96s user 12.38s system 185% cpu 1:10.81 total after: 
 yarn build  121.55s user 12.90s system 183% cpu 1:13.17 total ``` 

Eventually I would like to have a similar setup to React's rollup config where 
we can have DEV and prod builds. After the repo merge we could probably share or 
reuse bits of React's rollup config.
2024-04-02 12:13:38 -04:00
Joe Savona f0806c43fe Remove flag for enableEarlyReturnInReactiveScopes (always enable) 2024-04-01 15:37:03 -07:00
Joe Savona ef285e0702 Remove memoizeJsxElements flag 2024-04-01 15:34:42 -07:00
Joe Savona b00300c4cf Remove disableAllMemoization flag 2024-04-01 15:18:59 -07:00
Joe Savona 69f03dc4c4 Remove unused environment flags
These flags aren't used anymore
2024-04-01 15:16:31 -07:00
Sathya Gunasekaran cd3f56bb18 [eslint] Enforce generic array type syntax
Fix and enforce generic array type syntax
2024-04-02 15:31:25 +01:00
Sathya Gunasekaran 1de6c5aea2 [eslint] Don't lint generated files
drive by fix multi line comments in .eslintrc
2024-04-02 12:03:26 +01:00
Lauren Tan ba661c80f3 Remove comments in builds 2024-04-02 10:32:24 -04:00
Lauren Tan 4e6aa42773 Upgrade to typescript 5.4.3
This PR makes all packages share the same typescript version and updates us to 
latest versions of typescript, ts-node, typescript-eslint/eslint-plugin and 
typescript-eslint/parser. 

I also noticed that the tsconfig we were extending (node18-strictest) was 
deprecated, so I switched us over to one that's more up to date. 

Also had to make a couple of small changes to the playground so that continues 
to build correctly.
2024-04-02 10:32:23 -04:00
Lauren Tan 5516cbb708 [ez] Fix unneeded path in copyright script 2024-04-02 10:32:22 -04:00
Lauren Tan 0af58d6c9e [ez] Clean up unused test filters 2024-04-02 10:32:21 -04:00
Sathya Gunasekaran d5b6e584fb [hir] Add support for directives
Previously, we would drop directives inside a component or hook but this is 
problematic with reanimated which uses `'worklet'` to mark components from 
compilation. 

This PR adds a directive to HIRFunction and ReactiveFunction and codegens the 
directive add the end. No processing is done on the directives themselves. 

Babel seems to store the directives on a BlockStatement, rather than on the 
Function but I've stored it on the Function types because we only support 
compiling functions and the spec defines directives as occuring in the initial 
statement list of a function: > A Directive Prologue is the longest sequence of 
ExpressionStatements > occurring as the initial StatementListItems or 
ModuleItems of a > FunctionBody, a ScriptBody, or a ModuleBody and where each > 
ExpressionStatement in the sequence consists entirely of a > StringLiteral token 
followed by a semicolon.
2024-04-02 11:25:10 +01:00
Joe Savona 1c3313707f ReactiveTerminal: add loc and id to all variants 2024-04-01 15:03:47 -07:00
Joe Savona 832285fed2 Only create mutable ranges for phis *mutated* later
This PR was the result of a long chain of ~yak-shaving~ debugging kicked off as 
a result of fixing up invariants. Where this started was that i noticed some 
cases of loops where the first instance we saw of a reactive scope was after its 
starting instruction. Eg instruction N would have an operand with scope 
Start:End, where Start was _before_  N. One of the cases involved a phi with a 
backedge. Then i noticed that we assign scopes differently for phis with and 
without backedges: 

``` 

[1] let x0 = init; 

[2] if (x0 < limit) { 

[3] x1 += increment; 

} 

x2 = phi(x0, x1); 

[4] x2; 

``` 

The phi isn't mutated _or_ reassigned after its creation, so we don't assign a 
mutable range to the phi or any of its operands. We also don't create a scope 
for `x`. 

But change the `if` to a `while` and now the phi moves - now there's a backedge: 

``` 

[1] let x0 = init; 

[2] while (x0 < limit) { 

x2 = phi(x0, x2); // now this is "mutated" later!!! 

[3] x2 += increment; 

} 

[4] x2; 

``` 

What was happening here is that x2 has a mutable range which is "after" the phi 
instruction, so it would appear that the phi was actually being mutated later. 
Ie, this was treated equivalently to the original "if" version, but with a 
mutation: 

``` 

let x = []; 

if (cond) { 

x = {}; 

} 

mutate(x); // later mutation of the phi 

``` 

But these latter two cases are different! We only need to (should) create a 
mutable range for a phi _if its value is actually mutated_. If it's just being 
reassigned, well then it shouldn't matter if there are back edges or not. 

So this PR implements that intuition: only create a mutable range for a phi if 
it is actually _mutated_ later, ie don't assign a mutable range if it is only 
_reassigned_ later. Concretely in InferMutableRanges: 

* InferMutableLifetimes no longer has to initialize a range for phis during the 
first pass (inferMutableRangesForStores=false). We wait to see if the phi is 
mutated during the main fixpoint iteration of InferMutableRanges 

* The main fixpoint iteration in InferMutableRanges already aliases phi operands 
if the phi is later mutated, which will extend the end of the mutable range of 
all the operands accordingly. 

* Finally, InferMutableLifetimes's second run 
(inferMutableRangesForStores=true), we ensure that any phis mutated later have a 
valid mutable range, specifically setting the `start` of the range since the 
fixpoint only updates the `end` value.
2024-04-01 14:21:48 -07:00
Lauren Tan 8d2cd323a4 Include package.json when bundling for oss
This was causing issues for an external partner's build pipeline which expected 
each package to have its own package.json.
2024-03-29 12:19:04 -04:00
Mofei Zhang 5656d5f078 [config] Change compiler defaults to prepare for oss 2024-03-29 09:31:09 -04:00
Lauren Tan 9d20aa2819 Don't compile node_modules on playground 2024-03-28 10:46:56 -04:00
Lauren Tan 7748ce8f3f Update compiler naming in remaining error text 2024-03-28 10:40:05 -04:00
Lauren Tan df5ff9753a Fix jest script 2024-03-28 10:40:05 -04:00
Lauren Tan f4229cdb7f Rename error type 2024-03-28 10:40:04 -04:00
Lauren Tan 638f8e3ddc Remove [ReactForget] prefix from error messages 2024-03-28 10:40:03 -04:00
Mofei Zhang 5166869204 [patch] Fix control flow bug in PropagateScopeDeps
A dependency D from either an instruction or scope is poisoned if there may be a 
(non-linear) jump instruction between it and the start of its immediate parent 
scope. Poisoned dependencies are added as conditional dependencies to their 
parent scope. 

(done: reduce false positives in scopes that begin after return/throw) (done: 
fix bugs in recording and joining exhaustive conditional deps) (done: flesh out 
commit message, clean up PR, add more fixtures) 

--- \## Bug details: 

Take a simple example: ```js target: {   instrA;   if (...) {     instrB;     
break target;   } else {     instrC;   }   instrD;   // ... } instrE; // ... ``` 

This diagram shows how we represent this program in the reactive IR. - Blocks 
are represented as a list of nodes. - Green nodes show instructions and value 
blocks (simplified as a single instruction). - Pink nodes show terminals, which 
transfer control to a subtree of nodes. <img width="450" alt="image" 
src="https://github.com/facebook/react-forget/assets/34200447/930789f2-39cd-4ea8-b12a-530042807b46"> 

Prior to this PR, PropagateReactiveScopeDeps was incorrect because it assumed 
that a block's instructions are evaluated unconditionally (which is how HIR 
basic blocks work). E.g. if a reactive scope enclosed `block 1`, we assume that 
`instrA` and `instrD` both will evaluate unconditionally. 

This failed to account for `jump` instructions like break, continue, return, and 
throw. This may result in invalid hoisting of PropertyLoads (i.e. Forget output 
may throw when source does not throw). Note that other terminals (e.g. if and 
loops) are not affected as they are self contained subtrees that evaluate 
sequentially. 

With the changes in this PR, we mark `block 1` as poisoned upon encountering the 
`break` instruction. While `block 1` is active and poisoned, it will determine 
how visited dependencies are added. 

Here, added solid lines show unconditional dependencies, dashed lines show 
conditionally accessed dependencies: - dependencies from `instrB, instrC` are 
conditional because they are within conditional subtrees - dependencies from 
`instrD` are conditional because it is within a poisoned block within its parent 
scope. 

<img width="450" alt="image" 
src="https://github.com/facebook/react-forget/assets/34200447/81980f68-7e65-4bd7-ba94-3f0c26550e5c"> 

--- Recapping an offline discussion with @josephsavona: this pass would really 
benefit from operating on HIR. The minimal work needed for this pass to run on 
HIR is to rewrite and reorder  `AlignReactiveScopesToBlockScopes` to operate on 
HIR. 

The following diagram shows what HIR blocks look like for the same code. 
Evaluating hoistable PropertyLoad dependencies for a scope enclosing 
`instr{A-D}` is much simpler:  just evaluate whether the PropertyLoad evaluates 
for every path between `bb0` and `bb4`. <img width="250" alt="image" 
src="https://github.com/facebook/react-forget/assets/34200447/44b38939-defb-4b29-878d-4445ec6ccc06"> 

---
2024-03-27 20:26:18 -04:00
Mofei Zhang b98b569017 [DeriveMinimalDeps] Account for conditional / poisoned accesses within
conditionals 

This change is needed for #2752. To minimize renaming `error.fixture` -> 
`fixture` files, I'm reordering this PR to earlier in the stack. 

Prior to the fix in #2752, we only expected unconditional accesses within 
`depsInCurrentConditional`, which records instructions directly within a 
conditional block (not including nested conditional blocks).
2024-03-27 20:26:18 -04:00
Mofei Zhang 2782fa2664 [rhir] Preserve block labels + target blockIds for break/continue terminals
RFC: we can either retain break/continue target ids (instead of pruning them in 
`buildReactiveFunction`) or re-implement the same logic in `propagateScopeDeps` 
(ignore implicit breaks; match unlabeled break / continues to their closest loop 
/ while parent terminal). 

If we go ahead with this approach, I'll clean up this PR (add relevant types and 
comments)
2024-03-27 20:26:17 -04:00
Mofei Zhang 877c490fa3 [visitors] Visit loop body before update 2024-03-27 20:26:16 -04:00
Mofei Zhang 1515cb3218 [be][sprout] Add ErrorBoundary to test exceptions in sequentialRenders 2024-03-27 20:26:15 -04:00
Mofei Zhang 4d37a599e3 [be][tests] Move PropagateScopeDep fixtures to separate directory 2024-03-27 20:26:14 -04:00