From 7ca3b004aee982ee637dc06ccb5fe890c9f6e5ba Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Wed, 3 Jan 2024 10:47:47 -0800 Subject: [PATCH] Early branch with new type inference foundation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's starting to get complex just with a couple of extra passes — we either need to substantially extend the HIR or (as i've done so far) pass information from early passes to later ones. This PR changes things so that very early in the babel plugin we fork into a separate mode. Forest has its own `compileProgram()` equivalent, its own pipeline, its own codegen, etc. --- .../babel-plugin-react-forget/package.json | 2 +- .../src/Entrypoint/Pipeline.ts | 5 ----- .../src/Entrypoint/Program.ts | 13 ++++++++----- .../src/HIR/Environment.ts | 14 +++++++++++++- .../babel-plugin-react-forget/src/HIR/HIR.ts | 3 ++- .../babel-plugin-react-forget/src/HIR/visitors.ts | 15 +++++++++++---- .../src/ReactiveScopes/CodegenReactiveFunction.ts | 11 +++++++++++ compiler/packages/sprout/src/SproutTodoFilter.ts | 13 +++++++------ compiler/yarn.lock | 12 ++++++++++++ 9 files changed, 65 insertions(+), 23 deletions(-) diff --git a/compiler/packages/babel-plugin-react-forget/package.json b/compiler/packages/babel-plugin-react-forget/package.json index e70d614144..f0a773f44a 100644 --- a/compiler/packages/babel-plugin-react-forget/package.json +++ b/compiler/packages/babel-plugin-react-forget/package.json @@ -58,7 +58,7 @@ "babel-plugin-syntax-hermes-parser": "^0.15.1", "eslint": "8.27.0", "glob": "^7.1.6", - "hermes-parser": "^0.17.1", + "hermes-parser": "^0.18.2", "jest": "^29.0.3", "jest-environment-jsdom": "^29.0.3", "prettier": "2.8.8", diff --git a/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts index bf77ccba7c..498d1e06fa 100644 --- a/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts @@ -8,7 +8,6 @@ import { NodePath } from "@babel/traverse"; import * as t from "@babel/types"; import prettyFormat from "pretty-format"; -import { lowerToForest } from "../Forest"; import { HIRFunction, ReactiveFunction, @@ -364,10 +363,6 @@ function* runWithEnvironment( validatePreservedManualMemoization(reactiveFunction); } - if (env.config.enableForest) { - yield* lowerToForest(reactiveFunction); - } - const ast = codegenFunction(reactiveFunction).unwrap(); yield log({ kind: "ast", name: "Codegen", value: ast }); diff --git a/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts index ce0d2c0875..7611944c3d 100644 --- a/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts @@ -14,8 +14,8 @@ import { } from "../CompilerError"; import { ExternalFunction, + parseEnvironmentConfig, tryParseExternalFunction, - validateEnvironmentConfig, } from "../HIR/Environment"; import { CodegenFunction } from "../ReactiveScopes"; import { isComponentDeclaration } from "../Utils/ComponentDeclaration"; @@ -67,12 +67,12 @@ function isConfigError(err: unknown): boolean { return false; } -type BabelFn = +export type BabelFn = | NodePath | NodePath | NodePath; -type CompileResult = { +export type CompileResult = { originalFn: BabelFn; compiledFn: CodegenFunction; }; @@ -115,7 +115,7 @@ function handleError( } } -function createNewFunctionNode( +export function createNewFunctionNode( originalFn: BabelFn, compiledFn: CodegenFunction ): t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression { @@ -182,6 +182,8 @@ export function compileProgram( pass: CompilerPass ): void { const options = parsePluginOptions(pass.opts); + const environment = parseEnvironmentConfig(pass.opts.environment ?? {}); + /* * Record lint errors and critical errors as depending on Forget's config, * we may still need to run Forget's analysis on every function (even if we @@ -224,7 +226,8 @@ export function compileProgram( * TODO(lauren): Remove pass.opts.environment nullcheck once PluginOptions * is validated */ - const config = validateEnvironmentConfig(pass.opts.environment ?? {}); + const config = environment.unwrap(); + compiledFn = compileFn(fn, config); pass.opts.logger?.logEvent(pass.filename, { kind: "CompileSuccess", diff --git a/compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts index 05a0688836..0c93d30ead 100644 --- a/compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts @@ -6,9 +6,10 @@ */ import * as t from "@babel/types"; -import { z } from "zod"; +import { ZodError, z } from "zod"; import { fromZodError } from "zod-validation-error"; import { CompilerError } from "../CompilerError"; +import { Err, Ok, Result } from "../Utils/Result"; import { log } from "../Utils/logger"; import { DEFAULT_GLOBALS, @@ -508,6 +509,17 @@ export function isHookName(name: string): boolean { return /^use[A-Z0-9]/.test(name); } +export function parseEnvironmentConfig( + partialConfig: PartialEnvironmentConfig +): Result> { + const config = EnvironmentConfigSchema.safeParse(partialConfig); + if (config.success) { + return Ok(config.data); + } else { + return Err(config.error); + } +} + export function validateEnvironmentConfig( partialConfig: PartialEnvironmentConfig ): EnvironmentConfig { diff --git a/compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts index 086aaf471b..9350369766 100644 --- a/compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts @@ -637,6 +637,7 @@ export type CallExpression = { callee: Place; args: Array; loc: SourceLocation; + typeArguments?: Array; }; /* @@ -712,7 +713,7 @@ export type InstructionValue = | MethodCall | { kind: "UnaryExpression"; - operator: string; + operator: t.UnaryExpression["operator"]; value: Place; loc: SourceLocation; } diff --git a/compiler/packages/babel-plugin-react-forget/src/HIR/visitors.ts b/compiler/packages/babel-plugin-react-forget/src/HIR/visitors.ts index 54007e5e09..55d2536b67 100644 --- a/compiler/packages/babel-plugin-react-forget/src/HIR/visitors.ts +++ b/compiler/packages/babel-plugin-react-forget/src/HIR/visitors.ts @@ -14,6 +14,7 @@ import { Pattern, Place, ReactiveInstruction, + ReactiveValue, SpreadPattern, Terminal, } from "./HIR"; @@ -24,20 +25,26 @@ export function* eachInstructionLValue( if (instr.lvalue !== null) { yield instr.lvalue; } - switch (instr.value.kind) { + yield* eachInstructionValueLValue(instr.value); +} + +export function* eachInstructionValueLValue( + value: ReactiveValue +): Iterable { + switch (value.kind) { case "DeclareLocal": case "DeclareContext": case "StoreLocal": { - yield instr.value.lvalue.place; + yield value.lvalue.place; break; } case "Destructure": { - yield* eachPatternOperand(instr.value.lvalue.pattern); + yield* eachPatternOperand(value.lvalue.pattern); break; } case "PostfixUpdate": case "PrefixUpdate": { - yield instr.value.lvalue; + yield value.lvalue; break; } } diff --git a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts index 748cbbaaab..537730811d 100644 --- a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -1164,6 +1164,17 @@ function codegenInstructionValue( break; } case "CallExpression": { + if (cx.env.config.enableForest) { + const callee = codegenPlaceToExpression(cx, instrValue.callee); + const args = instrValue.args.map((arg) => codegenArgument(cx, arg)); + value = t.callExpression(callee, args); + if (instrValue.typeArguments != null) { + value.typeArguments = t.typeParameterInstantiation( + instrValue.typeArguments + ); + } + break; + } const isHook = getHookKind(cx.env, instrValue.callee.identifier) != null; const callee = codegenPlaceToExpression(cx, instrValue.callee); const args = instrValue.args.map((arg) => codegenArgument(cx, arg)); diff --git a/compiler/packages/sprout/src/SproutTodoFilter.ts b/compiler/packages/sprout/src/SproutTodoFilter.ts index aadb901a88..02cbe97542 100644 --- a/compiler/packages/sprout/src/SproutTodoFilter.ts +++ b/compiler/packages/sprout/src/SproutTodoFilter.ts @@ -422,11 +422,12 @@ const skipFilter = new Set([ "readonly-object-method-calls-mutable-lambda", // TODO: 🌲 - "forest/forest-basic", - "forest/forest-basic-jsx", - "forest/forest-primitive-operations", - "forest/computed-load-props", - "forest/property-load-props", + "forest/forest-basic.flow", + "forest/forest-basic-jsx.flow", + "forest/forest-typing.flow", + "forest/forest-primitive-operations.flow", + "forest/computed-load-props.flow", + "forest/property-load-props.flow", // TODO: we probably want to always skip these "rules-of-hooks/rules-of-hooks-0592bd574811", @@ -519,7 +520,7 @@ const skipFilter = new Set([ "bug-jsx-memberexpr-tag-in-lambda", "bug-invalid-code-when-bailout", "component-syntax-ref-gating.flow", - + // 'react-forget-runtime' not yet supported "flag-enable-emit-hook-guards", ]); diff --git a/compiler/yarn.lock b/compiler/yarn.lock index b51d0106a5..fe6f783751 100644 --- a/compiler/yarn.lock +++ b/compiler/yarn.lock @@ -6711,6 +6711,11 @@ hermes-estree@0.17.1: resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.17.1.tgz#902806a900c185720424ffcf958027821d23c051" integrity sha512-EdUJms+eRE40OQxysFlPr1mPpvUbbMi7uDAKlScBw8o3tQY22BZ5yx56OYyp1bVaBm+7Cjc3NQz24sJEFXkPxg== +hermes-estree@0.18.2: + version "0.18.2" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.18.2.tgz#fd450fa1659cf074ceaa2ddeeb21674f3b2342f3" + integrity sha512-KoLsoWXJ5o81nit1wSyEZnWUGy9cBna9iYMZBR7skKh7okYAYKqQ9/OczwpMHn/cH0hKDyblulGsJ7FknlfVxQ== + hermes-parser@0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.14.0.tgz#edb2e7172fce996d2c8bbba250d140b70cc1aaaf" @@ -6732,6 +6737,13 @@ hermes-parser@0.17.1, hermes-parser@^0.17.1: dependencies: hermes-estree "0.17.1" +hermes-parser@^0.18.2: + version "0.18.2" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.18.2.tgz#50f15e2fcd559a48c68cd7af259d4292298bd14d" + integrity sha512-1eQfvib+VPpgBZ2zYKQhpuOjw1tH+Emuib6QmjkJWJMhyjM8xnXMvA+76o9LhF0zOAJDZgPfQhg43cyXEyl5Ew== + dependencies: + hermes-estree "0.18.2" + hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"