Early branch with new type inference foundation

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.
This commit is contained in:
Joe Savona
2024-01-03 10:47:47 -08:00
parent bd37fbe06a
commit 7ca3b004ae
9 changed files with 65 additions and 23 deletions
@@ -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",
@@ -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 });
@@ -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<t.FunctionDeclaration>
| NodePath<t.FunctionExpression>
| NodePath<t.ArrowFunctionExpression>;
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",
@@ -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<EnvironmentConfig, ZodError<PartialEnvironmentConfig>> {
const config = EnvironmentConfigSchema.safeParse(partialConfig);
if (config.success) {
return Ok(config.data);
} else {
return Err(config.error);
}
}
export function validateEnvironmentConfig(
partialConfig: PartialEnvironmentConfig
): EnvironmentConfig {
@@ -637,6 +637,7 @@ export type CallExpression = {
callee: Place;
args: Array<Place | SpreadPattern>;
loc: SourceLocation;
typeArguments?: Array<t.FlowType>;
};
/*
@@ -712,7 +713,7 @@ export type InstructionValue =
| MethodCall
| {
kind: "UnaryExpression";
operator: string;
operator: t.UnaryExpression["operator"];
value: Place;
loc: SourceLocation;
}
@@ -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<Place> {
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;
}
}
@@ -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));
@@ -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",
]);
+12
View File
@@ -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"