diff --git a/compiler/forget/OLD_ARCHITECTURE.md b/compiler/forget/OLD_ARCHITECTURE.md deleted file mode 100644 index 51500a691e..0000000000 --- a/compiler/forget/OLD_ARCHITECTURE.md +++ /dev/null @@ -1,69 +0,0 @@ -# Architecture Overview - -Note: This refers to the non-HIR based architecture. - -## Diagnostics - -Diagnostics are a way to indicate to the user that the compiler has encountered an unexpected issue, and are created in a structured format that is reused in the Playground to imperatively draw model markers ("squigglies") at their respective node locations. - -### Diagnostic levels - -Rough guidelines for diagnostic levels: - -- `Error`: This is emitted by the compiler when it detects an issue that makes it unable to compile the program. This is typically because the program is invalid, or when we have decided to make a specific `Warning` an error. An example for the latter might be when we want to introduce constraints into React programs that the compiler enforces. - -- `Warning`: This is emitted by the compiler when we've detected something strange in the program that causes a bailout/deopt. - -### Adding new diagnostics - -- Define a new error code, monotonically increasing the previous error code by 1. Error codes are in the format `E0000` and must be unique. - -## Terminology - -### AST - -(Babel) Abstract Syntax Tree - -### IR - -Intermediate Representation. - -The purpose of IR is to represent the program in the way to help with static analysis (e.g. mutability of values, dependencies between values). - -### LIR - -Low-Level Intermediate Representation. - -The purpose of LIR is to represent the program in the way to help with -code generation (e.g. grouping, re-ordering). - -### ValGraph - -Graph representing IR values and their direct dependencies on other IR values. - -### SCCGraph - -The ValGraph condensed into [strongly connected components](https://en.wikipedia.org/wiki/Strongly_connected_component) to group mututally dependent Vals. - -### RedGraph - -The SCCGraph processed after a topological iterative algorithm to reduce the dependency relations between SCCs. - -This can be seen as the final "Reactive Graph" that captured the relations of "what invalidates me" and "what do I invalidate" (directly and transitively) which will be utilized by the Back End and eventually be materialized by the memoization. - -## Compiler Passes - -### Middle End Passes - -1. `ReactFuncsInfer` uses heurstics to infer functions that are considered as "React Functions" (functional components and hook definitions) and will be used as compilation units. -2. `ParamAnalysis` analyzes React function parameters resolve inputs. For components these are individual props and for Hooks all arguments. -3. `BodyAnalysis` analyzes the body of React functions to create an `IR.Block` for each top level statement. This classifies calls to hooks and records what variable bindings are used as function inputs or going into JSX calls. -4. `DumpIR` optionally records the IR in the compiler context for debugging purposes. -5. `DepGraphAnalysis` collects dependencies for a React function into a `DepGraph.ValGraph` according to syntax-directed rules and analyzes the graph with graph algorithms like graph condensation and topological-orderred iteration. - -### Back End Passes - -1. `LIRGen` lowers `IR.Func` to `LIR.Func` which groups statements by their inputs into non-reactive or reactive blocks. -2. `MemoCacheAlloc` allocates memo cache locations for change tracking and caching of intermediate values or outputs. -3. `DumpLIR` optionally records the LIR in the compiler context for debugging purposes. -4. `JSGen` the last step that actually modifies the Babel AST with the result of the LIR. diff --git a/compiler/forget/docs/ARCHITECTURE.md b/compiler/forget/docs/ARCHITECTURE.md new file mode 100644 index 0000000000..f6e820d84d --- /dev/null +++ b/compiler/forget/docs/ARCHITECTURE.md @@ -0,0 +1,35 @@ +# React Forget — Goals, Design Principles, and Architecture + +This document describes the goals, design principles, and high-level architecture of React Forget. See the code for specifics of the data structures and compiler passes. + +## Goals + +The idea of React Forget is to allow developers to use React's familiar declarative, component-based programming model, while ensuring that apps are fast by default. Concretely we seek to achieve the following goals: + +* Bound the amount of re-rendering that happens on updates to ensure that apps have predictably fast peformance by default. +* Keep startup time neutral with pre-Forget performance. Notably, this means holding code size increases and memoization low enough to not impact startup. +* Retain React's familiar declarative, component-oriented programming model. Ie, the solution should not fundamentally change how developers think about writing React, and should generally _remove_ concepts (the need to use React.memo(), useMemo(), and useCallback()) rather than introduce new concepts. +* "Just work" on idiomatic React code that follows React's rules (pure render functions, the rules of hooks, etc). +* Support typical debugging and profiling tools and workflows. +* Be predictable and understandable enough by React developers — ie developers should be able to quickly develop a rough intuition of how Forget works. + +### Non-Goals + +The following are explicitly *not* goals for Forget: + +* Provide perfectly optimal re-rendering with zero unnecessary recomputation. This is a non-goal for several reasons: + * The runtime overhead of the extra tracking involved can outweight the cost of recomputation in many cases. + * It is difficult (or potentially impossible) to achieve in cases with dynamical, conditional dependencies. + * The amount of code may regress startup times, which would conflict with our goal of neutral startup performance. +* Support code that violates React's rules. React's rules exist to help developers build robust, scalable applications and form a contract that allows us to continue improving React without breaking applications. Forget depends on these rules to safely transform code, and violations of rules will therefore break Forget's optimizations. +* Support legacy React features. Notably we will not support class components due to their inherent mutable state being shared across multiple methods with complex lifetimes and data flow. +* Support 100% of the JavaScript language. In particular, we will not support rarely used features and/or features which are known to be unsafe or which cannot be modeled soundly. For example, nested classes that capture values from their closure are difficult to model accurately bc of mutability, and `eval()` is unsafe. + +## Design Principles + +Many aspects of the design follow naturally from the above goals: + +* The compiler output must be high-level code that retains not just the semantics of the input but also is expressed using similar constructs to the input. For example, rather than convert logical expressions (`a ?? b`) into an `if` statement, we retain the high-level form of the logical expression. Rather than convert all looping constructs to a single form, we retain the original type of the loop. Etc. This follows from our goals: + * High-level code is more compact, and helps reduce the impact of compilation on application size + * High-level constructs that match what the developer wrote are easier to debug +* From the above, it then follows that the compiler's internal representation must also be high-level enough to be able to output the original high-level constructs. The internal representation is what we call a High-level Intermediate Representation (HIR) — a name borrowed from Rust Compiler. However, Forget's HIR is perhaps even more suited to this name, as it retains high-level information (distinguishing if vs logical vs ternary, or for vs while vs for..of) but also represents code as a control-flow graph with no nesting.