Commit Graph
1126 Commits
Author SHA1 Message Date
Lauren Tan 74aec8aa35 [ez] Add concurrently labels to yarn dev 2023-08-17 12:01:42 -04:00
Lauren Tan 9c2ce7a8c8 Try running tests for babel-plugin-react-forget concurrently
Unscientifically seems to be a little faster. I made sure to preserve the 
ordering of the logs even though the processes will run concurrently, so the ci 
output should still be readable 

![Screenshot 2023-08-17 at 11 51 45 
AM](https://github.com/facebook/react-forget/assets/1390709/b9878983-ed0c-4b93-bc10-61109ff8103d)
2023-08-17 11:58:15 -04:00
Mofei Zhang 5448b33eb0 [yarn workspaces] Add typescript dependencies to tsconfig
--- 

Previously, I used `yarn workspace build fixture-test-utils` and `yarn workspace 
build babel-plugin-react-forget` to recompile dependencies on sprout. However, 
tsc supports project references which was intended to solve this problem 
https://www.typescriptlang.org/docs/handbook/project-references.html 

Note that apparently this places `.tsbuildinfo` in package roots, according to 
https://github.com/microsoft/TypeScript/issues/30925
2023-08-16 15:38:30 -04:00
Mofei Zhang d6dea6b9b9 [sprout][fixtures] Annotate some fixtures
--- 

This PR is an example of how to update fixture to add sprout annotations. 

Running with `yarn sprout --verbose --filter` shows the fixture outputs. 

``` 

PASS  array-access-assignment 

ok [[2],[[2],[],[3]]] 

PASS  array-expression-spread 

ok [0,1,2,3,null,4,5,6,"z"] 

PASS  array-map-frozen-array 

ok [[],[]] 

PASS  array-map-mutable-array-mutating-lambda 

ok [[],[]] 

PASS  array-pattern-params 

ok [{"a":"val1"},{"b":"val2"}] 

PASS  array-properties 

ok {"a":[[1,2],2,"hello"],"x":3,"y":"[[ function params=1 ]]","z":"[[ function 
params=1 ]]"} 

PASS  array-property-call 

ok {"a":[1,2,"hello",42],"x":4,"y":1} 

PASS  assignment-expression-computed 

ok [7] 

PASS  assignment-expression-nested-path 

ok {"b":{"c":6}} 

PASS  capturing-func-mutate-2 

ok {"a":2} 

PASS  capturing-function-alias-computed-load-2 

ok "val2" 

PASS  capturing-function-alias-computed-load-4 

ok "val2" 

```
2023-08-16 15:38:29 -04:00
Mofei Zhang 9d1a842a83 [sprout] Enable sprout on all new fixtures
--- 

Rename `SproutOnlyFilterTodoRemove.ts` to `SproutTodoFilter.ts`. I've tried to 
group the skipped fixtures by difficulty and add comments about what need to be 
done, also happy to change the structure of this. 

From this point, sprout will run on all new fixtures by default. If the fixture 
forgets to export a `FIXTURE_ENTRYPOINT`, sprout will fail with this error. 

<img width="853" alt="Screenshot 2023-08-15 at 5 59 32 PM" 
src="https://github.com/facebook/react-forget/assets/34200447/0f80d650-1dc1-4df4-9710-e49acbb424b0"> 

See #1961 for an example of how to annotate existing skipped fixtures or new 
ones. The `README.md` is also updated with a guide.
2023-08-16 15:38:28 -04:00
Mofei Zhang c4ae2a48dc [sprout] patch bug + add verbose mode
--- 

``` 

yarn sprout --verbose 

``` 

Verbose mode prints out all outputs of tests. The test output is a status (`ok` 
or `exception`), a returned or thrown value, and a set of console logs. 

Currently as of #1960 , this is the output: 

```sh 

$ yarn workspace babel-plugin-react-forget run build && node 
../sprout/dist/main.js --verbose 

$ rimraf dist && tsc 

PASS  alias-nested-member-path 

ok {"y":{"z":[]}} 

PASS  assignment-variations-complex-lvalue 

ok {"y":{"z":4}} 

PASS  assignment-variations 

ok 1 

PASS  chained-assignment-expressions 

ok {"z":null} 

PASS  computed-call-evaluation-order 

ok {"f":"[[ function params=0 ]]"} [ 

"A", 

"B", 

"arg", 

"original" 

] 

PASS  const-propagation-into-function-expression-primitive 

ok 42 [ 

"42" 

] 

PASS  constant-propagation-for 

ok 0 

PASS  constant-propagation-while 

ok 0 

PASS  constant-propagation 

ok -6 [ 

"foo" 

] 

PASS  controlled-input 

ok <input value="0"> 

PASS  do-while-continue 

ok [1.5,1,0.5] 

PASS  do-while-simple 

ok [6,4,2] 

PASS  expression-with-assignment 

ok 5 

PASS  for-of-break 

ok [] 

PASS  for-of-conditional-break 

ok [] 

PASS  for-of-continue 

ok [0.5,1,1.5] 

PASS  for-of-destructure 

ok [0,2,4] 

PASS  for-of-simple 

ok [0,2,4] 

PASS  function-declaration-reassign 

ok {} 

PASS  function-declaration-redeclare 

ok "[[ function params=0 ]]" 

PASS  lambda-reassign-primitive 

ok 41 

PASS  lambda-reassign-shadowed-primitive 

ok {} 

PASS  property-call-evaluation-order 

ok {"f":"[[ function params=0 ]]"} [ 

"A", 

"arg", 

"original" 

] 

PASS  reactive-scope-grouping 

ok {"y":[{}]} 

PASS  sequentially-constant-progagatable-if-test-conditions 

ok "ok" 

PASS  simple-function-1 

ok "[[ function params=1 ]]" 

PASS  ssa-complex-multiple-if 

ok 

PASS  ssa-complex-single-if 

ok 

PASS  ssa-for 

ok 11 

PASS  ssa-if-else 

ok 

PASS  ssa-objectexpression-phi 

ok {"x":1,"y":3} 

PASS  ssa-property-call 

ok {"x":[[]]} 

PASS  ssa-property 

ok {"x":[]} 

PASS  ssa-return 

ok 2 

PASS  ssa-simple-phi 

ok 

PASS  ssa-simple 

ok 

PASS  ssa-single-if 

ok 

PASS  ssa-switch 

ok 

PASS  ssa-throw 

exception undefined 

PASS  ssa-while 

ok 10 

PASS  type-field-load 

ok 1 

PASS  type-test-field-store 

ok {} 

PASS  type-test-primitive 

ok 2 

PASS  update-expression-constant-propagation 

ok {"a":0,"b":0,"c":2,"d":2,"e":0} 

44 Tests, 44 Passed, 0 Failed 

✨  Done in 9.27s. 

```
2023-08-16 15:32:15 -04:00
Mofei Zhang 9d1f2c86ed [sprout] Codemod: add todos for test fixtures
--- 

Codemod to add an `export FIXTURE_ENTRYPOINT` with todo values for params for 
tests with at least one param and 0 refs to external values
2023-08-16 15:32:15 -04:00
Mofei Zhang 43e3cd61f8 [snap] Use sync fs apis to stop crashes
--- 

Not sure why, but snap kept silently crashing when I used fs/promises to write 
to many file handles. I tried a `.catch(...)`, but couldn't figure it out. This 
diff changes snap to use sync fs apis to avoid crashes, but I'd love to get 
feedback if someone knows how to debug this.
2023-08-16 15:32:14 -04:00
Mofei Zhang ce015a164b [sprout] Codemod: add isComponent: false annotations to current fixtures
To keep the type signature simple, let's always have the same set of keys in 
`FIXTURE_ENTRYPOINT`
2023-08-16 15:32:13 -04:00
Lauren Tan 6b242c12a9 [ci] Don't run yarn build in ci
Can't recall why we added this, but it shouldn't be necessary and should shave 
off ~2 mins from ci
2023-08-16 12:16:51 -04:00
Lauren Tan a59e15d40f [ci] Try to cache node_modules to speed up ci 2023-08-16 11:04:53 -04:00
Sathya Gunasekaran b968d4da48 [hir] Validate globals are equal before propagating
Make sure the value of the globals in phi operands are the same before constant 
propagating them.
2023-08-16 15:39:01 +01:00
Sathya Gunasekaran b686b5dd9d [hir] Refactor constant propagation of phis
This PR moves the phi evaluation to a separate function. 

Most importantly, it inverts the default case to _not_ constant propagate unless 
we have explicit validation of the phi operands.
2023-08-16 15:39:00 +01:00
Sathya Gunasekaran c7210bac93 [test] Add failing test for constant propogation of globals as phi operands 2023-08-16 15:39:00 +01:00
Mofei Zhang a418e35bf9 [sprout] QoL: use cli args, report pretty results
--- 

- Added sprout to Github Actions by updating `yarn test` command 

- Added cli args (`filter` and `sync`) 

- use chalk to make results nicer ✨ 

Tested locally: 

<img width="900" alt="Screenshot 2023-08-14 at 6 04 28 PM" 
src="https://github.com/facebook/react-forget/assets/34200447/b5481bbc-6a50-40c6-a85f-d2443130dd2a">
2023-08-14 19:26:05 -04:00
Mofei Zhang d2983888f4 [sprout] Initial draft of sprout 🌱 test runner
--- 

## Sprout 🌱 Overview 

**(Overview copied from 
[sprout/README.md](https://github.com/facebook/react-forget/blob/0468ddf8bbee54a9394317a667ea4ed96bb3bef3/forget/packages/sprout/README.md))** 

React Forget test framework that executes compiler fixtures. 

Currently, Sprout runs each fixture with a known set of inputs and annotations. 
We hope to add fuzzing capabilities to Sprout, synthesizing sets of program 
inputs based on type and/or effect annotations. 

Sprout is currently WIP and only executes files listed in 
`src/SproutOnlyFilterTodoRemove.ts`. 

### Milestones: 

- [✅] Render fixtures with React runtime / `testing-library/react`. 

- [ ] Make Sprout CLI -runnable and report results in process exit code. 

After this point: 

- Sprout can be enabled by default and added to the Github Actions pipeline. 

- `SproutOnlyFilterTodoRemove` can be renamed to `SproutSkipFilter`. 

- All new tests should provide a `FIXTURE_ENTRYPOINT`. 

- [ ] Annotate `FIXTURE_ENTRYPOINT` (fn entrypoint and params) for rest of 
fixtures. 

- [ ] Edit rest of fixtures to use shared functions or define their own helpers. 

- [ ] *(optional)* Store Sprout output as snapshot files. i.e. each fixture 
could have a `fixture.js`, `fixture.snap.md`, and `fixture.sprout.md`. 

### Constraints 

Each fixture test executed by Sprout needs to export a `FIXTURE_ENTRYPOINT`, a 
single function and parameter set with the following type signature. 

```js 

type FIXTURE_ENTRYPOINT<T> = { 

// function to be invoked 

fn: ((...params: Array<T>) => any), 

// params to pass to fn 

params: Array<T>, 

// true if fn should be rendered as a React Component 

//  i.e. returns jsx or primitives 

isComponent?: boolean, 

} 

``` 

Example: 

```js 

// test.js 

function MyComponent(props) { 

return <div>{props.a + props.b}</div>; 

} 

export const FIXTURE_ENTRYPOINT = { 

fn: MyComponent, 

params: [{a: "hello ", b: "world"}], 

isComponent: true, 

}; 

``` 

--- 

## Implementation Details 

- jest-worker test orchestrator (similar to Snap 🫰). 

I chose to write a test runner instead of directly using Jest for flexibility 
and speed. 

- Sprout 🌱 currently runs much more code per fixture (2 babel transform 
pipelines + 2 `exec(...)` than snap, so all scaling concerns apply. 

- Sprout may need more customization in the future (e.g. fuzzing component 
inputs, caching artifacts) 

- We probably want to add snapshot files for Sprout, which is much easier with a 
custom runner. 

This is also one of the main reasons we wrote Snap. Jest consolidates all 
external snapshots (i.e. non-inline snapshots) from a test into [a single 
file](https://github.com/facebook/react-forget/blob/d0a006ffa9842ee9db6b23ada94622af901eb07f/forget/src/__tests__/__snapshots__/compiler-test.ts.snap). 
This was painful mainly for rebasing changes, but also for small papercuts (e.g. 
needing to run `yarn test -u` twice when deleting a fixture) 

- Currently does not save output to snapshot file, but we can easily add this 
later (i.e. each test would have a `.snap.md` and `.sprout.md` file) 

- Supports filter mode (same testfilter.txt file as snap) 

- Currently does not support watch mode. I expect that sprout's primary use will 
be catching bugs in the PR / Github Actions phase. Can be changed if we need to 
iterate on sprout output while developing. 

- All tests are run with `react-test-renderer` to access the React Runtime, 
which is needed for calls to `useMemoCache` (and other potential hooks). 

- react-test-renderer required a mocked DOM, so I ~~used js-dom~~ did a terrible 
js-dom hack to add document, window, and other browser globals to the 
jest-worker globals, then exec the test code in the jest worker global. 

I can clean this up later by bundling library code (e.g. react, 
react-test-renderer) to not use `require(...)`, then calling `exec` on all "test 
client code" in the js-dom mock global (instead of the real jest worker one) 

- Tests marked `isComponent` need to return valid jsx or primitives. Values 
returned by all other tests are converted to a string primitive via 
JSON.stringify. 

```sh 

# install new dependencies to node_modules 

$ yarn 

$ cd forget/packages/babel-plugin-react-forget 

$ yarn sprout:build && yarn sprout 

PASS  alias-nested-member-path 

PASS  assignment-variations-complex-lvalue 

PASS  assignment-variations 

PASS  chained-assignment-expressions 

PASS  computed-call-evaluation-order 

PASS  const-propagation-into-function-expression-primitive 

PASS  constant-propagation-for 

PASS  constant-propagation-while 

PASS  constant-propagation 

... 

✨  Done in 3.91s. 

```
2023-08-14 19:26:02 -04:00
Mofei Zhang 904ea6c9e4 [sprout] codemod for tests that can be run out of the box 2023-08-14 19:26:02 -04:00
Mofei Zhang 5669fc73c0 [snap] Refactor fixture utils to separate package
--- 

Move shared functions to `fixture-test-utils` package.
2023-08-14 19:26:01 -04:00
Mofei Zhang 02e8cf9790 [snap][easy] typecheck forget babel plugin options 2023-08-14 19:26:01 -04:00
Joe Savona 45d884bb6b Fix LeaveSSA missing param declarations
Nice find from @mofeiz, we weren't adding declarations for function params in 
LeaveSSA. This caused us to hit an invariant for update expressions that updated 
params which assumed that all named variables had a declaration. This is why 
it's helpful to add invariants, it helps you find places where you violate them 
:-)
2023-08-14 15:48:05 -07:00
Joe Savona cb2ba7118f [rust][sema] Support hoisting semantics
Adds basic support for hoisting semantics: * Resolution of variable references 
is _always_ deferred in case the correct binding   hasn't been seet yet due to 
hoisting. * Var and function declarations bubble to the appropriate scope 

There are lots of subtleties that aren't implemented yet but these rules cover a 
lot.
2023-08-14 12:24:05 -07:00
Joe Savona 011570a078 [rust][sema] Bubble unresolved references (for hoisting)
This is a precursor to adding support for hoisting in semantic analysis. 
Previously when we encountered an unknown reference we immediately reported an 
error. But hoisted variables may be referenced before they're defined, so we 
don't know for sure when we see an unknown variable if its actually unbound or 
not. 

This PR adds the first part of hoistingn support: rather than immediately report 
an error when encountering an unbound variable we store it in a list of 
unresolved references on the current scope. As we close each scope we recheck 
and see if the variable can now be resolved. If yes we record that, otherwise we 
bubble up the unresolved reference to the parent scope (and try again there). 

The next PR(s) will handle hoisting of `var` and other syntax to the apropriate 
nearest scope boundary (function/module).
2023-08-14 10:08:02 -07:00
Joe Savona 57f648009c [rust] Cleanup visitors 2023-08-14 09:54:29 -07:00
Joe Savona e248534184 [rust] SSA fixes after operand refactor
When updating the data model for operands from instruction indices to 
identifiers, I forgot to rewrite terminal operands.Doing so required a refactor 
to use the new BlockRewriter helper.
2023-08-14 09:54:26 -07:00
Joe Savona a49335f72a [rust] Support destructuring
Adds a `Destructure` instruction closely following the design of the TS-based 
compiler. The main change is fairly small: the TS compiler doesn't support rest 
spreads that are not identifiers, such as the `...y[]` in `const [x, ...[y]] = 
z`. I added support for this in the Rust compiler.
2023-08-11 15:13:21 -04:00
Lauren Tan 298bf55f25 Address feedback 2023-08-14 12:05:52 -04:00
Lauren Tan 3ec95c531b [be] More babel plugin Program cleanup
Give a more descriptive name
2023-08-11 16:39:03 -04:00
Lauren Tan 25e3575c38 [be] Move more logic into Imports module 2023-08-11 16:39:01 -04:00
Lauren Tan ac8d874711 [eslint-plugin] Silence babel warnings in test
be quiet Babel!
2023-08-11 16:38:59 -04:00
Lauren Tan d2990f1592 [be] Extract import logic to own module
Extracting logic around inserting import statements into its own module, with 
more descriptive function names
2023-08-11 16:38:57 -04:00
Lauren Tan ebd8ed5c71 [be] Extract gating logic to its own module
Just moving code around to another module.
2023-08-11 16:38:55 -04:00
Lauren Tan dd8b743f6a [be] Refactor gating logic in babel plugin
`compileProgram` was getting complex, so this extracts some of the logic into 
smaller functions. Additionally, the `try` block now only wraps the `compileFn` 
generator from Pipeline, which means not accidentally catching other non-Forget 
errors
2023-08-11 16:38:52 -04:00
Lauren Tan 6c719f62a0 [ez] ComponentDeclaration transforms only into FunctionDeclaration
ComponentDeclarations can only be transformed into FunctionDeclarations, so the 
types were wrong
2023-08-11 13:31:06 -04:00
Lauren Tan c3ec002d69 Invert shouldVisitNode logic 2023-08-10 17:24:22 -04:00
Lauren Tan ec44c4789b Add ComponentDeclaration util 2023-08-10 17:24:21 -04:00
Sathya Gunasekaran e70dfbb5a4 [babel] Lookup magic prop directly 2023-08-10 13:31:46 -04:00
Sathya Gunasekaran c1c35407ba [test] Test with non component declaration 2023-08-10 12:02:08 -04:00
Joe Savona c3ad8a86b1 [rust] Semantic analysis resolves break/continue 2023-08-10 10:59:51 -04:00
Joe Savona ee13c30d10 [rust] Sketch of ReactiveIR (prev ReactiveFunction)
Initial data types for `forget_reactive_ir`, which is the Rust analogue of 
`ReactiveFunction` in the TS compiler. I'm renaming here for clarity, though 
naming suggestions are very welcome!
2023-08-10 10:59:51 -04:00
Joe Savona 7c38b15078 [rust] Align instruction data model closer to JS
Aligns the data model for `Instruction` closer to JS: * Adds an `lvalue: 
IdentifierOperand` property (Identifier + Effect) * Changes rvalues from being a 
reference to the definining instruction by   instruction offset (InstrIx) to be 
a variable reference (IdentifierOperand) 

There are pros and cons to the previous offset-based approach. It's definitely 
convenient to be able to jump directly to the instruction that defined an 
operand value. However: * Many passes need to track things like basic 
reassignments, which mean they need to build   up mappings of IdentifierId to 
some data. When all operands are IdentifierOperands, this   can be a single 
mapping. * It aligns closer to JS, which makes porting a bit easier. 

But most of all: porting the `ReactiveFunction` data type — which is in tree 
form - is non-trivial with the offset-based approach. As we map HIR to the tree 
form, we'd have to remap every operand offset. Using identifiers for operands 
simplifies this. 

We can always revisit this design choice later.
2023-08-10 10:59:50 -04:00
Joe Savona cbb815e907 [rust] Port EliminateRedundantPhi fix 2023-08-10 10:59:50 -04:00
Joe Savona 74a062ba9d [rust] Autofix lints 2023-08-10 10:59:50 -04:00
Joe Savona b8f1d37b6a [rust] Remove SWC
After the previous PR we no longer depend on SWC for the critical path of 
development/testing. Notably this unblocks adding support for the rest of the 
language: the new `forget_hermes_parser` crate automatically converts Hermes 
Parser's AST into our `forget_estree` format via codegen. Achieving full 
language support with SWC would have required manually defining the remaining 
conversions for the rest of the language. 

Long-term we'll need to revisit how to integrate into SWC, and more generally 
into setups build atop SWC such as Next.js's Turbopack-based build 
configuration. But i'll delete for now since we don't depend on it for 
iteration, it's slow to build, and requires us to opt-in to nightly Rust.
2023-08-10 10:59:49 -04:00
Joe Savona 7feaabfe68 [rust] Use hermes parser for Forget fixture tests
Replaces the use of SWC parser in the Forget fixture tests with Hermes Parser. 
This includes aligning on a single type to represent JS Values and numbers 
(combining semi-duplicated code from forget_hir and forget_estree) and adding 
support for lowering babel-style 
NumericLiteral/BooleanLiteral/StringLiteral/NullLiteral node types (since Hermes 
Parser produces a mix of estree/babel node types)
2023-08-10 10:59:49 -04:00
Joe Savona 6bca9fb7a3 [rust] Update HIR builder to use new semantic analysis
Updates HIR builder to rely on the new semantic analysis instead of assuming 
that the ast nodes will already have binding info attached. That was a stopgap 
until we had our own name resolution :-) 

Once this lands we can remove SWC and switch everything to forget_hermes_parser, 
and also remove the non-spec Identifier.binding field (which stored the 
temporary name resolution data).
2023-08-10 10:59:48 -04:00
Sathya Gunasekaran 5d8587b81c Add option to compile ReactScript 2023-08-09 14:49:29 -04:00
Sathya Gunasekaran 27f38ec321 Add hermes-parser 2023-08-09 14:47:56 -04:00
Sathya Gunasekaran 6075d7ea34 Upgrade babel 2023-08-09 14:47:56 -04:00
Joe Savona 43677da7e4 Add instructions for UpdateExpression variants
Adds new instructions to accurately model UpdateExpression semantics, since 
`x++` is un-intuitively not the same as `x = x + 1`. There are a few different 
ways to model the combination of prefix/postfix and increment/decrement: 

* One instruction for all combinations of prefix/postfix and 
increment/decrement, eg 'UpdateExpression' 

* Instructions for Increment/Decrement, each with a property to distinguish 
prefix/postfix 

* Instructions for Prefix/Postfix, each with aproperty to distinguish 
increment/decrement. 

I chose the latter, `PrefixUpdate` and `PostfixUpdate`, because it keeps the 
number of new instructions minimal while keeping separate instructions for the 
most important distinction: whether the result of the instruction is the value 
before applying the operation or after. I'm open to suggestions about this 
though. 

A few quick notes: 

* Constant propagation is supported but only for numbers (we don't support 
bigint yet anyway) 

* LeaveSSA needs to know about these instructions since their presence requires 
making the original variable declaration Let, not Const. 

* EnterSSA mapped lvalues before rvalues, which is out of order but didn't 
previously matter. I just had to flip the order and everything worked.
2023-08-07 13:50:32 -07:00
Mofei Zhang e33c9c43cc [ssa] Patch: propagate every rewrite to function expressions
--- 

#1899 only propagated eliminated phi nodes to function expressions on the first 
iteration through blocks. However, this was buggy as later iterations could 
introduce rewrites that need to be propagated. 

[playground 
repro](https://0xeac7-forget.vercel.app/#eyJzb3VyY2UiOiJmdW5jdGlvbiBDb21wb25lbnQoKSB7XG4gIGNvbnN0IHggPSA0O1xuXG4gIGNvbnN0IGdldDQgPSAoKSA9PiB7XG4gICAgd2hpbGUgKGJhcigpKSB7XG4gICAgICBpZiAoYmF6KSB7XG4gICAgICAgIGJhcigpO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gKCkgPT4geDtcbiAgfTtcblxuICByZXR1cm4gZ2V0NDtcbn0ifQ==). 

I manually synced #1907 to check that this fix works for the VR Store codebase.
2023-08-07 17:41:35 -04:00