Commit Graph

1081 Commits

Author SHA1 Message Date
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
Mofei Zhang ff2f2e0685 [patch] Gate EliminatePhi fix behind feature flag 2023-08-07 17:41:34 -04:00
Sathya Gunasekaran adfae63f20 [hir] Remove unnecessary rewrite of instr.lvalue
The for loop over eachInstructionLValue already rewrites instr.lvalue: ```       
  for (const place of eachInstructionLValue(instr)) {           
rewritePlace(place, rewrites);         } ```
2023-08-07 13:41:59 -04:00
Joe Savona 461083d7bd [rust] Deprecate old manual ast visitor 2023-08-04 14:44:54 -07:00
Joe Savona ab5213e8f4 [rust][sema] Comments 2023-08-04 12:16:08 -07:00
Joe Savona 3df04725bd [rust][sema] For statements and JSX
Adds semantic analysis support for normal `for` statements and for JSX. The main 
catch with JSX is that there are a bunch of identifiers that we have to ignore 
since they aren't variable references: jsx attribute names, namespace names, jsx 
member expression properties, and closing elements.
2023-08-04 12:02:21 -07:00
Joe Savona 4756c5ab26 [rust][sema] Shared logic for for-in/for-of
Shares the code for for..in and for..of, along with a few other internal 
refactorings.
2023-08-04 12:02:18 -07:00
Joe Savona e6e2d9437e [rust] Improved name resolution
This is the start of an improved semantic analysis pass, reusing the 
ScopeManager added earlier in the stack but with new analysis built using the 
new visitor trait. The logic is a rough port of 


https://github.com/facebook/hermes/blob/main/tools/hermes-parser/js/hermes-eslint/src/scope-manager/referencer/Referencer.js 

Lots of bits are still missing, i'm starting with the parts that Forget needs.
2023-08-04 09:45:56 -07:00
Joe Savona c6ae535e94 [rust] Codegen AST visitor
Updates `estree` codegen to emit a Visitor trait (temporarily named `Visitor2` 
since there is a hand-rolled one that some code is using). We use knowledge of 
the grammar to only visit fields whose type is a Node or Enum, or an "object" 
type that opts into being visitable. The latter is used for Function and Class. 

This will make it much easier to write the semantic analyzer.
2023-08-03 17:00:02 -07:00
Joe Savona a67fefdebd [rust] Data structures for semantic analysis
This is two things: 

* A toy semantic analysis that handles a tiny subset of JS, including labeled 
statements, labeled break/continue, and variable 
declaration/reference/reassignment. This only exists as a way to prove out the 
API for the more important bit: 

* More importantly, this defines a data model for the semantic analysis results 
and an API for building up the semantic analysis. 

Subsequent diffs will replace the first bit (toy analysis impl), while keeping 
the second part.
2023-08-03 12:41:50 -07:00
Joe Savona 8042970ea4 [rust] Scaffolding for semantic analysis 2023-08-01 15:55:53 -07:00
Sathya Gunasekaran 3e9e3a1c92 [e2e] Add test for useState 2023-08-04 15:50:42 +01:00
Mofei Zhang 015029dc65 [patch] Fix for constant propagation bug in VR Store
--- 

Changes in `@enableOptimizeFunctionExpressions` caused a bug in the last Forget 
sync to VR Store. The repro can be summarized to something like this: 

```js 

function foo() { 

const x = true; // some constant or global 

// Add some branching for type inference 

// This can be a Logical expression as well (e.g. `4 || 5`) 

if (...) { } 

// In this HIR block, SSA inserts a `x$2 = phi(x$1, x$1)`. 

// EliminateRedundantPhiNodes needs to rewrite all references of `x$2` to `x$1` 

const accessXInLambda = () => x; 

return accessXInLambda; 

}
2023-08-02 17:30:57 -04:00
Mofei Zhang 50b0954279 [tests] Add typed CompilerOptions for e2e tests 2023-08-02 17:30:56 -04:00
Mofei Zhang 2a5a8d552f [tests] Revive Forget e2e tests
--- 

Revives e2e test infra from #587. 

- All React component-like functions are compiled. 

- `yarn jest` runs each e2e test twice (forget and no forget) 

Github Actions is already running `yarn test`, which includes all jest tests 

``` 

Run yarn test 

yarn run v1.22.19 

$ yarn workspaces run test 

> babel-plugin-react-forget 

$ yarn jest && yarn snap:build && yarn snap 

$ tsc && jest 

PASS main src/__tests__/Result-test.ts 

PASS main src/__tests__/DisjointSet-test.ts 

PASS e2e with forget src/__tests__/e2e/hello.e2e.js 

PASS e2e no forget src/__tests__/e2e/hello.e2e.js 

Test Suites: 
[4](https://github.com/facebook/react-forget/actions/runs/5732016200/job/15534129231?pr=1881#step:8:5) 
passed, 4 total 

Tests:       23 passed, 23 total 

Snapshots:   11 passed, 11 total 

Time:        
6.1[5](https://github.com/facebook/react-forget/actions/runs/5732016200/job/15534129231?pr=1881#step:8:6)3 
s 

```
2023-08-02 17:30:55 -04:00
Lauren Tan 2d8da0d32d [babel] Don't console.error on unknown options
Internally these are considered errors in our pipelines
2023-08-02 12:34:18 -04:00
Joe Savona 203d5ad43b [rust] ESTree cleanup 2023-08-01 13:55:19 -07:00
Joe Savona c09bfff04c [rust] Tests for HermesParser->ESTree conversion 2023-08-01 13:55:17 -07:00
Joe Savona e353f3702e [rust][wip] Codegen conversion from HermesParser (C++) 2023-08-01 13:55:13 -07:00
Joe Savona ce2725eda5 [rust] Very rough sketch of consuming Hermes parser results 2023-07-28 16:12:18 -07:00
Joe Savona b61a47eb2c Parser benchmark 2023-07-28 16:12:17 -07:00
Joe Savona 01ad9d895e [rust] Remove arena allocator
Using an arena allocator can be faster, but it comes with several challenges: 

* It requires tediously tagging nearly every value with a lifetime. Any function 
that has to deal with arena-allocated data (which is every meaningful function) 
ends up with a lifetime parameter. Bleh. We also have to thread the allocator 
itself wherever we need to allocate, though this is less of a problem since 
_most_ functions already need the Environment and we can store the allocator 
there. 

* There is not yet widespread support in the Rust ecosystem for using custom 
allocators with custom data types. This means that things like HashMap/Set and 
IndexMap/Set can't be arena allocated. This means that either we have to add 
support to these data types (by upstreaming or forking) or just accept that 
we're only partially using the arena allocator. 

* Finally, `bumpalo`'s `Box` cannot be moved out of, which turns out to be an 
annoying limitation that i've already had to work around several times. 

In the end i'm not sure arena allocators are worth it at this stage of the 
project. Relay Compiler has been successful without one, and other Rust-based 
internal compilers get by without them too.
2023-07-28 16:12:17 -07:00
Joe Savona 0e4656ac1c [rust][wip] swc->estree for jsx 2023-07-28 16:12:16 -07:00
Lauren Tan 5230e6d849 Add failing test case for ValidateNoSetStateInRender bug
The current heuristic to check if setState is called in render is based on 
whether the lambda containing the call to setState has a mutable range that got 
extended. This doesn't seem to work in all cases so this validation needs a bit 
more work before we can turn it on by default
2023-07-27 17:08:01 -04:00
Lauren Tan e77d975b16 Flag ValidateNoSetStateInRender
This needs a bit more work before we can turn it on by default, see the next PR 
for a failing test case.
2023-07-27 17:08:01 -04:00
Lauren Tan 12e6cee06f [eslint-plugin] Update hermes-parser to 0.15.0
This includes a fix where the HermesToBabelAdapter was incorrectly outputting a 
`ClassMethod` instead of `ClassPrivateMethod` in certain scenarios. This was 
causing issues with our eslint plugin as it would crash on any file containing 
private methods because a malformed AST was formed.
2023-07-27 11:46:50 -04:00
Lauren Tan 20b175fcd5 [ci] Pass correct flags to yarn install
Turns out `--immutable` and `--immutable-cache` are for yarn 2, yarn 1.x needs 
to use the `--frozen-lockfile` flag instead
2023-07-27 11:06:29 -04:00
Lauren Tan f45a4611e7 [ci] Don't run build on react
Building artifacts shouldn't be required for the tests, this should shave off a 
few minutes from CI
2023-07-27 11:06:27 -04:00
Lauren Tan 8405d8644f [eslint] Add plugin-proposal-private-methods
There was a bug in our internal Hermes to Babel adapter which caused some 
malformed AST to be constructed, which babel would then validate as being 
incorrect. That bug was fixed, but we still have to add this plugin so that 
private class methods can be parsed by babel. 

Tested internally
2023-07-27 10:52:28 -04:00
Joseph Savona 6912c31056 [CI] Run cargo build/test
Adds GitHub actions to build and test the Rust version of Forget on every commit 
to main. I didn't enable this on PRs for now just to avoid slowing down other 
folks, this seemed like a reasonable compromise while we're still in the 
experimentation phase. I test locally, and CI will catch if I or anyone else 
slips up.
2023-07-26 14:01:57 -07:00
Joe Savona d4a1356eca [rust] swc->estree for optional chaining
Handles conversion of optional chains from swc -> estree.
2023-07-25 16:56:29 -07:00
Joe Savona 8bc6184728 [rust] deny unused fields on non-node structs
Rejects unknown fields during deserialization of helper structs (which don't 
have a `type` field). This would be super useful for AST `Node` types, too, but 
that requires implementing a custom deserializer so i'm punting on that for now.
2023-07-25 16:22:57 -07:00
Joe Savona f21270a97d [rust] Complete estree through ES2021
Adds more AST types to handle the full ES2021 spec. At least, in theory I 
defined the schema correctly, and we'll have to just fix any bugs as we 
encounter them.
2023-07-25 15:58:58 -07:00
Joe Savona 90c3a3866d [rust] Improve estree serialization
Updates to use our own codegen'd `Serialize` implementation for AST node types. 
Notably, this allows us to ensure that we always emit a `type` field, even when 
the type is obvious from context (serde doesn't do this) and avoid 
double-emitting `type` fields (which serde can do if you try to force a tag to 
be emitted). We still use the Deserialize impl because it works fine for our 
purposes.
2023-07-25 15:16:22 -07:00
Joe Savona c19286fce7 [rust] More es2015 ast types
Adds almost all the remaining AST types from ES2015 to `estree`, and updates the 
swc->estree->hir conversions accordingly. In a few places i punted with a 
`Diagnostic::todo()`.
2023-07-25 14:08:09 -07:00
Joe Savona 59c1f22057 [rust] port inline_use_memo
Ports InlineUseMemo to Rust, this is the only missing transform pass on the 
pipeline up through constant propagation. I wanted to finish this so that we 
could do benchmarking of the early phase of compilation. UseMemo does a bunch of 
rewriting so it seemed worth comparing performance. 

Included: 

* Add swc -> estree and estree -> HIR conversions for arrow functions and call 
expressions 

* Add HIR definition for labeled terminals and call expressions 

* Utility for inlining one function into another — note that this requires 
remapping InstrIx operands since instruction indices will change. An alternative 
would be to store all the instructions for both outer/inner functions in the 
same array, in which case we wouldn't need to remap. 

* Helpers for mutably iterating the operands of an instruction or terminal. 

* The actual inline_use_memo() function. The overall logic is similar, i just 
had to slightly shuffle the order of operations to satisfy the borrow checker.
2023-07-25 09:24:19 -07:00
Sathya Gunasekaran 4976f5c1be [deps] Update ts-jest version
Console warnings when running tests: ``` ts-jest[versions] (WARN) Version 5.1.3 
of typescript installed has not been tested with ts-jest. ``` 

Our typescript version is ahead of what's supported in ts-jest. This PR updates 
ts-jest to work with the latest typescript compiler.
2023-07-26 12:11:53 +01:00
Sathya Gunasekaran 25527b3c0a [hir] Add support for holey arrays
This PR adds a Hole kind that can be present in both ArrayExpression and 
ArrayPatterns. 

This Hole type is not interesting for our inference passes and is skipped over 
for all of the pipeline.
2023-07-26 12:03:19 +01:00
Sathya Gunasekaran dcb6549dda [hir] Bailout when reading from React namespace
Forget doesn't understand the React namespace object and generates incorrect 
code when compiling code that loads props from this namespace object. 

This PR makes Forget bailout when we see a property load from React namespace 
object.
2023-07-25 16:08:32 +01:00
Sathya Gunasekaran 3f0afcbe47 [test] Failing test for using hooks from React namespace
Forget assumes hooks are imported and used directly without the React namespace 
and generates incorrect code when if there's a namespace.
2023-07-25 15:56:46 +01:00
Sathya Gunasekaran 82e426e229 [CI] Run react test suite on every PR
Add a new workflow to run test suite to run on every PR. 

Updates the build step to just build the UMD variant of react & react-dom for 
the tests.
2023-07-25 13:34:50 +01:00
Timothy Yung 49cf3b4038 [deps] Upgrade to hermes-parser@^0.14.0 and prettier@2.8.8
Upgrades every dependency on `hermes-parser` and `prettier` to the versions that 
we're using in the rest of our other codebases. Notably, these package versions 
are necessary for our other codebases to make use of mapped types in Flow.
2023-07-21 09:19:17 -07:00
Timothy Yung fc9db4c487 [ez] Delete unnecessary yarn.lock files
8d021e5d797c5ef3b4e18a0be735c04005f2ae7a configured `./forget` as a workspace 
root, so these `yarn.lock` files in `app/*` and `packages/*` are no longer 
relevant. This PR deletes them to reduce noise and confusion.
2023-07-21 09:15:28 -07:00
Lauren Tan 5ac909172e Validate unconditional setState in render 2023-07-17 13:00:38 -04:00
Joe Savona bac24ffd78 [rust] Use new block helper for more passes 2023-07-14 22:47:21 +09:00
Joe Savona de0c2393ad [rust] MergeConsecutiveBlocks and block helpers
Ports `MergeConsecutiveBlocks` to Rust. This was a tricky one: as we iterate 
through the blocks _if_ the block ends up being merged with its predecessor we 
need to consume it and modify its predecessor block (ie, mutating two things 
from the same data structure - shared mutation!). But if we _don't_ need to 
merge, then we need to not drop the current block. Ie, we sort of need to 
conditionally take ownership of the current block during iteration and put it 
back. 

I added a `BlockRewriter` helper type for this which has a helper to iterate 
safely. It calls the iterator lambda, moving blocks one at a time into the 
lambda. The lambda returns either `Keep(block)` to give the block back and keep 
it or `Remove` to tell the rewriter to drop the block. Thanks to making the 
Blocks data structure hold `Option<Box<BasicBlock>>` items, "moving" the block 
actually just means nulling out the option and conceptually giving ownership of 
the pointer to the callback - the data itself never moves. 

This involved creating a custom `Blocks` wrapper type, which i had been putting 
off doing. This cleans up a bunch of other logic around traversing blocks.
2023-07-14 18:00:16 +09:00