mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
Compiler: unfork prettier config (#30205)
Updates the prettier config to format all `.ts` and `.tsx` files in the repo using the existing defaults and removing overrides. The first commit in this PR contains the config changes, the second is just the result of running `yarn prettier-all`.
This commit is contained in:
parent
6fac743ed7
commit
fd2b3e13d3
|
|
@ -1,10 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const {
|
||||
compilerPaths,
|
||||
esNextPaths,
|
||||
typescriptPaths,
|
||||
} = require('./scripts/shared/pathsByLanguageVersion');
|
||||
const {esNextPaths} = require('./scripts/shared/pathsByLanguageVersion');
|
||||
|
||||
module.exports = {
|
||||
bracketSpacing: false,
|
||||
|
|
@ -28,25 +24,11 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
{
|
||||
files: typescriptPaths,
|
||||
files: ['*.ts', '*.tsx'],
|
||||
options: {
|
||||
trailingComma: 'all',
|
||||
parser: 'typescript',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: compilerPaths,
|
||||
options: {
|
||||
requirePragma: false,
|
||||
parser: 'babel-ts',
|
||||
semi: true,
|
||||
singleQuote: false,
|
||||
trailingComma: 'es5',
|
||||
bracketSpacing: true,
|
||||
bracketSameLine: false,
|
||||
printWidth: 80,
|
||||
arrowParens: 'always',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { encodeStore, type Store } from "../../lib/stores";
|
||||
import {expect, test} from '@playwright/test';
|
||||
import {encodeStore, type Store} from '../../lib/stores';
|
||||
|
||||
const STORE: Store = {
|
||||
source: `export default function TestComponent({ x }) {
|
||||
|
|
@ -17,33 +17,33 @@ const STORE: Store = {
|
|||
const HASH = encodeStore(STORE);
|
||||
|
||||
function concat(data: Array<string>): string {
|
||||
return data.join("");
|
||||
return data.join('');
|
||||
}
|
||||
|
||||
test("editor should compile successfully", async ({ page }) => {
|
||||
await page.goto(`/#${HASH}`, { waitUntil: "networkidle" });
|
||||
test('editor should compile successfully', async ({page}) => {
|
||||
await page.goto(`/#${HASH}`, {waitUntil: 'networkidle'});
|
||||
await page.screenshot({
|
||||
fullPage: true,
|
||||
path: "test-results/00-on-networkidle.png",
|
||||
path: 'test-results/00-on-networkidle.png',
|
||||
});
|
||||
|
||||
// User input from hash compiles
|
||||
await page.screenshot({
|
||||
fullPage: true,
|
||||
path: "test-results/01-show-js-before.png",
|
||||
path: 'test-results/01-show-js-before.png',
|
||||
});
|
||||
const userInput =
|
||||
(await page.locator(".monaco-editor").nth(2).allInnerTexts()) ?? [];
|
||||
expect(concat(userInput)).toMatchSnapshot("user-input.txt");
|
||||
(await page.locator('.monaco-editor').nth(2).allInnerTexts()) ?? [];
|
||||
expect(concat(userInput)).toMatchSnapshot('user-input.txt');
|
||||
|
||||
// Reset button works
|
||||
page.on("dialog", (dialog) => dialog.accept());
|
||||
await page.getByRole("button", { name: "Reset" }).click();
|
||||
page.on('dialog', dialog => dialog.accept());
|
||||
await page.getByRole('button', {name: 'Reset'}).click();
|
||||
await page.screenshot({
|
||||
fullPage: true,
|
||||
path: "test-results/02-show-js-after.png",
|
||||
path: 'test-results/02-show-js-after.png',
|
||||
});
|
||||
const defaultInput =
|
||||
(await page.locator(".monaco-editor").nth(2).allInnerTexts()) ?? [];
|
||||
expect(concat(defaultInput)).toMatchSnapshot("default-input.txt");
|
||||
(await page.locator('.monaco-editor').nth(2).allInnerTexts()) ?? [];
|
||||
expect(concat(defaultInput)).toMatchSnapshot('default-input.txt');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,25 +5,24 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type { NextPage } from "next";
|
||||
import Head from "next/head";
|
||||
import { SnackbarProvider } from "notistack";
|
||||
import { Editor, Header, StoreProvider } from "../components";
|
||||
import MessageSnackbar from "../components/Message";
|
||||
import type {NextPage} from 'next';
|
||||
import Head from 'next/head';
|
||||
import {SnackbarProvider} from 'notistack';
|
||||
import {Editor, Header, StoreProvider} from '../components';
|
||||
import MessageSnackbar from '../components/Message';
|
||||
|
||||
const Home: NextPage = () => {
|
||||
return (
|
||||
<div className="flex flex-col w-screen h-screen font-light">
|
||||
<Head>
|
||||
<title>
|
||||
{process.env.NODE_ENV === "development"
|
||||
? "[DEV] React Compiler Playground"
|
||||
: "React Compiler Playground"}
|
||||
{process.env.NODE_ENV === 'development'
|
||||
? '[DEV] React Compiler Playground'
|
||||
: 'React Compiler Playground'}
|
||||
</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"
|
||||
></meta>
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"></meta>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link
|
||||
|
|
@ -45,8 +44,7 @@ const Home: NextPage = () => {
|
|||
<SnackbarProvider
|
||||
preventDuplicate
|
||||
maxSnack={10}
|
||||
Components={{ message: MessageSnackbar }}
|
||||
>
|
||||
Components={{message: MessageSnackbar}}>
|
||||
<Header />
|
||||
<Editor />
|
||||
</SnackbarProvider>
|
||||
|
|
|
|||
|
|
@ -5,26 +5,21 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import "../styles/globals.css";
|
||||
import '../styles/globals.css';
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
"use no memo";
|
||||
export default function RootLayout({children}: {children: React.ReactNode}) {
|
||||
'use no memo';
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>
|
||||
{process.env.NODE_ENV === "development"
|
||||
? "[DEV] React Compiler Playground"
|
||||
: "React Compiler Playground"}
|
||||
{process.env.NODE_ENV === 'development'
|
||||
? '[DEV] React Compiler Playground'
|
||||
: 'React Compiler Playground'}
|
||||
</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"
|
||||
></meta>
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"></meta>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
'use client';
|
||||
|
||||
import { SnackbarProvider } from "notistack";
|
||||
import { Editor, Header, StoreProvider } from "../components";
|
||||
import MessageSnackbar from "../components/Message";
|
||||
import {SnackbarProvider} from 'notistack';
|
||||
import {Editor, Header, StoreProvider} from '../components';
|
||||
import MessageSnackbar from '../components/Message';
|
||||
|
||||
export default function Hoot() {
|
||||
return (
|
||||
|
|
@ -17,8 +17,7 @@ export default function Hoot() {
|
|||
<SnackbarProvider
|
||||
preventDuplicate
|
||||
maxSnack={10}
|
||||
Components={{ message: MessageSnackbar }}
|
||||
>
|
||||
Components={{message: MessageSnackbar}}>
|
||||
<Header />
|
||||
<Editor />
|
||||
</SnackbarProvider>
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@
|
|||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: ["next/babel"],
|
||||
presets: ['next/babel'],
|
||||
plugins: [
|
||||
[
|
||||
"babel-plugin-react-compiler",
|
||||
'babel-plugin-react-compiler',
|
||||
{
|
||||
runtimeModule: "react-compiler-runtime",
|
||||
runtimeModule: 'react-compiler-runtime',
|
||||
},
|
||||
],
|
||||
],
|
||||
|
|
|
|||
|
|
@ -11,86 +11,86 @@
|
|||
|
||||
module.exports = {
|
||||
// Text colors
|
||||
primary: "#23272F", // gray-90
|
||||
"primary-dark": "#F6F7F9", // gray-5
|
||||
secondary: "#404756", // gray-70
|
||||
"secondary-dark": "#EBECF0", // gray-10
|
||||
link: "#087EA4", // blue-50
|
||||
"link-dark": "#149ECA", // blue-40
|
||||
syntax: "#EBECF0", // gray-10
|
||||
wash: "#FFFFFF",
|
||||
"wash-dark": "#23272F", // gray-90
|
||||
card: "#F6F7F9", // gray-05
|
||||
"card-dark": "#343A46", // gray-80
|
||||
highlight: "#E6F7FF", // blue-10
|
||||
"highlight-dark": "rgba(88,175,223,.1)",
|
||||
border: "#EBECF0", // gray-10
|
||||
"border-dark": "#343A46", // gray-80
|
||||
"secondary-button": "#EBECF0", // gray-10
|
||||
"secondary-button-dark": "#404756", // gray-70
|
||||
primary: '#23272F', // gray-90
|
||||
'primary-dark': '#F6F7F9', // gray-5
|
||||
secondary: '#404756', // gray-70
|
||||
'secondary-dark': '#EBECF0', // gray-10
|
||||
link: '#087EA4', // blue-50
|
||||
'link-dark': '#149ECA', // blue-40
|
||||
syntax: '#EBECF0', // gray-10
|
||||
wash: '#FFFFFF',
|
||||
'wash-dark': '#23272F', // gray-90
|
||||
card: '#F6F7F9', // gray-05
|
||||
'card-dark': '#343A46', // gray-80
|
||||
highlight: '#E6F7FF', // blue-10
|
||||
'highlight-dark': 'rgba(88,175,223,.1)',
|
||||
border: '#EBECF0', // gray-10
|
||||
'border-dark': '#343A46', // gray-80
|
||||
'secondary-button': '#EBECF0', // gray-10
|
||||
'secondary-button-dark': '#404756', // gray-70
|
||||
|
||||
// Gray
|
||||
"gray-95": "#16181D",
|
||||
"gray-90": "#23272F",
|
||||
"gray-80": "#343A46",
|
||||
"gray-70": "#404756",
|
||||
"gray-60": "#4E5769",
|
||||
"gray-50": "#5E687E", // unused
|
||||
"gray-40": "#78839B",
|
||||
"gray-30": "#99A1B3",
|
||||
"gray-20": "#BCC1CD",
|
||||
"gray-10": "#EBECF0",
|
||||
"gray-5": "#F6F7F9",
|
||||
'gray-95': '#16181D',
|
||||
'gray-90': '#23272F',
|
||||
'gray-80': '#343A46',
|
||||
'gray-70': '#404756',
|
||||
'gray-60': '#4E5769',
|
||||
'gray-50': '#5E687E', // unused
|
||||
'gray-40': '#78839B',
|
||||
'gray-30': '#99A1B3',
|
||||
'gray-20': '#BCC1CD',
|
||||
'gray-10': '#EBECF0',
|
||||
'gray-5': '#F6F7F9',
|
||||
|
||||
// Blue
|
||||
"blue-60": "#045975",
|
||||
"blue-50": "#087EA4",
|
||||
"blue-40": "#149ECA", // Brand Blue
|
||||
"blue-30": "#58C4DC", // unused
|
||||
"blue-20": "#ABE2ED",
|
||||
"blue-10": "#E6F7FF", // todo: doesn't match illustrations
|
||||
"blue-5": "#E6F6FA",
|
||||
'blue-60': '#045975',
|
||||
'blue-50': '#087EA4',
|
||||
'blue-40': '#149ECA', // Brand Blue
|
||||
'blue-30': '#58C4DC', // unused
|
||||
'blue-20': '#ABE2ED',
|
||||
'blue-10': '#E6F7FF', // todo: doesn't match illustrations
|
||||
'blue-5': '#E6F6FA',
|
||||
|
||||
// Yellow
|
||||
"yellow-60": "#B65700",
|
||||
"yellow-50": "#C76A15",
|
||||
"yellow-40": "#DB7D27", // unused
|
||||
"yellow-30": "#FABD62", // unused
|
||||
"yellow-20": "#FCDEB0", // unused
|
||||
"yellow-10": "#FDE7C7",
|
||||
"yellow-5": "#FEF5E7",
|
||||
'yellow-60': '#B65700',
|
||||
'yellow-50': '#C76A15',
|
||||
'yellow-40': '#DB7D27', // unused
|
||||
'yellow-30': '#FABD62', // unused
|
||||
'yellow-20': '#FCDEB0', // unused
|
||||
'yellow-10': '#FDE7C7',
|
||||
'yellow-5': '#FEF5E7',
|
||||
|
||||
// Purple
|
||||
"purple-60": "#2B3491", // unused
|
||||
"purple-50": "#575FB7",
|
||||
"purple-40": "#6B75DB",
|
||||
"purple-30": "#8891EC",
|
||||
"purple-20": "#C3C8F5", // unused
|
||||
"purple-10": "#E7E9FB",
|
||||
"purple-5": "#F3F4FD",
|
||||
'purple-60': '#2B3491', // unused
|
||||
'purple-50': '#575FB7',
|
||||
'purple-40': '#6B75DB',
|
||||
'purple-30': '#8891EC',
|
||||
'purple-20': '#C3C8F5', // unused
|
||||
'purple-10': '#E7E9FB',
|
||||
'purple-5': '#F3F4FD',
|
||||
|
||||
// Green
|
||||
"green-60": "#2B6E62",
|
||||
"green-50": "#388F7F",
|
||||
"green-40": "#44AC99",
|
||||
"green-30": "#7FCCBF",
|
||||
"green-20": "#ABDED5",
|
||||
"green-10": "#E5F5F2",
|
||||
"green-5": "#F4FBF9",
|
||||
'green-60': '#2B6E62',
|
||||
'green-50': '#388F7F',
|
||||
'green-40': '#44AC99',
|
||||
'green-30': '#7FCCBF',
|
||||
'green-20': '#ABDED5',
|
||||
'green-10': '#E5F5F2',
|
||||
'green-5': '#F4FBF9',
|
||||
|
||||
// RED
|
||||
"red-60": "#712D28",
|
||||
"red-50": "#A6423A", // unused
|
||||
"red-40": "#C1554D",
|
||||
"red-30": "#D07D77",
|
||||
"red-20": "#E5B7B3", // unused
|
||||
"red-10": "#F2DBD9", // unused
|
||||
"red-5": "#FAF1F0",
|
||||
'red-60': '#712D28',
|
||||
'red-50': '#A6423A', // unused
|
||||
'red-40': '#C1554D',
|
||||
'red-30': '#D07D77',
|
||||
'red-20': '#E5B7B3', // unused
|
||||
'red-10': '#F2DBD9', // unused
|
||||
'red-5': '#FAF1F0',
|
||||
|
||||
// MISC
|
||||
"code-block": "#99a1b30f", // gray-30 @ 6%
|
||||
"gradient-blue": "#58C4DC", // Only used for the landing gradient for now.
|
||||
'code-block': '#99a1b30f', // gray-30 @ 6%
|
||||
'gradient-blue': '#58C4DC', // Only used for the landing gradient for now.
|
||||
github: {
|
||||
highlight: "#fffbdd",
|
||||
highlight: '#fffbdd',
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { parse as babelParse, ParserPlugin } from "@babel/parser";
|
||||
import * as HermesParser from "hermes-parser";
|
||||
import traverse, { NodePath } from "@babel/traverse";
|
||||
import * as t from "@babel/types";
|
||||
import {parse as babelParse, ParserPlugin} from '@babel/parser';
|
||||
import * as HermesParser from 'hermes-parser';
|
||||
import traverse, {NodePath} from '@babel/traverse';
|
||||
import * as t from '@babel/types';
|
||||
import {
|
||||
CompilerError,
|
||||
CompilerErrorDetail,
|
||||
|
|
@ -20,51 +20,51 @@ import {
|
|||
run,
|
||||
ValueKind,
|
||||
type Hook,
|
||||
} from "babel-plugin-react-compiler/src";
|
||||
import { type ReactFunctionType } from "babel-plugin-react-compiler/src/HIR/Environment";
|
||||
import clsx from "clsx";
|
||||
import invariant from "invariant";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { useDeferredValue, useMemo } from "react";
|
||||
import { useMountEffect } from "../../hooks";
|
||||
import { defaultStore } from "../../lib/defaultStore";
|
||||
} from 'babel-plugin-react-compiler/src';
|
||||
import {type ReactFunctionType} from 'babel-plugin-react-compiler/src/HIR/Environment';
|
||||
import clsx from 'clsx';
|
||||
import invariant from 'invariant';
|
||||
import {useSnackbar} from 'notistack';
|
||||
import {useDeferredValue, useMemo} from 'react';
|
||||
import {useMountEffect} from '../../hooks';
|
||||
import {defaultStore} from '../../lib/defaultStore';
|
||||
import {
|
||||
createMessage,
|
||||
initStoreFromUrlOrLocalStorage,
|
||||
MessageLevel,
|
||||
MessageSource,
|
||||
type Store,
|
||||
} from "../../lib/stores";
|
||||
import { useStore, useStoreDispatch } from "../StoreContext";
|
||||
import Input from "./Input";
|
||||
} from '../../lib/stores';
|
||||
import {useStore, useStoreDispatch} from '../StoreContext';
|
||||
import Input from './Input';
|
||||
import {
|
||||
CompilerOutput,
|
||||
default as Output,
|
||||
PrintedCompilerPipelineValue,
|
||||
} from "./Output";
|
||||
import { printFunctionWithOutlined } from "babel-plugin-react-compiler/src/HIR/PrintHIR";
|
||||
import { printReactiveFunctionWithOutlined } from "babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction";
|
||||
} from './Output';
|
||||
import {printFunctionWithOutlined} from 'babel-plugin-react-compiler/src/HIR/PrintHIR';
|
||||
import {printReactiveFunctionWithOutlined} from 'babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction';
|
||||
|
||||
function parseInput(input: string, language: "flow" | "typescript") {
|
||||
function parseInput(input: string, language: 'flow' | 'typescript') {
|
||||
// Extract the first line to quickly check for custom test directives
|
||||
if (language === "flow") {
|
||||
if (language === 'flow') {
|
||||
return HermesParser.parse(input, {
|
||||
babel: true,
|
||||
flow: "all",
|
||||
sourceType: "module",
|
||||
flow: 'all',
|
||||
sourceType: 'module',
|
||||
enableExperimentalComponentSyntax: true,
|
||||
});
|
||||
} else {
|
||||
return babelParse(input, {
|
||||
plugins: ["typescript", "jsx"],
|
||||
sourceType: "module",
|
||||
plugins: ['typescript', 'jsx'],
|
||||
sourceType: 'module',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function parseFunctions(
|
||||
source: string,
|
||||
language: "flow" | "typescript"
|
||||
language: 'flow' | 'typescript',
|
||||
): Array<
|
||||
NodePath<
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
|
|
@ -105,7 +105,7 @@ function parseFunctions(
|
|||
|
||||
const COMMON_HOOKS: Array<[string, Hook]> = [
|
||||
[
|
||||
"useFragment",
|
||||
'useFragment',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
|
|
@ -114,7 +114,7 @@ const COMMON_HOOKS: Array<[string, Hook]> = [
|
|||
},
|
||||
],
|
||||
[
|
||||
"usePaginationFragment",
|
||||
'usePaginationFragment',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
|
|
@ -123,7 +123,7 @@ const COMMON_HOOKS: Array<[string, Hook]> = [
|
|||
},
|
||||
],
|
||||
[
|
||||
"useRefetchableFragment",
|
||||
'useRefetchableFragment',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
|
|
@ -132,7 +132,7 @@ const COMMON_HOOKS: Array<[string, Hook]> = [
|
|||
},
|
||||
],
|
||||
[
|
||||
"useLazyLoadQuery",
|
||||
'useLazyLoadQuery',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
|
|
@ -141,7 +141,7 @@ const COMMON_HOOKS: Array<[string, Hook]> = [
|
|||
},
|
||||
],
|
||||
[
|
||||
"usePreloadedQuery",
|
||||
'usePreloadedQuery',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
|
|
@ -156,22 +156,22 @@ function isHookName(s: string): boolean {
|
|||
}
|
||||
|
||||
function getReactFunctionType(
|
||||
id: NodePath<t.Identifier | null | undefined>
|
||||
id: NodePath<t.Identifier | null | undefined>,
|
||||
): ReactFunctionType {
|
||||
if (id && id.node && id.isIdentifier()) {
|
||||
if (isHookName(id.node.name)) {
|
||||
return "Hook";
|
||||
return 'Hook';
|
||||
}
|
||||
|
||||
const isPascalCaseNameSpace = /^[A-Z].*/;
|
||||
if (isPascalCaseNameSpace.test(id.node.name)) {
|
||||
return "Component";
|
||||
return 'Component';
|
||||
}
|
||||
}
|
||||
return "Other";
|
||||
return 'Other';
|
||||
}
|
||||
|
||||
function compile(source: string): [CompilerOutput, "flow" | "typescript"] {
|
||||
function compile(source: string): [CompilerOutput, 'flow' | 'typescript'] {
|
||||
const results = new Map<string, PrintedCompilerPipelineValue[]>();
|
||||
const error = new CompilerError();
|
||||
const upsert = (result: PrintedCompilerPipelineValue) => {
|
||||
|
|
@ -182,15 +182,15 @@ function compile(source: string): [CompilerOutput, "flow" | "typescript"] {
|
|||
results.set(result.name, [result]);
|
||||
}
|
||||
};
|
||||
let language: "flow" | "typescript";
|
||||
let language: 'flow' | 'typescript';
|
||||
if (source.match(/\@flow/)) {
|
||||
language = "flow";
|
||||
language = 'flow';
|
||||
} else {
|
||||
language = "typescript";
|
||||
language = 'typescript';
|
||||
}
|
||||
try {
|
||||
// Extract the first line to quickly check for custom test directives
|
||||
const pragma = source.substring(0, source.indexOf("\n"));
|
||||
const pragma = source.substring(0, source.indexOf('\n'));
|
||||
const config = parseConfigPragma(pragma);
|
||||
|
||||
for (const fn of parseFunctions(source, language)) {
|
||||
|
|
@ -199,16 +199,16 @@ function compile(source: string): [CompilerOutput, "flow" | "typescript"] {
|
|||
new CompilerErrorDetail({
|
||||
reason: `Unexpected function type ${fn.node.type}`,
|
||||
description:
|
||||
"Playground only supports parsing function declarations",
|
||||
'Playground only supports parsing function declarations',
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: fn.node.loc ?? null,
|
||||
suggestions: null,
|
||||
})
|
||||
}),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const id = fn.get("id");
|
||||
const id = fn.get('id');
|
||||
for (const result of run(
|
||||
fn,
|
||||
{
|
||||
|
|
@ -216,20 +216,20 @@ function compile(source: string): [CompilerOutput, "flow" | "typescript"] {
|
|||
customHooks: new Map([...COMMON_HOOKS]),
|
||||
},
|
||||
getReactFunctionType(id),
|
||||
"_c",
|
||||
'_c',
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)) {
|
||||
const fnName = fn.node.id?.name ?? null;
|
||||
switch (result.kind) {
|
||||
case "ast": {
|
||||
case 'ast': {
|
||||
upsert({
|
||||
kind: "ast",
|
||||
kind: 'ast',
|
||||
fnName,
|
||||
name: result.name,
|
||||
value: {
|
||||
type: "FunctionDeclaration",
|
||||
type: 'FunctionDeclaration',
|
||||
id: result.value.id,
|
||||
async: result.value.async,
|
||||
generator: result.value.generator,
|
||||
|
|
@ -239,27 +239,27 @@ function compile(source: string): [CompilerOutput, "flow" | "typescript"] {
|
|||
});
|
||||
break;
|
||||
}
|
||||
case "hir": {
|
||||
case 'hir': {
|
||||
upsert({
|
||||
kind: "hir",
|
||||
kind: 'hir',
|
||||
fnName,
|
||||
name: result.name,
|
||||
value: printFunctionWithOutlined(result.value),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "reactive": {
|
||||
case 'reactive': {
|
||||
upsert({
|
||||
kind: "reactive",
|
||||
kind: 'reactive',
|
||||
fnName,
|
||||
name: result.name,
|
||||
value: printReactiveFunctionWithOutlined(result.value),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "debug": {
|
||||
case 'debug': {
|
||||
upsert({
|
||||
kind: "debug",
|
||||
kind: 'debug',
|
||||
fnName,
|
||||
name: result.name,
|
||||
value: result.value,
|
||||
|
|
@ -288,24 +288,24 @@ function compile(source: string): [CompilerOutput, "flow" | "typescript"] {
|
|||
reason: `Unexpected failure when transforming input! ${err}`,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
if (error.hasErrors()) {
|
||||
return [{ kind: "err", results, error: error }, language];
|
||||
return [{kind: 'err', results, error: error}, language];
|
||||
}
|
||||
return [{ kind: "ok", results }, language];
|
||||
return [{kind: 'ok', results}, language];
|
||||
}
|
||||
|
||||
export default function Editor() {
|
||||
const store = useStore();
|
||||
const deferredStore = useDeferredValue(store);
|
||||
const dispatchStore = useStoreDispatch();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const {enqueueSnackbar} = useSnackbar();
|
||||
const [compilerOutput, language] = useMemo(
|
||||
() => compile(deferredStore.source),
|
||||
[deferredStore.source]
|
||||
[deferredStore.source],
|
||||
);
|
||||
|
||||
useMountEffect(() => {
|
||||
|
|
@ -313,35 +313,35 @@ export default function Editor() {
|
|||
try {
|
||||
mountStore = initStoreFromUrlOrLocalStorage();
|
||||
} catch (e) {
|
||||
invariant(e instanceof Error, "Only Error may be caught.");
|
||||
invariant(e instanceof Error, 'Only Error may be caught.');
|
||||
enqueueSnackbar(e.message, {
|
||||
variant: "message",
|
||||
variant: 'message',
|
||||
...createMessage(
|
||||
"Bad URL - fell back to the default Playground.",
|
||||
'Bad URL - fell back to the default Playground.',
|
||||
MessageLevel.Info,
|
||||
MessageSource.Playground
|
||||
MessageSource.Playground,
|
||||
),
|
||||
});
|
||||
mountStore = defaultStore;
|
||||
}
|
||||
dispatchStore({
|
||||
type: "setStore",
|
||||
payload: { store: mountStore },
|
||||
type: 'setStore',
|
||||
payload: {store: mountStore},
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative flex basis top-14">
|
||||
<div className={clsx("relative sm:basis-1/4")}>
|
||||
<div className={clsx('relative sm:basis-1/4')}>
|
||||
<Input
|
||||
language={language}
|
||||
errors={
|
||||
compilerOutput.kind === "err" ? compilerOutput.error.details : []
|
||||
compilerOutput.kind === 'err' ? compilerOutput.error.details : []
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={clsx("flex sm:flex flex-wrap")}>
|
||||
<div className={clsx('flex sm:flex flex-wrap')}>
|
||||
<Output store={deferredStore} compilerOutput={compilerOutput} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,28 +5,28 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import MonacoEditor, { loader, type Monaco } from "@monaco-editor/react";
|
||||
import { CompilerErrorDetail } from "babel-plugin-react-compiler/src";
|
||||
import invariant from "invariant";
|
||||
import type { editor } from "monaco-editor";
|
||||
import * as monaco from "monaco-editor";
|
||||
import { Resizable } from "re-resizable";
|
||||
import { useEffect, useState } from "react";
|
||||
import { renderReactCompilerMarkers } from "../../lib/reactCompilerMonacoDiagnostics";
|
||||
import { useStore, useStoreDispatch } from "../StoreContext";
|
||||
import { monacoOptions } from "./monacoOptions";
|
||||
import MonacoEditor, {loader, type Monaco} from '@monaco-editor/react';
|
||||
import {CompilerErrorDetail} from 'babel-plugin-react-compiler/src';
|
||||
import invariant from 'invariant';
|
||||
import type {editor} from 'monaco-editor';
|
||||
import * as monaco from 'monaco-editor';
|
||||
import {Resizable} from 're-resizable';
|
||||
import {useEffect, useState} from 'react';
|
||||
import {renderReactCompilerMarkers} from '../../lib/reactCompilerMonacoDiagnostics';
|
||||
import {useStore, useStoreDispatch} from '../StoreContext';
|
||||
import {monacoOptions} from './monacoOptions';
|
||||
// TODO: Make TS recognize .d.ts files, in addition to loading them with webpack.
|
||||
// @ts-ignore
|
||||
import React$Types from "../../node_modules/@types/react/index.d.ts";
|
||||
import React$Types from '../../node_modules/@types/react/index.d.ts';
|
||||
|
||||
loader.config({ monaco });
|
||||
loader.config({monaco});
|
||||
|
||||
type Props = {
|
||||
errors: CompilerErrorDetail[];
|
||||
language: "flow" | "typescript";
|
||||
language: 'flow' | 'typescript';
|
||||
};
|
||||
|
||||
export default function Input({ errors, language }: Props) {
|
||||
export default function Input({errors, language}: Props) {
|
||||
const [monaco, setMonaco] = useState<Monaco | null>(null);
|
||||
const store = useStore();
|
||||
const dispatchStore = useStoreDispatch();
|
||||
|
|
@ -36,11 +36,11 @@ export default function Input({ errors, language }: Props) {
|
|||
if (!monaco) return;
|
||||
const uri = monaco.Uri.parse(`file:///index.js`);
|
||||
const model = monaco.editor.getModel(uri);
|
||||
invariant(model, "Model must exist for the selected input file.");
|
||||
renderReactCompilerMarkers({ monaco, model, details: errors });
|
||||
invariant(model, 'Model must exist for the selected input file.');
|
||||
renderReactCompilerMarkers({monaco, model, details: errors});
|
||||
// N.B. that `tabSize` is a model property, not an editor property.
|
||||
// So, the tab size has to be set per model.
|
||||
model.updateOptions({ tabSize: 2 });
|
||||
model.updateOptions({tabSize: 2});
|
||||
}, [monaco, errors]);
|
||||
|
||||
const flowDiagnosticDisable = [
|
||||
|
|
@ -64,11 +64,11 @@ export default function Input({ errors, language }: Props) {
|
|||
8011,
|
||||
8012,
|
||||
8013,
|
||||
...(language === "flow" ? flowDiagnosticDisable : []),
|
||||
...(language === 'flow' ? flowDiagnosticDisable : []),
|
||||
],
|
||||
noSemanticValidation: true,
|
||||
// Monaco can't validate Flow component syntax
|
||||
noSyntaxValidation: language === "flow",
|
||||
noSyntaxValidation: language === 'flow',
|
||||
});
|
||||
}, [monaco, language]);
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ export default function Input({ errors, language }: Props) {
|
|||
if (!value) return;
|
||||
|
||||
dispatchStore({
|
||||
type: "updateFile",
|
||||
type: 'updateFile',
|
||||
payload: {
|
||||
source: value,
|
||||
},
|
||||
|
|
@ -91,11 +91,11 @@ export default function Input({ errors, language }: Props) {
|
|||
target: monaco.languages.typescript.ScriptTarget.ES2015,
|
||||
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
|
||||
jsx: monaco.languages.typescript.JsxEmit.Preserve,
|
||||
typeRoots: ["node_modules/@types"],
|
||||
typeRoots: ['node_modules/@types'],
|
||||
allowSyntheticDefaultImports: true,
|
||||
};
|
||||
monaco.languages.typescript.javascriptDefaults.setCompilerOptions(
|
||||
tscOptions
|
||||
tscOptions,
|
||||
);
|
||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||
...tscOptions,
|
||||
|
|
@ -106,7 +106,7 @@ export default function Input({ errors, language }: Props) {
|
|||
// Add React type declarations to Monaco
|
||||
const reactLib = [
|
||||
React$Types,
|
||||
"file:///node_modules/@types/react/index.d.ts",
|
||||
'file:///node_modules/@types/react/index.d.ts',
|
||||
] as [any, string];
|
||||
monaco.languages.typescript.javascriptDefaults.addExtraLib(...reactLib);
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(...reactLib);
|
||||
|
|
@ -124,17 +124,16 @@ export default function Input({ errors, language }: Props) {
|
|||
<div className="relative flex flex-col flex-none border-r border-gray-200">
|
||||
<Resizable
|
||||
minWidth={650}
|
||||
enable={{ right: true }}
|
||||
enable={{right: true}}
|
||||
// Restrict MonacoEditor's height, since the config autoLayout:true
|
||||
// will grow the editor to fit within parent element
|
||||
className="!h-[calc(100vh_-_3.5rem)]"
|
||||
>
|
||||
className="!h-[calc(100vh_-_3.5rem)]">
|
||||
<MonacoEditor
|
||||
path={"index.js"}
|
||||
path={'index.js'}
|
||||
// .js and .jsx files are specified to be TS so that Monaco can actually
|
||||
// check their syntax using its TS language service. They are still JS files
|
||||
// due to their extensions, so TS language features don't work.
|
||||
language={"javascript"}
|
||||
language={'javascript'}
|
||||
value={store.source}
|
||||
onMount={handleMount}
|
||||
onChange={handleChange}
|
||||
|
|
|
|||
|
|
@ -5,46 +5,46 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import generate from "@babel/generator";
|
||||
import * as t from "@babel/types";
|
||||
import generate from '@babel/generator';
|
||||
import * as t from '@babel/types';
|
||||
import {
|
||||
CodeIcon,
|
||||
DocumentAddIcon,
|
||||
InformationCircleIcon,
|
||||
} from "@heroicons/react/outline";
|
||||
import MonacoEditor, { DiffEditor } from "@monaco-editor/react";
|
||||
import { type CompilerError } from "babel-plugin-react-compiler/src";
|
||||
import parserBabel from "prettier/plugins/babel";
|
||||
import * as prettierPluginEstree from "prettier/plugins/estree";
|
||||
import * as prettier from "prettier/standalone";
|
||||
import { memo, useEffect, useState } from "react";
|
||||
import { type Store } from "../../lib/stores";
|
||||
import TabbedWindow from "../TabbedWindow";
|
||||
import { monacoOptions } from "./monacoOptions";
|
||||
} from '@heroicons/react/outline';
|
||||
import MonacoEditor, {DiffEditor} from '@monaco-editor/react';
|
||||
import {type CompilerError} from 'babel-plugin-react-compiler/src';
|
||||
import parserBabel from 'prettier/plugins/babel';
|
||||
import * as prettierPluginEstree from 'prettier/plugins/estree';
|
||||
import * as prettier from 'prettier/standalone';
|
||||
import {memo, useEffect, useState} from 'react';
|
||||
import {type Store} from '../../lib/stores';
|
||||
import TabbedWindow from '../TabbedWindow';
|
||||
import {monacoOptions} from './monacoOptions';
|
||||
const MemoizedOutput = memo(Output);
|
||||
|
||||
export default MemoizedOutput;
|
||||
|
||||
export type PrintedCompilerPipelineValue =
|
||||
| {
|
||||
kind: "ast";
|
||||
kind: 'ast';
|
||||
name: string;
|
||||
fnName: string | null;
|
||||
value: t.FunctionDeclaration;
|
||||
}
|
||||
| {
|
||||
kind: "hir";
|
||||
kind: 'hir';
|
||||
name: string;
|
||||
fnName: string | null;
|
||||
value: string;
|
||||
}
|
||||
| { kind: "reactive"; name: string; fnName: string | null; value: string }
|
||||
| { kind: "debug"; name: string; fnName: string | null; value: string };
|
||||
| {kind: 'reactive'; name: string; fnName: string | null; value: string}
|
||||
| {kind: 'debug'; name: string; fnName: string | null; value: string};
|
||||
|
||||
export type CompilerOutput =
|
||||
| { kind: "ok"; results: Map<string, PrintedCompilerPipelineValue[]> }
|
||||
| {kind: 'ok'; results: Map<string, PrintedCompilerPipelineValue[]>}
|
||||
| {
|
||||
kind: "err";
|
||||
kind: 'err';
|
||||
results: Map<string, PrintedCompilerPipelineValue[]>;
|
||||
error: CompilerError;
|
||||
};
|
||||
|
|
@ -63,7 +63,7 @@ async function tabify(source: string, compilerOutput: CompilerOutput) {
|
|||
for (const [passName, results] of compilerOutput.results) {
|
||||
for (const result of results) {
|
||||
switch (result.kind) {
|
||||
case "hir": {
|
||||
case 'hir': {
|
||||
const prev = concattedResults.get(result.name);
|
||||
const next = result.value;
|
||||
const identName = `function ${result.fnName}`;
|
||||
|
|
@ -74,7 +74,7 @@ async function tabify(source: string, compilerOutput: CompilerOutput) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case "reactive": {
|
||||
case 'reactive': {
|
||||
const prev = concattedResults.get(passName);
|
||||
const next = result.value;
|
||||
if (prev != null) {
|
||||
|
|
@ -84,30 +84,29 @@ async function tabify(source: string, compilerOutput: CompilerOutput) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case "ast":
|
||||
case 'ast':
|
||||
topLevelFnDecls.push(result.value);
|
||||
break;
|
||||
case "debug": {
|
||||
case 'debug': {
|
||||
concattedResults.set(passName, result.value);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const _: never = result;
|
||||
throw new Error("Unexpected result kind");
|
||||
throw new Error('Unexpected result kind');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let lastPassOutput: string | null = null;
|
||||
let nonDiffPasses = ["HIR", "BuildReactiveFunction", "EnvironmentConfig"];
|
||||
let nonDiffPasses = ['HIR', 'BuildReactiveFunction', 'EnvironmentConfig'];
|
||||
for (const [passName, text] of concattedResults) {
|
||||
tabs.set(
|
||||
passName,
|
||||
<TextTabContent
|
||||
output={text}
|
||||
diff={lastPassOutput}
|
||||
showInfoPanel={!nonDiffPasses.includes(passName)}
|
||||
></TextTabContent>
|
||||
showInfoPanel={!nonDiffPasses.includes(passName)}></TextTabContent>,
|
||||
);
|
||||
lastPassOutput = text;
|
||||
}
|
||||
|
|
@ -116,25 +115,24 @@ async function tabify(source: string, compilerOutput: CompilerOutput) {
|
|||
// Make a synthetic Program so we can have a single AST with all the top level
|
||||
// FunctionDeclarations
|
||||
const ast = t.program(topLevelFnDecls);
|
||||
const { code, sourceMapUrl } = await codegen(ast, source);
|
||||
const {code, sourceMapUrl} = await codegen(ast, source);
|
||||
reorderedTabs.set(
|
||||
"JS",
|
||||
'JS',
|
||||
<TextTabContent
|
||||
output={code}
|
||||
diff={null}
|
||||
showInfoPanel={false}
|
||||
></TextTabContent>
|
||||
showInfoPanel={false}></TextTabContent>,
|
||||
);
|
||||
if (sourceMapUrl) {
|
||||
reorderedTabs.set(
|
||||
"SourceMap",
|
||||
'SourceMap',
|
||||
<>
|
||||
<iframe
|
||||
src={sourceMapUrl}
|
||||
className="w-full h-monaco_small sm:h-monaco"
|
||||
title="Generated Code"
|
||||
/>
|
||||
</>
|
||||
</>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -146,23 +144,23 @@ async function tabify(source: string, compilerOutput: CompilerOutput) {
|
|||
|
||||
async function codegen(
|
||||
ast: t.Program,
|
||||
source: string
|
||||
): Promise<{ code: any; sourceMapUrl: string | null }> {
|
||||
source: string,
|
||||
): Promise<{code: any; sourceMapUrl: string | null}> {
|
||||
const generated = generate(
|
||||
ast,
|
||||
{ sourceMaps: true, sourceFileName: "input.js" },
|
||||
source
|
||||
{sourceMaps: true, sourceFileName: 'input.js'},
|
||||
source,
|
||||
);
|
||||
const sourceMapUrl = getSourceMapUrl(
|
||||
generated.code,
|
||||
JSON.stringify(generated.map)
|
||||
JSON.stringify(generated.map),
|
||||
);
|
||||
const codegenOutput = await prettier.format(generated.code, {
|
||||
semi: true,
|
||||
parser: "babel",
|
||||
parser: 'babel',
|
||||
plugins: [parserBabel, prettierPluginEstree],
|
||||
});
|
||||
return { code: codegenOutput, sourceMapUrl };
|
||||
return {code: codegenOutput, sourceMapUrl};
|
||||
}
|
||||
|
||||
function utf16ToUTF8(s: string): string {
|
||||
|
|
@ -173,27 +171,27 @@ function getSourceMapUrl(code: string, map: string): string | null {
|
|||
code = utf16ToUTF8(code);
|
||||
map = utf16ToUTF8(map);
|
||||
return `https://evanw.github.io/source-map-visualization/#${btoa(
|
||||
`${code.length}\0${code}${map.length}\0${map}`
|
||||
`${code.length}\0${code}${map.length}\0${map}`,
|
||||
)}`;
|
||||
}
|
||||
|
||||
function Output({ store, compilerOutput }: Props) {
|
||||
const [tabsOpen, setTabsOpen] = useState<Set<string>>(() => new Set(["JS"]));
|
||||
function Output({store, compilerOutput}: Props) {
|
||||
const [tabsOpen, setTabsOpen] = useState<Set<string>>(() => new Set(['JS']));
|
||||
const [tabs, setTabs] = useState<Map<string, React.ReactNode>>(
|
||||
() => new Map()
|
||||
() => new Map(),
|
||||
);
|
||||
useEffect(() => {
|
||||
tabify(store.source, compilerOutput).then((tabs) => {
|
||||
tabify(store.source, compilerOutput).then(tabs => {
|
||||
setTabs(tabs);
|
||||
});
|
||||
}, [store.source, compilerOutput]);
|
||||
|
||||
const changedPasses: Set<string> = new Set(["JS", "HIR"]); // Initial and final passes should always be bold
|
||||
let lastResult: string = "";
|
||||
const changedPasses: Set<string> = new Set(['JS', 'HIR']); // Initial and final passes should always be bold
|
||||
let lastResult: string = '';
|
||||
for (const [passName, results] of compilerOutput.results) {
|
||||
for (const result of results) {
|
||||
let currResult = "";
|
||||
if (result.kind === "hir" || result.kind === "reactive") {
|
||||
let currResult = '';
|
||||
if (result.kind === 'hir' || result.kind === 'reactive') {
|
||||
currResult += `function ${result.fnName}\n\n${result.value}`;
|
||||
}
|
||||
if (currResult !== lastResult) {
|
||||
|
|
@ -212,18 +210,16 @@ function Output({ store, compilerOutput }: Props) {
|
|||
tabs={tabs}
|
||||
changedPasses={changedPasses}
|
||||
/>
|
||||
{compilerOutput.kind === "err" ? (
|
||||
{compilerOutput.kind === 'err' ? (
|
||||
<div
|
||||
className="flex flex-wrap absolute bottom-0 bg-white grow border-y border-grey-200 transition-all ease-in"
|
||||
style={{ width: "calc(100vw - 650px)" }}
|
||||
>
|
||||
style={{width: 'calc(100vw - 650px)'}}>
|
||||
<div className="w-full p-4 basis-full border-b">
|
||||
<h2>COMPILER ERRORS</h2>
|
||||
</div>
|
||||
<pre
|
||||
className="p-4 basis-full text-red-600 overflow-y-scroll whitespace-pre-wrap"
|
||||
style={{ width: "calc(100vw - 650px)", height: "150px" }}
|
||||
>
|
||||
style={{width: 'calc(100vw - 650px)', height: '150px'}}>
|
||||
<code>{compilerOutput.error.toString()}</code>
|
||||
</pre>
|
||||
</div>
|
||||
|
|
@ -251,8 +247,7 @@ function TextTabContent({
|
|||
{diff != null && output !== diff ? (
|
||||
<button
|
||||
className="flex items-center gap-1 transition-colors duration-150 ease-in text-secondary hover:text-link"
|
||||
onClick={() => setDiffMode((diffMode) => !diffMode)}
|
||||
>
|
||||
onClick={() => setDiffMode(diffMode => !diffMode)}>
|
||||
{!diffMode ? (
|
||||
<>
|
||||
<DocumentAddIcon className="w-5 h-5" /> Show Diff
|
||||
|
|
@ -280,7 +275,7 @@ function TextTabContent({
|
|||
options={{
|
||||
...monacoOptions,
|
||||
readOnly: true,
|
||||
lineNumbers: "off",
|
||||
lineNumbers: 'off',
|
||||
glyphMargin: false,
|
||||
// Undocumented see https://github.com/Microsoft/vscode/issues/30795#issuecomment-410998882
|
||||
lineDecorationsWidth: 0,
|
||||
|
|
@ -294,7 +289,7 @@ function TextTabContent({
|
|||
options={{
|
||||
...monacoOptions,
|
||||
readOnly: true,
|
||||
lineNumbers: "off",
|
||||
lineNumbers: 'off',
|
||||
glyphMargin: false,
|
||||
// Undocumented see https://github.com/Microsoft/vscode/issues/30795#issuecomment-410998882
|
||||
lineDecorationsWidth: 0,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
// monaco-editor is currently not compatible with ssr
|
||||
// https://github.com/vercel/next.js/issues/31692
|
||||
const Editor = dynamic(() => import("./EditorImpl"), {
|
||||
const Editor = dynamic(() => import('./EditorImpl'), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type { EditorProps } from "@monaco-editor/react";
|
||||
import type {EditorProps} from '@monaco-editor/react';
|
||||
|
||||
export const monacoOptions: Partial<EditorProps["options"]> = {
|
||||
export const monacoOptions: Partial<EditorProps['options']> = {
|
||||
fontSize: 14,
|
||||
padding: { top: 8 },
|
||||
padding: {top: 8},
|
||||
scrollbar: {
|
||||
verticalScrollbarSize: 10,
|
||||
alwaysConsumeMouseWheel: false,
|
||||
|
|
@ -22,11 +22,11 @@ export const monacoOptions: Partial<EditorProps["options"]> = {
|
|||
fontFamily: '"Source Code Pro", monospace',
|
||||
glyphMargin: true,
|
||||
|
||||
autoClosingBrackets: "languageDefined",
|
||||
autoClosingDelete: "always",
|
||||
autoClosingOvertype: "always",
|
||||
autoClosingBrackets: 'languageDefined',
|
||||
autoClosingDelete: 'always',
|
||||
autoClosingOvertype: 'always',
|
||||
|
||||
automaticLayout: true,
|
||||
wordWrap: "on",
|
||||
wrappingIndent: "deepIndent",
|
||||
wordWrap: 'on',
|
||||
wrappingIndent: 'deepIndent',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,24 +5,24 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { RefreshIcon, ShareIcon } from "@heroicons/react/outline";
|
||||
import { CheckIcon } from "@heroicons/react/solid";
|
||||
import clsx from "clsx";
|
||||
import Link from "next/link";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { useState } from "react";
|
||||
import { defaultStore } from "../lib/defaultStore";
|
||||
import { IconGitHub } from "./Icons/IconGitHub";
|
||||
import Logo from "./Logo";
|
||||
import { useStoreDispatch } from "./StoreContext";
|
||||
import {RefreshIcon, ShareIcon} from '@heroicons/react/outline';
|
||||
import {CheckIcon} from '@heroicons/react/solid';
|
||||
import clsx from 'clsx';
|
||||
import Link from 'next/link';
|
||||
import {useSnackbar} from 'notistack';
|
||||
import {useState} from 'react';
|
||||
import {defaultStore} from '../lib/defaultStore';
|
||||
import {IconGitHub} from './Icons/IconGitHub';
|
||||
import Logo from './Logo';
|
||||
import {useStoreDispatch} from './StoreContext';
|
||||
|
||||
export default function Header() {
|
||||
const [showCheck, setShowCheck] = useState(false);
|
||||
const dispatchStore = useStoreDispatch();
|
||||
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
|
||||
const {enqueueSnackbar, closeSnackbar} = useSnackbar();
|
||||
|
||||
const handleReset = () => {
|
||||
if (confirm("Are you sure you want to reset the playground?")) {
|
||||
if (confirm('Are you sure you want to reset the playground?')) {
|
||||
/*
|
||||
Close open snackbars if any. This is necessary because when displaying
|
||||
outputs (Preview or not), we only close previous snackbars if we received
|
||||
|
|
@ -31,13 +31,13 @@ export default function Header() {
|
|||
such as "Bad URL" will be closed by the outputs calling `closeSnackbar`.
|
||||
*/
|
||||
closeSnackbar();
|
||||
dispatchStore({ type: "setStore", payload: { store: defaultStore } });
|
||||
dispatchStore({type: 'setStore', payload: {store: defaultStore}});
|
||||
}
|
||||
};
|
||||
|
||||
const handleShare = () => {
|
||||
navigator.clipboard.writeText(location.href).then(() => {
|
||||
enqueueSnackbar("URL copied to clipboard");
|
||||
enqueueSnackbar('URL copied to clipboard');
|
||||
setShowCheck(true);
|
||||
// Show the check mark icon briefly after URL is copied
|
||||
setTimeout(() => setShowCheck(false), 1000);
|
||||
|
|
@ -49,8 +49,8 @@ export default function Header() {
|
|||
<div className="flex items-center flex-none h-full gap-2 text-lg">
|
||||
<Logo
|
||||
className={clsx(
|
||||
"w-8 h-8 text-link",
|
||||
process.env.NODE_ENV === "development" && "text-yellow-600"
|
||||
'w-8 h-8 text-link',
|
||||
process.env.NODE_ENV === 'development' && 'text-yellow-600',
|
||||
)}
|
||||
/>
|
||||
<p className="hidden select-none sm:block">React Compiler Playground</p>
|
||||
|
|
@ -60,8 +60,7 @@ export default function Header() {
|
|||
title="Reset Playground"
|
||||
aria-label="Reset Playground"
|
||||
className="flex items-center gap-1 transition-colors duration-150 ease-in text-secondary hover:text-link"
|
||||
onClick={handleReset}
|
||||
>
|
||||
onClick={handleReset}>
|
||||
<RefreshIcon className="w-5 h-5" />
|
||||
<p className="hidden sm:block">Reset</p>
|
||||
</button>
|
||||
|
|
@ -70,8 +69,7 @@ export default function Header() {
|
|||
aria-label="Copy sharable URL"
|
||||
className="flex items-center gap-1 transition-colors duration-150 ease-in text-secondary hover:text-link"
|
||||
onClick={handleShare}
|
||||
disabled={showCheck}
|
||||
>
|
||||
disabled={showCheck}>
|
||||
{!showCheck ? (
|
||||
<ShareIcon className="w-5 h-5" />
|
||||
) : (
|
||||
|
|
@ -84,8 +82,7 @@ export default function Header() {
|
|||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
aria-label="Open on GitHub"
|
||||
className="flex items-center gap-1 transition-colors duration-150 ease-in text-secondary hover:text-link"
|
||||
>
|
||||
className="flex items-center gap-1 transition-colors duration-150 ease-in text-secondary hover:text-link">
|
||||
<IconGitHub />
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { memo } from "react";
|
||||
import {memo} from 'react';
|
||||
|
||||
export const IconGitHub = memo<JSX.IntrinsicElements["svg"]>(
|
||||
export const IconGitHub = memo<JSX.IntrinsicElements['svg']>(
|
||||
function IconGitHub(props) {
|
||||
return (
|
||||
<svg
|
||||
|
|
@ -16,10 +16,9 @@ export const IconGitHub = memo<JSX.IntrinsicElements["svg"]>(
|
|||
height="1.5em"
|
||||
viewBox="0 -2 24 24"
|
||||
fill="currentColor"
|
||||
{...props}
|
||||
>
|
||||
{...props}>
|
||||
<path d="M10 0a10 10 0 0 0-3.16 19.49c.5.1.68-.22.68-.48l-.01-1.7c-2.78.6-3.37-1.34-3.37-1.34-.46-1.16-1.11-1.47-1.11-1.47-.9-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.9 1.52 2.34 1.08 2.91.83.1-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.94 0-1.1.39-1.99 1.03-2.69a3.6 3.6 0 0 1 .1-2.64s.84-.27 2.75 1.02a9.58 9.58 0 0 1 5 0c1.91-1.3 2.75-1.02 2.75-1.02.55 1.37.2 2.4.1 2.64.64.7 1.03 1.6 1.03 2.69 0 3.84-2.34 4.68-4.57 4.93.36.31.68.92.68 1.85l-.01 2.75c0 .26.18.58.69.48A10 10 0 0 0 10 0"></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,14 +7,13 @@
|
|||
|
||||
// https://github.com/reactjs/reactjs.org/blob/main/beta/src/components/Logo.tsx
|
||||
|
||||
export default function Logo(props: JSX.IntrinsicElements["svg"]) {
|
||||
export default function Logo(props: JSX.IntrinsicElements['svg']) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 410 369"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
{...props}>
|
||||
<path
|
||||
d="M204.995 224.552C226.56 224.552 244.042 207.07 244.042 185.506C244.042 163.941 226.56 146.459 204.995 146.459C183.43 146.459 165.948 163.941 165.948 185.506C165.948 207.07 183.43 224.552 204.995 224.552Z"
|
||||
fill="currentColor"
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@ import {
|
|||
ExclamationIcon,
|
||||
InformationCircleIcon,
|
||||
XIcon,
|
||||
} from "@heroicons/react/solid";
|
||||
import { CustomContentProps, SnackbarContent, useSnackbar } from "notistack";
|
||||
import { forwardRef } from "react";
|
||||
import { MessageLevel, MessageSource } from "../lib/stores";
|
||||
} from '@heroicons/react/solid';
|
||||
import {CustomContentProps, SnackbarContent, useSnackbar} from 'notistack';
|
||||
import {forwardRef} from 'react';
|
||||
import {MessageLevel, MessageSource} from '../lib/stores';
|
||||
|
||||
// https://notistack.com/examples/advanced/custom-component#custom-variant-(typescript)
|
||||
declare module "notistack" {
|
||||
declare module 'notistack' {
|
||||
interface VariantOverrides {
|
||||
message: {
|
||||
title: string;
|
||||
|
|
@ -34,15 +34,14 @@ interface MessageProps extends CustomContentProps {
|
|||
}
|
||||
|
||||
const Message = forwardRef<HTMLDivElement, MessageProps>(
|
||||
({ id, title, level, source, codeframe }, ref) => {
|
||||
const { closeSnackbar } = useSnackbar();
|
||||
({id, title, level, source, codeframe}, ref) => {
|
||||
const {closeSnackbar} = useSnackbar();
|
||||
const isDismissible = source !== MessageSource.Playground;
|
||||
|
||||
return (
|
||||
<SnackbarContent
|
||||
ref={ref}
|
||||
className="flex items-start justify-between gap-3 px-4 py-3 text-sm bg-white border rounded-md shadow w-toast"
|
||||
>
|
||||
className="flex items-start justify-between gap-3 px-4 py-3 text-sm bg-white border rounded-md shadow w-toast">
|
||||
<div className="flex gap-3 w-toast-body">
|
||||
{level === MessageLevel.Warning ? (
|
||||
<div className="flex items-center justify-center flex-none rounded-md w-7 h-7 bg-amber-100">
|
||||
|
|
@ -69,16 +68,15 @@ const Message = forwardRef<HTMLDivElement, MessageProps>(
|
|||
{isDismissible ? (
|
||||
<button
|
||||
className="flex items-center justify-center flex-none transition-colors duration-150 ease-in rounded-md justify-self-end group w-7 h-7 hover:bg-gray-200"
|
||||
onClick={() => closeSnackbar(id)}
|
||||
>
|
||||
onClick={() => closeSnackbar(id)}>
|
||||
<XIcon className="w-5 h-5 fill-gray-500 group-hover:fill-gray-800" />
|
||||
</button>
|
||||
) : null}
|
||||
</SnackbarContent>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
Message.displayName = "MessageComponent";
|
||||
Message.displayName = 'MessageComponent';
|
||||
|
||||
export default Message;
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type { Dispatch, ReactNode } from "react";
|
||||
import { useReducer } from "react";
|
||||
import createContext from "../lib/createContext";
|
||||
import { emptyStore } from "../lib/defaultStore";
|
||||
import type { Store } from "../lib/stores";
|
||||
import { saveStore } from "../lib/stores";
|
||||
import type {Dispatch, ReactNode} from 'react';
|
||||
import {useReducer} from 'react';
|
||||
import createContext from '../lib/createContext';
|
||||
import {emptyStore} from '../lib/defaultStore';
|
||||
import type {Store} from '../lib/stores';
|
||||
import {saveStore} from '../lib/stores';
|
||||
|
||||
const StoreContext = createContext<Store>();
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ export const useStoreDispatch = StoreDispatchContext.useContext;
|
|||
/**
|
||||
* Make Store and dispatch function available to all sub-components in children.
|
||||
*/
|
||||
export function StoreProvider({ children }: { children: ReactNode }) {
|
||||
export function StoreProvider({children}: {children: ReactNode}) {
|
||||
const [store, dispatch] = useReducer(storeReducer, emptyStore);
|
||||
|
||||
return (
|
||||
|
|
@ -43,13 +43,13 @@ export function StoreProvider({ children }: { children: ReactNode }) {
|
|||
|
||||
type ReducerAction =
|
||||
| {
|
||||
type: "setStore";
|
||||
type: 'setStore';
|
||||
payload: {
|
||||
store: Store;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: "updateFile";
|
||||
type: 'updateFile';
|
||||
payload: {
|
||||
source: string;
|
||||
};
|
||||
|
|
@ -57,14 +57,14 @@ type ReducerAction =
|
|||
|
||||
function storeReducer(store: Store, action: ReducerAction): Store {
|
||||
switch (action.type) {
|
||||
case "setStore": {
|
||||
case 'setStore': {
|
||||
const newStore = action.payload.store;
|
||||
|
||||
saveStore(newStore);
|
||||
return newStore;
|
||||
}
|
||||
case "updateFile": {
|
||||
const { source } = action.payload;
|
||||
case 'updateFile': {
|
||||
const {source} = action.payload;
|
||||
|
||||
const newStore = {
|
||||
...store,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { Resizable } from "re-resizable";
|
||||
import React, { useCallback } from "react";
|
||||
import {Resizable} from 're-resizable';
|
||||
import React, {useCallback} from 'react';
|
||||
|
||||
type TabsRecord = Map<string, React.ReactNode>;
|
||||
|
||||
|
|
@ -21,15 +21,14 @@ export default function TabbedWindow(props: {
|
|||
return (
|
||||
<div
|
||||
className="flex items-center justify-center"
|
||||
style={{ width: "calc(100vw - 650px)" }}
|
||||
>
|
||||
style={{width: 'calc(100vw - 650px)'}}>
|
||||
No compiler output detected, see errors below
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-row">
|
||||
{Array.from(props.tabs.keys()).map((name) => {
|
||||
{Array.from(props.tabs.keys()).map(name => {
|
||||
return (
|
||||
<TabbedWindowItem
|
||||
name={name}
|
||||
|
|
@ -73,15 +72,14 @@ function TabbedWindowItem({
|
|||
return (
|
||||
<div key={name} className="flex flex-row">
|
||||
{isShow ? (
|
||||
<Resizable className="border-r" minWidth={550} enable={{ right: true }}>
|
||||
<Resizable className="border-r" minWidth={550} enable={{right: true}}>
|
||||
<h2
|
||||
title="Minimize tab"
|
||||
aria-label="Minimize tab"
|
||||
onClick={toggleTabs}
|
||||
className={`p-4 duration-150 ease-in border-b cursor-pointer border-grey-200 ${
|
||||
hasChanged ? "font-bold" : "font-light"
|
||||
} text-secondary hover:text-link`}
|
||||
>
|
||||
hasChanged ? 'font-bold' : 'font-light'
|
||||
} text-secondary hover:text-link`}>
|
||||
- {name}
|
||||
</h2>
|
||||
{tabs.get(name) ?? <div>No output for {name}</div>}
|
||||
|
|
@ -91,12 +89,11 @@ function TabbedWindowItem({
|
|||
<button
|
||||
title={`Expand compiler tab: ${name}`}
|
||||
aria-label={`Expand compiler tab: ${name}`}
|
||||
style={{ transform: "rotate(90deg) translate(-50%)" }}
|
||||
style={{transform: 'rotate(90deg) translate(-50%)'}}
|
||||
onClick={toggleTabs}
|
||||
className={`flex-grow-0 w-5 transition-colors duration-150 ease-in ${
|
||||
hasChanged ? "font-bold" : "font-light"
|
||||
} text-secondary hover:text-link`}
|
||||
>
|
||||
hasChanged ? 'font-bold' : 'font-light'
|
||||
} text-secondary hover:text-link`}>
|
||||
{name}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
export { default as Editor } from "./Editor";
|
||||
export { default as Header } from "./Header";
|
||||
export { StoreProvider } from "./StoreContext";
|
||||
export {default as Editor} from './Editor';
|
||||
export {default as Header} from './Header';
|
||||
export {StoreProvider} from './StoreContext';
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
export { default as useMountEffect } from "./useMountEffect";
|
||||
export {default as useMountEffect} from './useMountEffect';
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type { EffectCallback } from "react";
|
||||
import { useEffect } from "react";
|
||||
import type {EffectCallback} from 'react';
|
||||
import {useEffect} from 'react';
|
||||
|
||||
export default function useMountEffect(effect: EffectCallback) {
|
||||
return useEffect(effect, []);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Replacement to React.createContext.
|
||||
|
|
@ -29,9 +29,9 @@ export default function createContext<T>() {
|
|||
function useContext() {
|
||||
const c = React.useContext(context);
|
||||
if (!c)
|
||||
throw new Error("useContext must be within a Provider with a value");
|
||||
throw new Error('useContext must be within a Provider with a value');
|
||||
return c;
|
||||
}
|
||||
|
||||
return { useContext, Provider: context.Provider };
|
||||
return {useContext, Provider: context.Provider};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type { Store } from "./stores";
|
||||
import type {Store} from './stores';
|
||||
|
||||
const index = `\
|
||||
export default function MyApp() {
|
||||
|
|
@ -18,5 +18,5 @@ export const defaultStore: Store = {
|
|||
};
|
||||
|
||||
export const emptyStore: Store = {
|
||||
source: "",
|
||||
source: '',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,16 +5,16 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { Monaco } from "@monaco-editor/react";
|
||||
import {Monaco} from '@monaco-editor/react';
|
||||
import {
|
||||
CompilerErrorDetail,
|
||||
ErrorSeverity,
|
||||
} from "babel-plugin-react-compiler/src";
|
||||
import { MarkerSeverity, type editor } from "monaco-editor";
|
||||
} from 'babel-plugin-react-compiler/src';
|
||||
import {MarkerSeverity, type editor} from 'monaco-editor';
|
||||
|
||||
function mapReactCompilerSeverityToMonaco(
|
||||
level: ErrorSeverity,
|
||||
monaco: Monaco
|
||||
monaco: Monaco,
|
||||
): MarkerSeverity {
|
||||
switch (level) {
|
||||
case ErrorSeverity.Todo:
|
||||
|
|
@ -26,9 +26,9 @@ function mapReactCompilerSeverityToMonaco(
|
|||
|
||||
function mapReactCompilerDiagnosticToMonacoMarker(
|
||||
detail: CompilerErrorDetail,
|
||||
monaco: Monaco
|
||||
monaco: Monaco,
|
||||
): editor.IMarkerData | null {
|
||||
if (detail.loc == null || typeof detail.loc === "symbol") {
|
||||
if (detail.loc == null || typeof detail.loc === 'symbol') {
|
||||
return null;
|
||||
}
|
||||
const severity = mapReactCompilerSeverityToMonaco(detail.severity, monaco);
|
||||
|
|
@ -63,27 +63,27 @@ export function renderReactCompilerMarkers({
|
|||
markers.push(marker);
|
||||
}
|
||||
if (markers.length > 0) {
|
||||
monaco.editor.setModelMarkers(model, "owner", markers);
|
||||
const newDecorations = markers.map((marker) => {
|
||||
monaco.editor.setModelMarkers(model, 'owner', markers);
|
||||
const newDecorations = markers.map(marker => {
|
||||
return {
|
||||
range: new monaco.Range(
|
||||
marker.startLineNumber,
|
||||
marker.startColumn,
|
||||
marker.endLineNumber,
|
||||
marker.endColumn
|
||||
marker.endColumn,
|
||||
),
|
||||
options: {
|
||||
isWholeLine: true,
|
||||
glyphMarginClassName: "bg-red-300",
|
||||
glyphMarginClassName: 'bg-red-300',
|
||||
},
|
||||
};
|
||||
});
|
||||
decorations = model.deltaDecorations(decorations, newDecorations);
|
||||
} else {
|
||||
monaco.editor.setModelMarkers(model, "owner", []);
|
||||
monaco.editor.setModelMarkers(model, 'owner', []);
|
||||
decorations = model.deltaDecorations(
|
||||
model.getAllDecorations().map((d) => d.id),
|
||||
[]
|
||||
model.getAllDecorations().map(d => d.id),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
export * from "./messages";
|
||||
export * from "./store";
|
||||
export * from './messages';
|
||||
export * from './store';
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ export interface Message {
|
|||
export function createMessage(
|
||||
message: string,
|
||||
level: MessageLevel,
|
||||
source: MessageSource
|
||||
source: MessageSource,
|
||||
): Message {
|
||||
const [title, ...body] = message.split("\n");
|
||||
const codeframe = body.length > 0 ? body.join("\n") : undefined;
|
||||
const [title, ...body] = message.split('\n');
|
||||
const codeframe = body.length > 0 ? body.join('\n') : undefined;
|
||||
|
||||
return {
|
||||
source,
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import invariant from "invariant";
|
||||
import invariant from 'invariant';
|
||||
import {
|
||||
compressToEncodedURIComponent,
|
||||
decompressFromEncodedURIComponent,
|
||||
} from "lz-string";
|
||||
import { defaultStore } from "../defaultStore";
|
||||
} from 'lz-string';
|
||||
import {defaultStore} from '../defaultStore';
|
||||
|
||||
/**
|
||||
* Global Store for Playground
|
||||
|
|
@ -30,8 +30,8 @@ export function decodeStore(hash: string): Store {
|
|||
*/
|
||||
export function saveStore(store: Store) {
|
||||
const hash = encodeStore(store);
|
||||
localStorage.setItem("playgroundStore", hash);
|
||||
history.replaceState({}, "", `#${hash}`);
|
||||
localStorage.setItem('playgroundStore', hash);
|
||||
history.replaceState({}, '', `#${hash}`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -41,9 +41,9 @@ export function saveStore(store: Store) {
|
|||
function isValidStore(raw: unknown): raw is Store {
|
||||
return (
|
||||
raw != null &&
|
||||
typeof raw == "object" &&
|
||||
"source" in raw &&
|
||||
typeof raw["source"] === "string"
|
||||
typeof raw == 'object' &&
|
||||
'source' in raw &&
|
||||
typeof raw['source'] === 'string'
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ function isValidStore(raw: unknown): raw is Store {
|
|||
*/
|
||||
export function initStoreFromUrlOrLocalStorage(): Store {
|
||||
const encodedSourceFromUrl = location.hash.slice(1);
|
||||
const encodedSourceFromLocal = localStorage.getItem("playgroundStore");
|
||||
const encodedSourceFromLocal = localStorage.getItem('playgroundStore');
|
||||
const encodedSource = encodedSourceFromUrl || encodedSourceFromLocal;
|
||||
|
||||
// No data in the URL and no data in the localStorage to fallback to.
|
||||
|
|
@ -62,6 +62,6 @@ export function initStoreFromUrlOrLocalStorage(): Store {
|
|||
|
||||
const raw = decodeStore(encodedSource);
|
||||
|
||||
invariant(isValidStore(raw), "Invalid Store");
|
||||
invariant(isValidStore(raw), 'Invalid Store');
|
||||
return raw;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
|
||||
const path = require("path");
|
||||
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
|
||||
const path = require('path');
|
||||
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
|
|
@ -14,24 +14,24 @@ const nextConfig = {
|
|||
// Load *.d.ts files as strings using https://webpack.js.org/guides/asset-modules/#source-assets.
|
||||
config.module.rules.push({
|
||||
test: /\.d\.ts/,
|
||||
type: "asset/source",
|
||||
type: 'asset/source',
|
||||
});
|
||||
|
||||
// Monaco Editor
|
||||
if (!options.isServer) {
|
||||
config.plugins.push(
|
||||
new MonacoWebpackPlugin({
|
||||
languages: ["typescript", "javascript"],
|
||||
filename: "static/[name].worker.js",
|
||||
languages: ['typescript', 'javascript'],
|
||||
filename: 'static/[name].worker.js',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
config.resolve.alias = {
|
||||
...config.resolve.alias,
|
||||
"react-compiler-runtime": path.resolve(
|
||||
'react-compiler-runtime': path.resolve(
|
||||
__dirname,
|
||||
"../../packages/react-compiler-runtime"
|
||||
'../../packages/react-compiler-runtime'
|
||||
),
|
||||
};
|
||||
config.resolve.fallback = {
|
||||
|
|
@ -43,7 +43,7 @@ const nextConfig = {
|
|||
return config;
|
||||
},
|
||||
|
||||
transpilePackages: ["monaco-editor"],
|
||||
transpilePackages: ['monaco-editor'],
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
import path from "path";
|
||||
import {defineConfig, devices} from '@playwright/test';
|
||||
import path from 'path';
|
||||
|
||||
// Use process.env.PORT by default and fallback to port 3000
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
|
@ -19,19 +19,19 @@ export default defineConfig({
|
|||
// Timeout per test
|
||||
timeout: 30 * 1000,
|
||||
// Test directory
|
||||
testDir: path.join(__dirname, "__tests__/e2e"),
|
||||
testDir: path.join(__dirname, '__tests__/e2e'),
|
||||
// If a test fails, retry it additional 2 times
|
||||
retries: 2,
|
||||
// Artifacts folder where screenshots, videos, and traces are stored.
|
||||
outputDir: "test-results/",
|
||||
outputDir: 'test-results/',
|
||||
// Note: we only use text snapshots, so its safe to omit the host environment name
|
||||
snapshotPathTemplate: "{testDir}/__snapshots__/{testFilePath}/{arg}{ext}",
|
||||
snapshotPathTemplate: '{testDir}/__snapshots__/{testFilePath}/{arg}{ext}',
|
||||
|
||||
// Run your local dev server before starting the tests:
|
||||
// https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests
|
||||
webServer: {
|
||||
command:
|
||||
"yarn workspace babel-plugin-react-compiler build && yarn workspace react-compiler-runtime build && yarn dev",
|
||||
'yarn workspace babel-plugin-react-compiler build && yarn workspace react-compiler-runtime build && yarn dev',
|
||||
url: baseURL,
|
||||
timeout: 300 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
|
|
@ -44,7 +44,7 @@ export default defineConfig({
|
|||
|
||||
// Retry a test if its failing with enabled tracing. This allows you to analyze the DOM, console logs, network traffic etc.
|
||||
// More information: https://playwright.dev/docs/trace-viewer
|
||||
trace: "retry-with-trace",
|
||||
trace: 'retry-with-trace',
|
||||
|
||||
// All available context options: https://playwright.dev/docs/api/class-browser#browser-new-context
|
||||
// contextOptions: {
|
||||
|
|
@ -54,8 +54,8 @@ export default defineConfig({
|
|||
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
name: 'chromium',
|
||||
use: {...devices['Desktop Chrome']},
|
||||
},
|
||||
// {
|
||||
// name: 'Desktop Firefox',
|
||||
|
|
|
|||
|
|
@ -5,16 +5,16 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const { execSync } = require("child_process");
|
||||
const {execSync} = require('child_process');
|
||||
|
||||
// So that we don't need to check them into the repo.
|
||||
// See https://github.com/reactjs/reactjs.org/blob/main/beta/scripts/downloadFonts.js.
|
||||
execSync(
|
||||
"curl https://conf.reactjs.org/fonts/Optimistic_Display_W_Lt.woff2 --output public/fonts/Optimistic_Display_W_Lt.woff2"
|
||||
'curl https://conf.reactjs.org/fonts/Optimistic_Display_W_Lt.woff2 --output public/fonts/Optimistic_Display_W_Lt.woff2'
|
||||
);
|
||||
execSync(
|
||||
"curl https://conf.reactjs.org/fonts/Optimistic_Display_W_Md.woff2 --output public/fonts/Optimistic_Display_W_Md.woff2"
|
||||
'curl https://conf.reactjs.org/fonts/Optimistic_Display_W_Md.woff2 --output public/fonts/Optimistic_Display_W_Md.woff2'
|
||||
);
|
||||
execSync(
|
||||
"curl https://conf.reactjs.org/fonts/Optimistic_Display_W_Bd.woff2 --output public/fonts/Optimistic_Display_W_Bd.woff2"
|
||||
'curl https://conf.reactjs.org/fonts/Optimistic_Display_W_Bd.woff2 --output public/fonts/Optimistic_Display_W_Bd.woff2'
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,33 +5,33 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const defaultTheme = require("tailwindcss/defaultTheme");
|
||||
const colors = require("./colors");
|
||||
const defaultTheme = require('tailwindcss/defaultTheme');
|
||||
const colors = require('./colors');
|
||||
|
||||
module.exports = {
|
||||
content: [
|
||||
"./app/**/*.{js,ts,jsx,tsx}",
|
||||
"./pages/**/*.{js,ts,jsx,tsx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx}",
|
||||
"./lib/forgetMonacoDiagnostics.ts",
|
||||
'./app/**/*.{js,ts,jsx,tsx}',
|
||||
'./pages/**/*.{js,ts,jsx,tsx}',
|
||||
'./components/**/*.{js,ts,jsx,tsx}',
|
||||
'./lib/forgetMonacoDiagnostics.ts',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors,
|
||||
width: {
|
||||
toast: "min(900px, 100vw - 40px)",
|
||||
"toast-body": "calc(100% - 60px)",
|
||||
"toast-title": "calc(100% - 40px)",
|
||||
toast: 'min(900px, 100vw - 40px)',
|
||||
'toast-body': 'calc(100% - 60px)',
|
||||
'toast-title': 'calc(100% - 40px)',
|
||||
},
|
||||
height: {
|
||||
content: "calc(100vh - 45px)",
|
||||
monaco: "calc(100vh - 93px)",
|
||||
monaco_small: "calc(100vh - 129px)",
|
||||
content: 'calc(100vh - 45px)',
|
||||
monaco: 'calc(100vh - 93px)',
|
||||
monaco_small: 'calc(100vh - 129px)',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: [
|
||||
"Optimistic Display",
|
||||
"-apple-system",
|
||||
'Optimistic Display',
|
||||
'-apple-system',
|
||||
...defaultTheme.fontFamily.sans,
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
/** @type {import('jest').Config} */
|
||||
const config = {
|
||||
projects: ["<rootDir>/scripts/jest/*.config.js"],
|
||||
projects: ['<rootDir>/scripts/jest/*.config.js'],
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
|
|
|||
|
|
@ -5,29 +5,29 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import typescript from "@rollup/plugin-typescript";
|
||||
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
||||
import commonjs from "@rollup/plugin-commonjs";
|
||||
import json from "@rollup/plugin-json";
|
||||
import path from "path";
|
||||
import process from "process";
|
||||
import terser from "@rollup/plugin-terser";
|
||||
import prettier from "rollup-plugin-prettier";
|
||||
import banner2 from "rollup-plugin-banner2";
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import {nodeResolve} from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import json from '@rollup/plugin-json';
|
||||
import path from 'path';
|
||||
import process from 'process';
|
||||
import terser from '@rollup/plugin-terser';
|
||||
import prettier from 'rollup-plugin-prettier';
|
||||
import banner2 from 'rollup-plugin-banner2';
|
||||
|
||||
const NO_INLINE = new Set(["@babel/types"]);
|
||||
const NO_INLINE = new Set(['@babel/types']);
|
||||
|
||||
const DEV_ROLLUP_CONFIG = {
|
||||
input: "src/index.ts",
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
file: "dist/index.js",
|
||||
format: "cjs",
|
||||
file: 'dist/index.js',
|
||||
format: 'cjs',
|
||||
sourcemap: false,
|
||||
exports: "named",
|
||||
exports: 'named',
|
||||
},
|
||||
plugins: [
|
||||
typescript({
|
||||
tsconfig: "./tsconfig.json",
|
||||
tsconfig: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
noEmit: true,
|
||||
},
|
||||
|
|
@ -35,8 +35,8 @@ const DEV_ROLLUP_CONFIG = {
|
|||
json(),
|
||||
nodeResolve({
|
||||
preferBuiltins: true,
|
||||
resolveOnly: (module) => NO_INLINE.has(module) === false,
|
||||
rootDir: path.join(process.cwd(), ".."),
|
||||
resolveOnly: module => NO_INLINE.has(module) === false,
|
||||
rootDir: path.join(process.cwd(), '..'),
|
||||
}),
|
||||
commonjs(),
|
||||
terser({
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type * as BabelCore from "@babel/core";
|
||||
import { NodePath } from "@babel/core";
|
||||
import * as t from "@babel/types";
|
||||
import type * as BabelCore from '@babel/core';
|
||||
import {NodePath} from '@babel/core';
|
||||
import * as t from '@babel/types';
|
||||
|
||||
export default function AnnotateReactCodeBabelPlugin(
|
||||
_babel: typeof BabelCore
|
||||
_babel: typeof BabelCore,
|
||||
): BabelCore.PluginObj {
|
||||
return {
|
||||
name: "annotate-react-code",
|
||||
name: 'annotate-react-code',
|
||||
visitor: {
|
||||
Program(prog): void {
|
||||
annotate(prog);
|
||||
|
|
@ -56,23 +56,23 @@ function buildTypeOfReactForget(): t.Statement {
|
|||
// typeof globalThis[Symbol.for("react_forget")]
|
||||
return t.expressionStatement(
|
||||
t.unaryExpression(
|
||||
"typeof",
|
||||
'typeof',
|
||||
t.memberExpression(
|
||||
t.identifier("globalThis"),
|
||||
t.identifier('globalThis'),
|
||||
t.callExpression(
|
||||
t.memberExpression(
|
||||
t.identifier("Symbol"),
|
||||
t.identifier("for"),
|
||||
t.identifier('Symbol'),
|
||||
t.identifier('for'),
|
||||
false,
|
||||
false,
|
||||
false
|
||||
),
|
||||
[t.stringLiteral("react_forget")]
|
||||
[t.stringLiteral('react_forget')],
|
||||
),
|
||||
true,
|
||||
false
|
||||
false,
|
||||
),
|
||||
true
|
||||
)
|
||||
true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -89,9 +89,9 @@ type BabelFn =
|
|||
| NodePath<t.ArrowFunctionExpression>;
|
||||
|
||||
export function isComponentDeclaration(
|
||||
node: t.FunctionDeclaration
|
||||
node: t.FunctionDeclaration,
|
||||
): node is ComponentDeclaration {
|
||||
return Object.prototype.hasOwnProperty.call(node, "__componentDeclaration");
|
||||
return Object.prototype.hasOwnProperty.call(node, '__componentDeclaration');
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -101,7 +101,7 @@ export function isComponentDeclaration(
|
|||
function isComponentOrHookLike(
|
||||
node: NodePath<
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
>
|
||||
>,
|
||||
): boolean {
|
||||
const functionName = getFunctionName(node);
|
||||
// Check if the name is component or hook like:
|
||||
|
|
@ -114,7 +114,7 @@ function isComponentOrHookLike(
|
|||
* helpers are _usually_ named with lowercase, but some code may
|
||||
* violate this rule
|
||||
*/
|
||||
node.get("params").length <= 1
|
||||
node.get('params').length <= 1
|
||||
);
|
||||
} else if (functionName !== null && isHook(functionName)) {
|
||||
// Hooks have hook invocations or JSX, but can take any # of arguments
|
||||
|
|
@ -151,11 +151,11 @@ function isHook(path: NodePath<t.Expression | t.PrivateName>): boolean {
|
|||
} else if (
|
||||
path.isMemberExpression() &&
|
||||
!path.node.computed &&
|
||||
isHook(path.get("property"))
|
||||
isHook(path.get('property'))
|
||||
) {
|
||||
const obj = path.get("object").node;
|
||||
const obj = path.get('object').node;
|
||||
const isPascalCaseNameSpace = /^[A-Z].*/;
|
||||
return obj.type === "Identifier" && isPascalCaseNameSpace.test(obj.name);
|
||||
return obj.type === 'Identifier' && isPascalCaseNameSpace.test(obj.name);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -177,8 +177,8 @@ function isComponentName(path: NodePath<t.Expression>): boolean {
|
|||
function isForwardRefCallback(path: NodePath<t.Expression>): boolean {
|
||||
return !!(
|
||||
path.parentPath.isCallExpression() &&
|
||||
path.parentPath.get("callee").isExpression() &&
|
||||
isReactAPI(path.parentPath.get("callee"), "forwardRef")
|
||||
path.parentPath.get('callee').isExpression() &&
|
||||
isReactAPI(path.parentPath.get('callee'), 'forwardRef')
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -190,22 +190,22 @@ function isForwardRefCallback(path: NodePath<t.Expression>): boolean {
|
|||
function isMemoCallback(path: NodePath<t.Expression>): boolean {
|
||||
return (
|
||||
path.parentPath.isCallExpression() &&
|
||||
path.parentPath.get("callee").isExpression() &&
|
||||
isReactAPI(path.parentPath.get("callee"), "memo")
|
||||
path.parentPath.get('callee').isExpression() &&
|
||||
isReactAPI(path.parentPath.get('callee'), 'memo')
|
||||
);
|
||||
}
|
||||
|
||||
function isReactAPI(
|
||||
path: NodePath<t.Expression | t.PrivateName | t.V8IntrinsicIdentifier>,
|
||||
functionName: string
|
||||
functionName: string,
|
||||
): boolean {
|
||||
const node = path.node;
|
||||
return (
|
||||
(node.type === "Identifier" && node.name === functionName) ||
|
||||
(node.type === "MemberExpression" &&
|
||||
node.object.type === "Identifier" &&
|
||||
node.object.name === "React" &&
|
||||
node.property.type === "Identifier" &&
|
||||
(node.type === 'Identifier' && node.name === functionName) ||
|
||||
(node.type === 'MemberExpression' &&
|
||||
node.object.type === 'Identifier' &&
|
||||
node.object.name === 'React' &&
|
||||
node.property.type === 'Identifier' &&
|
||||
node.property.name === functionName)
|
||||
);
|
||||
}
|
||||
|
|
@ -218,7 +218,7 @@ function callsHooksOrCreatesJsx(node: NodePath<t.Node>): boolean {
|
|||
createsJsx = true;
|
||||
},
|
||||
CallExpression(call) {
|
||||
const callee = call.get("callee");
|
||||
const callee = call.get('callee');
|
||||
if (callee.isExpression() && isHook(callee)) {
|
||||
invokesHooks = true;
|
||||
}
|
||||
|
|
@ -239,10 +239,10 @@ function callsHooksOrCreatesJsx(node: NodePath<t.Node>): boolean {
|
|||
function getFunctionName(
|
||||
path: NodePath<
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
>
|
||||
>,
|
||||
): NodePath<t.Expression> | null {
|
||||
if (path.isFunctionDeclaration()) {
|
||||
const id = path.get("id");
|
||||
const id = path.get('id');
|
||||
if (id.isIdentifier()) {
|
||||
return id;
|
||||
}
|
||||
|
|
@ -250,31 +250,31 @@ function getFunctionName(
|
|||
}
|
||||
let id: NodePath<t.LVal | t.Expression | t.PrivateName> | null = null;
|
||||
const parent = path.parentPath;
|
||||
if (parent.isVariableDeclarator() && parent.get("init").node === path.node) {
|
||||
if (parent.isVariableDeclarator() && parent.get('init').node === path.node) {
|
||||
// const useHook = () => {};
|
||||
id = parent.get("id");
|
||||
id = parent.get('id');
|
||||
} else if (
|
||||
parent.isAssignmentExpression() &&
|
||||
parent.get("right").node === path.node &&
|
||||
parent.get("operator") === "="
|
||||
parent.get('right').node === path.node &&
|
||||
parent.get('operator') === '='
|
||||
) {
|
||||
// useHook = () => {};
|
||||
id = parent.get("left");
|
||||
id = parent.get('left');
|
||||
} else if (
|
||||
parent.isProperty() &&
|
||||
parent.get("value").node === path.node &&
|
||||
!parent.get("computed") &&
|
||||
parent.get("key").isLVal()
|
||||
parent.get('value').node === path.node &&
|
||||
!parent.get('computed') &&
|
||||
parent.get('key').isLVal()
|
||||
) {
|
||||
/*
|
||||
* {useHook: () => {}}
|
||||
* {useHook() {}}
|
||||
*/
|
||||
id = parent.get("key");
|
||||
id = parent.get('key');
|
||||
} else if (
|
||||
parent.isAssignmentPattern() &&
|
||||
parent.get("right").node === path.node &&
|
||||
!parent.get("computed")
|
||||
parent.get('right').node === path.node &&
|
||||
!parent.get('computed')
|
||||
) {
|
||||
/*
|
||||
* const {useHook = () => {}} = {};
|
||||
|
|
@ -283,7 +283,7 @@ function getFunctionName(
|
|||
* Kinda clowny, but we'd said we'd follow spec convention for
|
||||
* `IsAnonymousFunctionDefinition()` usage.
|
||||
*/
|
||||
id = parent.get("left");
|
||||
id = parent.get('left');
|
||||
}
|
||||
if (id !== null && (id.isIdentifier() || id.isMemberExpression())) {
|
||||
return id;
|
||||
|
|
|
|||
|
|
@ -5,27 +5,27 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
const { tests } = require("./eslint-plugin-react-hooks-test-cases");
|
||||
const {tests} = require('./eslint-plugin-react-hooks-test-cases');
|
||||
const {
|
||||
runBabelPluginReactCompiler,
|
||||
} = require("../dist/Babel/RunReactCompilerBabelPlugin");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const prettier = require("prettier");
|
||||
const prettierConfigPath = require.resolve("../.prettierrc");
|
||||
const process = require("process");
|
||||
const { createHash } = require("crypto");
|
||||
const { create } = require("domain");
|
||||
} = require('../dist/Babel/RunReactCompilerBabelPlugin');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const prettier = require('prettier');
|
||||
const prettierConfigPath = require.resolve('../.prettierrc');
|
||||
const process = require('process');
|
||||
const {createHash} = require('crypto');
|
||||
const {create} = require('domain');
|
||||
|
||||
const FIXTURES_DIR = path.join(
|
||||
process.cwd(),
|
||||
"src",
|
||||
"__tests__",
|
||||
"fixtures",
|
||||
"compiler",
|
||||
"rules-of-hooks"
|
||||
'src',
|
||||
'__tests__',
|
||||
'fixtures',
|
||||
'compiler',
|
||||
'rules-of-hooks'
|
||||
);
|
||||
|
||||
const PRETTIER_OPTIONS = prettier.resolveConfig.sync(FIXTURES_DIR, {
|
||||
|
|
@ -34,10 +34,10 @@ const PRETTIER_OPTIONS = prettier.resolveConfig.sync(FIXTURES_DIR, {
|
|||
|
||||
const fixtures = [];
|
||||
for (const test of tests.valid) {
|
||||
fixtures.push({ code: test.code, valid: true });
|
||||
fixtures.push({code: test.code, valid: true});
|
||||
}
|
||||
for (const test of tests.invalid) {
|
||||
fixtures.push({ code: test.code, valid: false });
|
||||
fixtures.push({code: test.code, valid: false});
|
||||
}
|
||||
|
||||
for (const fixture of fixtures) {
|
||||
|
|
@ -47,8 +47,8 @@ for (const fixture of fixtures) {
|
|||
// Does the fixture pass with hooks validation disabled? if not skip it
|
||||
runBabelPluginReactCompiler(
|
||||
fixture.code,
|
||||
"rules-of-hooks.js",
|
||||
"typescript",
|
||||
'rules-of-hooks.js',
|
||||
'typescript',
|
||||
{
|
||||
environment: {
|
||||
validateHooksUsage: false,
|
||||
|
|
@ -59,8 +59,8 @@ for (const fixture of fixtures) {
|
|||
try {
|
||||
runBabelPluginReactCompiler(
|
||||
fixture.code,
|
||||
"rules-of-hooks.js",
|
||||
"typescript",
|
||||
'rules-of-hooks.js',
|
||||
'typescript',
|
||||
{
|
||||
environment: {
|
||||
validateHooksUsage: true,
|
||||
|
|
@ -74,7 +74,7 @@ for (const fixture of fixtures) {
|
|||
error = e;
|
||||
}
|
||||
let code = fixture.code;
|
||||
let prefix = "";
|
||||
let prefix = '';
|
||||
if (error !== null) {
|
||||
prefix = `todo.bail.`;
|
||||
code = `// @skip\n// Unsupported input\n${code}`;
|
||||
|
|
@ -92,11 +92,11 @@ for (const fixture of fixtures) {
|
|||
code = `// @skip\n// Failed but should have passed\n${code}`;
|
||||
}
|
||||
const formatted = prettier.format(code, PRETTIER_OPTIONS);
|
||||
const hmac = createHash("sha256");
|
||||
hmac.update(formatted, "utf8");
|
||||
const hmac = createHash('sha256');
|
||||
hmac.update(formatted, 'utf8');
|
||||
let name = `${prefix}rules-of-hooks-${hmac
|
||||
.digest("hex")
|
||||
.digest('hex')
|
||||
.substring(0, 12)}.js`;
|
||||
const fixturePath = path.join(FIXTURES_DIR, name);
|
||||
fs.writeFileSync(fixturePath, formatted, "utf8");
|
||||
fs.writeFileSync(fixturePath, formatted, 'utf8');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
// NOTE: Extracted from https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js
|
||||
|
||||
|
|
@ -13,9 +13,9 @@
|
|||
* A string template tag that removes padding from the left side of multi-line strings
|
||||
*/
|
||||
function normalizeIndent(strings) {
|
||||
const codeLines = strings[0].split("\n");
|
||||
const codeLines = strings[0].split('\n');
|
||||
const leftPadding = codeLines[1].match(/\s+/)[0];
|
||||
return codeLines.map((line) => line.slice(leftPadding.length)).join("\n");
|
||||
return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
|
||||
}
|
||||
|
||||
module.exports.tests = {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const makeE2EConfig = require("../jest/makeE2EConfig");
|
||||
const makeE2EConfig = require('../jest/makeE2EConfig');
|
||||
|
||||
module.exports = makeE2EConfig("e2e no forget", false);
|
||||
module.exports = makeE2EConfig('e2e no forget', false);
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const makeE2EConfig = require("../jest/makeE2EConfig");
|
||||
const makeE2EConfig = require('../jest/makeE2EConfig');
|
||||
|
||||
const config = makeE2EConfig("e2e with forget", true);
|
||||
config.setupFilesAfterEnv = ["<rootDir>/../scripts/jest/setupEnvE2E.js"];
|
||||
const config = makeE2EConfig('e2e with forget', true);
|
||||
config.setupFilesAfterEnv = ['<rootDir>/../scripts/jest/setupEnvE2E.js'];
|
||||
|
||||
module.exports = config;
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@
|
|||
*/
|
||||
|
||||
module.exports = {
|
||||
displayName: "main",
|
||||
preset: "ts-jest",
|
||||
rootDir: "../../src",
|
||||
testPathIgnorePatterns: ["e2e", "TestDriver", "test-utils", "fixtures"],
|
||||
displayName: 'main',
|
||||
preset: 'ts-jest',
|
||||
rootDir: '../../src',
|
||||
testPathIgnorePatterns: ['e2e', 'TestDriver', 'test-utils', 'fixtures'],
|
||||
globals: {
|
||||
__DEV__: true,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,27 +8,27 @@
|
|||
module.exports = function makeE2EConfig(displayName, useForget) {
|
||||
return {
|
||||
displayName,
|
||||
testEnvironment: "jsdom",
|
||||
rootDir: "../../src",
|
||||
testMatch: ["**/*.e2e.(js|tsx)"],
|
||||
testEnvironment: 'jsdom',
|
||||
rootDir: '../../src',
|
||||
testMatch: ['**/*.e2e.(js|tsx)'],
|
||||
modulePathIgnorePatterns: [
|
||||
// ignore snapshots from the opposite forget configuration
|
||||
useForget ? ".*\\.no-forget\\.snap$" : ".*\\.with-forget\\.snap$",
|
||||
useForget ? '.*\\.no-forget\\.snap$' : '.*\\.with-forget\\.snap$',
|
||||
// ignore snapshots from the main project
|
||||
".*\\.ts\\.snap$",
|
||||
'.*\\.ts\\.snap$',
|
||||
],
|
||||
globals: {
|
||||
__FORGET__: useForget,
|
||||
},
|
||||
snapshotResolver: useForget
|
||||
? "<rootDir>/../scripts/jest/snapshot-resolver-with-forget.js"
|
||||
: "<rootDir>/../scripts/jest/snapshot-resolver-no-forget.js",
|
||||
? '<rootDir>/../scripts/jest/snapshot-resolver-with-forget.js'
|
||||
: '<rootDir>/../scripts/jest/snapshot-resolver-no-forget.js',
|
||||
|
||||
transform: {
|
||||
"\\.[tj]sx?$": useForget
|
||||
? "<rootDir>/../scripts/jest/transform-with-forget"
|
||||
: "<rootDir>/../scripts/jest/transform-no-forget",
|
||||
'\\.[tj]sx?$': useForget
|
||||
? '<rootDir>/../scripts/jest/transform-with-forget'
|
||||
: '<rootDir>/../scripts/jest/transform-no-forget',
|
||||
},
|
||||
transformIgnorePatterns: ["/node_modules/"],
|
||||
transformIgnorePatterns: ['/node_modules/'],
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
module.exports = function makeSnapshotResolver(useForget) {
|
||||
const modeExtension = useForget ? ".with-forget" : ".no-forget";
|
||||
const modeExtension = useForget ? '.with-forget' : '.no-forget';
|
||||
return {
|
||||
resolveSnapshotPath: (testPath, snapshotExtension) =>
|
||||
testPath + modeExtension + snapshotExtension,
|
||||
|
|
@ -17,6 +17,6 @@ module.exports = function makeSnapshotResolver(useForget) {
|
|||
-modeExtension.length - snapshotExtension.length
|
||||
),
|
||||
|
||||
testPathForConsistencyCheck: "some/__tests__/example.test.js",
|
||||
testPathForConsistencyCheck: 'some/__tests__/example.test.js',
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,19 +5,19 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { jsx } from "@babel/plugin-syntax-jsx";
|
||||
import babelJest from "babel-jest";
|
||||
import { compile } from "babel-plugin-react-compiler";
|
||||
import { execSync } from "child_process";
|
||||
import {jsx} from '@babel/plugin-syntax-jsx';
|
||||
import babelJest from 'babel-jest';
|
||||
import {compile} from 'babel-plugin-react-compiler';
|
||||
import {execSync} from 'child_process';
|
||||
|
||||
import type { NodePath, Visitor } from "@babel/traverse";
|
||||
import type { CallExpression, FunctionDeclaration } from "@babel/types";
|
||||
import * as t from "@babel/types";
|
||||
import type {NodePath, Visitor} from '@babel/traverse';
|
||||
import type {CallExpression, FunctionDeclaration} from '@babel/types';
|
||||
import * as t from '@babel/types';
|
||||
import {
|
||||
EnvironmentConfig,
|
||||
validateEnvironmentConfig,
|
||||
} from "babel-plugin-react-compiler";
|
||||
import { basename } from "path";
|
||||
} from 'babel-plugin-react-compiler';
|
||||
import {basename} from 'path';
|
||||
|
||||
/**
|
||||
* -- IMPORTANT --
|
||||
|
|
@ -30,14 +30,14 @@ const forgetOptions: EnvironmentConfig = validateEnvironmentConfig({
|
|||
enableAssumeHooksFollowRulesOfReact: true,
|
||||
enableFunctionOutlining: false,
|
||||
});
|
||||
const debugMode = process.env["DEBUG_FORGET_COMPILER"] != null;
|
||||
const debugMode = process.env['DEBUG_FORGET_COMPILER'] != null;
|
||||
|
||||
module.exports = (useForget: boolean) => {
|
||||
function createTransformer() {
|
||||
return babelJest.createTransformer({
|
||||
passPerPreset: true,
|
||||
presets: [
|
||||
"@babel/preset-typescript",
|
||||
'@babel/preset-typescript',
|
||||
{
|
||||
plugins: [
|
||||
useForget
|
||||
|
|
@ -49,36 +49,36 @@ module.exports = (useForget: boolean) => {
|
|||
* (see https://github.com/jestjs/jest/blob/v29.6.2/packages/babel-jest/src/index.ts#L84)
|
||||
*/
|
||||
compilerCacheKey: execSync(
|
||||
"yarn --silent --cwd ../.. hash packages/babel-plugin-react-compiler/dist"
|
||||
'yarn --silent --cwd ../.. hash packages/babel-plugin-react-compiler/dist',
|
||||
).toString(),
|
||||
transformOptionsCacheKey: forgetOptions,
|
||||
e2eTransformerCacheKey,
|
||||
},
|
||||
]
|
||||
: "@babel/plugin-syntax-jsx",
|
||||
: '@babel/plugin-syntax-jsx',
|
||||
],
|
||||
},
|
||||
"@babel/preset-react",
|
||||
'@babel/preset-react',
|
||||
{
|
||||
plugins: [
|
||||
[
|
||||
function BabelPluginRewriteRequirePath(): { visitor: Visitor } {
|
||||
function BabelPluginRewriteRequirePath(): {visitor: Visitor} {
|
||||
return {
|
||||
visitor: {
|
||||
CallExpression(path: NodePath<CallExpression>): void {
|
||||
const { callee } = path.node;
|
||||
const {callee} = path.node;
|
||||
if (
|
||||
callee.type === "Identifier" &&
|
||||
callee.name === "require"
|
||||
callee.type === 'Identifier' &&
|
||||
callee.name === 'require'
|
||||
) {
|
||||
const arg = path.node.arguments[0];
|
||||
if (arg.type === "StringLiteral") {
|
||||
if (arg.type === 'StringLiteral') {
|
||||
/*
|
||||
* The compiler adds requires of "React", which is expected to be a wrapper
|
||||
* around the "react" package. For tests, we just rewrite the require.
|
||||
*/
|
||||
if (arg.value === "React") {
|
||||
arg.value = "react";
|
||||
if (arg.value === 'React') {
|
||||
arg.value = 'react';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ module.exports = (useForget: boolean) => {
|
|||
};
|
||||
},
|
||||
],
|
||||
"@babel/plugin-transform-modules-commonjs",
|
||||
'@babel/plugin-transform-modules-commonjs',
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
@ -125,7 +125,7 @@ function isReactComponentLike(fn: NodePath<FunctionDeclaration>): boolean {
|
|||
|
||||
fn.traverse({
|
||||
DirectiveLiteral(path) {
|
||||
if (path.node.value === "use no forget") {
|
||||
if (path.node.value === 'use no forget') {
|
||||
hasNoUseForgetDirective = true;
|
||||
}
|
||||
},
|
||||
|
|
@ -140,7 +140,7 @@ function isReactComponentLike(fn: NodePath<FunctionDeclaration>): boolean {
|
|||
CallExpression(path) {
|
||||
// Is there hook usage?
|
||||
if (
|
||||
path.node.callee.type === "Identifier" &&
|
||||
path.node.callee.type === 'Identifier' &&
|
||||
!/^use[A-Z0-9]/.test(path.node.callee.name)
|
||||
) {
|
||||
isReactComponent = true;
|
||||
|
|
@ -170,7 +170,7 @@ function ReactForgetFunctionTransform() {
|
|||
const filename = basename(state.file.opts.filename);
|
||||
if (fn.node.loc && fn.node.id) {
|
||||
console.log(
|
||||
` Compiling ${filename}:${fn.node.loc.start.line}:${fn.node.loc.start.column} ${fn.node.id.name}`
|
||||
` Compiling ${filename}:${fn.node.loc.start.line}:${fn.node.loc.start.column} ${fn.node.id.name}`,
|
||||
);
|
||||
} else {
|
||||
console.log(` Compiling ${filename} ${fn.node.id?.name}`);
|
||||
|
|
@ -180,11 +180,11 @@ function ReactForgetFunctionTransform() {
|
|||
const compiled = compile(
|
||||
fn,
|
||||
forgetOptions,
|
||||
"Other",
|
||||
"_c",
|
||||
'Other',
|
||||
'_c',
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
compiledFns.add(compiled);
|
||||
|
||||
|
|
@ -193,14 +193,14 @@ function ReactForgetFunctionTransform() {
|
|||
compiled.params,
|
||||
compiled.body,
|
||||
compiled.generator,
|
||||
compiled.async
|
||||
compiled.async,
|
||||
);
|
||||
fn.replaceWith(fun);
|
||||
fn.skip();
|
||||
},
|
||||
};
|
||||
return {
|
||||
name: "react-forget-e2e",
|
||||
name: 'react-forget-e2e',
|
||||
inherits: jsx,
|
||||
visitor,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const ReactCompilerRuntime = require("react/compiler-runtime");
|
||||
const ReactCompilerRuntime = require('react/compiler-runtime');
|
||||
|
||||
/*
|
||||
* Our e2e babel transform currently only compiles functions, not programs.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const makeSnapshotResolver = require("./makeSnapshotResolver");
|
||||
const makeSnapshotResolver = require('./makeSnapshotResolver');
|
||||
|
||||
module.exports = makeSnapshotResolver(false);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const makeSnapshotResolver = require("./makeSnapshotResolver");
|
||||
const makeSnapshotResolver = require('./makeSnapshotResolver');
|
||||
|
||||
module.exports = makeSnapshotResolver(true);
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
module.exports = require("./makeTransform")(false);
|
||||
module.exports = require('./makeTransform')(false);
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
module.exports = require("./makeTransform")(true);
|
||||
module.exports = require('./makeTransform')(true);
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type * as BabelCore from "@babel/core";
|
||||
import { compileProgram, parsePluginOptions } from "../Entrypoint";
|
||||
import type * as BabelCore from '@babel/core';
|
||||
import {compileProgram, parsePluginOptions} from '../Entrypoint';
|
||||
import {
|
||||
injectReanimatedFlag,
|
||||
pipelineUsesReanimatedPlugin,
|
||||
} from "../Entrypoint/Reanimated";
|
||||
} from '../Entrypoint/Reanimated';
|
||||
|
||||
/*
|
||||
* The React Forget Babel Plugin
|
||||
|
|
@ -18,10 +18,10 @@ import {
|
|||
* @returns
|
||||
*/
|
||||
export default function BabelPluginReactCompiler(
|
||||
_babel: typeof BabelCore
|
||||
_babel: typeof BabelCore,
|
||||
): BabelCore.PluginObj {
|
||||
return {
|
||||
name: "react-forget",
|
||||
name: 'react-forget',
|
||||
visitor: {
|
||||
/*
|
||||
* Note: Babel does some "smart" merging of visitors across plugins, so even if A is inserted
|
||||
|
|
@ -31,8 +31,8 @@ export default function BabelPluginReactCompiler(
|
|||
Program(prog, pass): void {
|
||||
let opts = parsePluginOptions(pass.opts);
|
||||
const isDev =
|
||||
(typeof __DEV__ !== "undefined" && __DEV__ === true) ||
|
||||
process.env["NODE_ENV"] === "development";
|
||||
(typeof __DEV__ !== 'undefined' && __DEV__ === true) ||
|
||||
process.env['NODE_ENV'] === 'development';
|
||||
if (
|
||||
opts.enableReanimatedCheck === true &&
|
||||
pipelineUsesReanimatedPlugin(pass.file.opts.plugins)
|
||||
|
|
|
|||
|
|
@ -5,25 +5,25 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type * as BabelCore from "@babel/core";
|
||||
import { transformFromAstSync } from "@babel/core";
|
||||
import * as BabelParser from "@babel/parser";
|
||||
import invariant from "invariant";
|
||||
import type { PluginOptions } from "../Entrypoint";
|
||||
import BabelPluginReactCompiler from "./BabelPlugin";
|
||||
import type * as BabelCore from '@babel/core';
|
||||
import {transformFromAstSync} from '@babel/core';
|
||||
import * as BabelParser from '@babel/parser';
|
||||
import invariant from 'invariant';
|
||||
import type {PluginOptions} from '../Entrypoint';
|
||||
import BabelPluginReactCompiler from './BabelPlugin';
|
||||
|
||||
export const DEFAULT_PLUGINS = ["babel-plugin-fbt", "babel-plugin-fbt-runtime"];
|
||||
export const DEFAULT_PLUGINS = ['babel-plugin-fbt', 'babel-plugin-fbt-runtime'];
|
||||
export function runBabelPluginReactCompiler(
|
||||
text: string,
|
||||
file: string,
|
||||
language: "flow" | "typescript",
|
||||
language: 'flow' | 'typescript',
|
||||
options: Partial<PluginOptions> | null,
|
||||
includeAst: boolean = false
|
||||
includeAst: boolean = false,
|
||||
): BabelCore.BabelFileResult {
|
||||
const ast = BabelParser.parse(text, {
|
||||
sourceFilename: file,
|
||||
plugins: [language, "jsx"],
|
||||
sourceType: "module",
|
||||
plugins: [language, 'jsx'],
|
||||
sourceType: 'module',
|
||||
});
|
||||
const result = transformFromAstSync(ast, text, {
|
||||
ast: includeAst,
|
||||
|
|
@ -32,16 +32,16 @@ export function runBabelPluginReactCompiler(
|
|||
retainLines: true,
|
||||
plugins: [
|
||||
[BabelPluginReactCompiler, options],
|
||||
"babel-plugin-fbt",
|
||||
"babel-plugin-fbt-runtime",
|
||||
'babel-plugin-fbt',
|
||||
'babel-plugin-fbt-runtime',
|
||||
],
|
||||
sourceType: "module",
|
||||
sourceType: 'module',
|
||||
configFile: false,
|
||||
babelrc: false,
|
||||
});
|
||||
invariant(
|
||||
result?.code != null,
|
||||
`Expected BabelPluginReactForget to codegen successfully, got: ${result}`
|
||||
`Expected BabelPluginReactForget to codegen successfully, got: ${result}`,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,37 +5,37 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type { SourceLocation } from "./HIR";
|
||||
import { assertExhaustive } from "./Utils/utils";
|
||||
import type {SourceLocation} from './HIR';
|
||||
import {assertExhaustive} from './Utils/utils';
|
||||
|
||||
export enum ErrorSeverity {
|
||||
/**
|
||||
* Invalid JS syntax, or valid syntax that is semantically invalid which may indicate some
|
||||
* misunderstanding on the user’s part.
|
||||
*/
|
||||
InvalidJS = "InvalidJS",
|
||||
InvalidJS = 'InvalidJS',
|
||||
/**
|
||||
* Code that breaks the rules of React.
|
||||
*/
|
||||
InvalidReact = "InvalidReact",
|
||||
InvalidReact = 'InvalidReact',
|
||||
/**
|
||||
* Incorrect configuration of the compiler.
|
||||
*/
|
||||
InvalidConfig = "InvalidConfig",
|
||||
InvalidConfig = 'InvalidConfig',
|
||||
/**
|
||||
* Code that can reasonably occur and that doesn't break any rules, but is unsafe to preserve
|
||||
* memoization.
|
||||
*/
|
||||
CannotPreserveMemoization = "CannotPreserveMemoization",
|
||||
CannotPreserveMemoization = 'CannotPreserveMemoization',
|
||||
/**
|
||||
* Unhandled syntax that we don't support yet.
|
||||
*/
|
||||
Todo = "Todo",
|
||||
Todo = 'Todo',
|
||||
/**
|
||||
* An unexpected internal error in the compiler that indicates critical issues that can panic
|
||||
* the compiler.
|
||||
*/
|
||||
Invariant = "Invariant",
|
||||
Invariant = 'Invariant',
|
||||
}
|
||||
|
||||
export enum CompilerSuggestionOperation {
|
||||
|
|
@ -79,19 +79,19 @@ export class CompilerErrorDetail {
|
|||
this.options = options;
|
||||
}
|
||||
|
||||
get reason(): CompilerErrorDetailOptions["reason"] {
|
||||
get reason(): CompilerErrorDetailOptions['reason'] {
|
||||
return this.options.reason;
|
||||
}
|
||||
get description(): CompilerErrorDetailOptions["description"] {
|
||||
get description(): CompilerErrorDetailOptions['description'] {
|
||||
return this.options.description;
|
||||
}
|
||||
get severity(): CompilerErrorDetailOptions["severity"] {
|
||||
get severity(): CompilerErrorDetailOptions['severity'] {
|
||||
return this.options.severity;
|
||||
}
|
||||
get loc(): CompilerErrorDetailOptions["loc"] {
|
||||
get loc(): CompilerErrorDetailOptions['loc'] {
|
||||
return this.options.loc;
|
||||
}
|
||||
get suggestions(): CompilerErrorDetailOptions["suggestions"] {
|
||||
get suggestions(): CompilerErrorDetailOptions['suggestions'] {
|
||||
return this.options.suggestions;
|
||||
}
|
||||
|
||||
|
|
@ -100,10 +100,10 @@ export class CompilerErrorDetail {
|
|||
if (this.description != null) {
|
||||
buffer.push(`. ${this.description}`);
|
||||
}
|
||||
if (this.loc != null && typeof this.loc !== "symbol") {
|
||||
if (this.loc != null && typeof this.loc !== 'symbol') {
|
||||
buffer.push(` (${this.loc.start.line}:${this.loc.end.line})`);
|
||||
}
|
||||
return buffer.join("");
|
||||
return buffer.join('');
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
|
|
@ -116,7 +116,7 @@ export class CompilerError extends Error {
|
|||
|
||||
static invariant(
|
||||
condition: unknown,
|
||||
options: Omit<CompilerErrorDetailOptions, "severity">
|
||||
options: Omit<CompilerErrorDetailOptions, 'severity'>,
|
||||
): asserts condition {
|
||||
if (!condition) {
|
||||
const errors = new CompilerError();
|
||||
|
|
@ -124,57 +124,57 @@ export class CompilerError extends Error {
|
|||
new CompilerErrorDetail({
|
||||
...options,
|
||||
severity: ErrorSeverity.Invariant,
|
||||
})
|
||||
}),
|
||||
);
|
||||
throw errors;
|
||||
}
|
||||
}
|
||||
|
||||
static throwTodo(
|
||||
options: Omit<CompilerErrorDetailOptions, "severity">
|
||||
options: Omit<CompilerErrorDetailOptions, 'severity'>,
|
||||
): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({ ...options, severity: ErrorSeverity.Todo })
|
||||
new CompilerErrorDetail({...options, severity: ErrorSeverity.Todo}),
|
||||
);
|
||||
throw errors;
|
||||
}
|
||||
|
||||
static throwInvalidJS(
|
||||
options: Omit<CompilerErrorDetailOptions, "severity">
|
||||
options: Omit<CompilerErrorDetailOptions, 'severity'>,
|
||||
): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
...options,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
})
|
||||
}),
|
||||
);
|
||||
throw errors;
|
||||
}
|
||||
|
||||
static throwInvalidReact(
|
||||
options: Omit<CompilerErrorDetailOptions, "severity">
|
||||
options: Omit<CompilerErrorDetailOptions, 'severity'>,
|
||||
): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
...options,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
})
|
||||
}),
|
||||
);
|
||||
throw errors;
|
||||
}
|
||||
|
||||
static throwInvalidConfig(
|
||||
options: Omit<CompilerErrorDetailOptions, "severity">
|
||||
options: Omit<CompilerErrorDetailOptions, 'severity'>,
|
||||
): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
...options,
|
||||
severity: ErrorSeverity.InvalidConfig,
|
||||
})
|
||||
}),
|
||||
);
|
||||
throw errors;
|
||||
}
|
||||
|
|
@ -187,7 +187,7 @@ export class CompilerError extends Error {
|
|||
|
||||
constructor(...args: Array<any>) {
|
||||
super(...args);
|
||||
this.name = "ReactCompilerError";
|
||||
this.name = 'ReactCompilerError';
|
||||
}
|
||||
|
||||
override get message(): string {
|
||||
|
|
@ -197,7 +197,7 @@ export class CompilerError extends Error {
|
|||
override set message(_message: string) {}
|
||||
|
||||
override toString(): string {
|
||||
return this.details.map((detail) => detail.toString()).join("\n\n");
|
||||
return this.details.map(detail => detail.toString()).join('\n\n');
|
||||
}
|
||||
|
||||
push(options: CompilerErrorDetailOptions): CompilerErrorDetail {
|
||||
|
|
@ -206,7 +206,7 @@ export class CompilerError extends Error {
|
|||
description: options.description ?? null,
|
||||
severity: options.severity,
|
||||
suggestions: options.suggestions,
|
||||
loc: typeof options.loc === "symbol" ? null : options.loc,
|
||||
loc: typeof options.loc === 'symbol' ? null : options.loc,
|
||||
});
|
||||
return this.pushErrorDetail(detail);
|
||||
}
|
||||
|
|
@ -226,7 +226,7 @@ export class CompilerError extends Error {
|
|||
* but otherwise continue compiling the rest of the app.
|
||||
*/
|
||||
isCritical(): boolean {
|
||||
return this.details.some((detail) => {
|
||||
return this.details.some(detail => {
|
||||
switch (detail.severity) {
|
||||
case ErrorSeverity.Invariant:
|
||||
case ErrorSeverity.InvalidJS:
|
||||
|
|
@ -237,7 +237,7 @@ export class CompilerError extends Error {
|
|||
case ErrorSeverity.Todo:
|
||||
return false;
|
||||
default:
|
||||
assertExhaustive(detail.severity, "Unhandled error severity");
|
||||
assertExhaustive(detail.severity, 'Unhandled error severity');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { NodePath } from "@babel/core";
|
||||
import * as t from "@babel/types";
|
||||
import { PluginOptions } from "./Options";
|
||||
import {NodePath} from '@babel/core';
|
||||
import * as t from '@babel/types';
|
||||
import {PluginOptions} from './Options';
|
||||
|
||||
export function insertGatedFunctionDeclaration(
|
||||
fnPath: NodePath<
|
||||
|
|
@ -17,12 +17,12 @@ export function insertGatedFunctionDeclaration(
|
|||
| t.FunctionDeclaration
|
||||
| t.ArrowFunctionExpression
|
||||
| t.FunctionExpression,
|
||||
gating: NonNullable<PluginOptions["gating"]>
|
||||
gating: NonNullable<PluginOptions['gating']>,
|
||||
): void {
|
||||
const gatingExpression = t.conditionalExpression(
|
||||
t.callExpression(t.identifier(gating.importSpecifierName), []),
|
||||
buildFunctionExpression(compiled),
|
||||
buildFunctionExpression(fnPath.node)
|
||||
buildFunctionExpression(fnPath.node),
|
||||
);
|
||||
|
||||
/*
|
||||
|
|
@ -32,30 +32,30 @@ export function insertGatedFunctionDeclaration(
|
|||
* conditional expression
|
||||
*/
|
||||
if (
|
||||
fnPath.parentPath.node.type !== "ExportDefaultDeclaration" &&
|
||||
fnPath.node.type === "FunctionDeclaration" &&
|
||||
fnPath.parentPath.node.type !== 'ExportDefaultDeclaration' &&
|
||||
fnPath.node.type === 'FunctionDeclaration' &&
|
||||
fnPath.node.id != null
|
||||
) {
|
||||
fnPath.replaceWith(
|
||||
t.variableDeclaration("const", [
|
||||
t.variableDeclaration('const', [
|
||||
t.variableDeclarator(fnPath.node.id, gatingExpression),
|
||||
])
|
||||
]),
|
||||
);
|
||||
} else if (
|
||||
fnPath.parentPath.node.type === "ExportDefaultDeclaration" &&
|
||||
fnPath.node.type !== "ArrowFunctionExpression" &&
|
||||
fnPath.parentPath.node.type === 'ExportDefaultDeclaration' &&
|
||||
fnPath.node.type !== 'ArrowFunctionExpression' &&
|
||||
fnPath.node.id != null
|
||||
) {
|
||||
fnPath.insertAfter(
|
||||
t.exportDefaultDeclaration(t.identifier(fnPath.node.id.name))
|
||||
t.exportDefaultDeclaration(t.identifier(fnPath.node.id.name)),
|
||||
);
|
||||
fnPath.parentPath.replaceWith(
|
||||
t.variableDeclaration("const", [
|
||||
t.variableDeclaration('const', [
|
||||
t.variableDeclarator(
|
||||
t.identifier(fnPath.node.id.name),
|
||||
gatingExpression
|
||||
gatingExpression,
|
||||
),
|
||||
])
|
||||
]),
|
||||
);
|
||||
} else {
|
||||
fnPath.replaceWith(gatingExpression);
|
||||
|
|
@ -63,16 +63,19 @@ export function insertGatedFunctionDeclaration(
|
|||
}
|
||||
|
||||
function buildFunctionExpression(
|
||||
node: t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
node:
|
||||
| t.FunctionDeclaration
|
||||
| t.ArrowFunctionExpression
|
||||
| t.FunctionExpression,
|
||||
): t.ArrowFunctionExpression | t.FunctionExpression {
|
||||
if (
|
||||
node.type === "ArrowFunctionExpression" ||
|
||||
node.type === "FunctionExpression"
|
||||
node.type === 'ArrowFunctionExpression' ||
|
||||
node.type === 'FunctionExpression'
|
||||
) {
|
||||
return node;
|
||||
} else {
|
||||
const fn: t.FunctionExpression = {
|
||||
type: "FunctionExpression",
|
||||
type: 'FunctionExpression',
|
||||
async: node.async,
|
||||
generator: node.generator,
|
||||
loc: node.loc ?? null,
|
||||
|
|
|
|||
|
|
@ -5,19 +5,19 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { NodePath } from "@babel/core";
|
||||
import * as t from "@babel/types";
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import { ExternalFunction, GeneratedSource } from "../HIR";
|
||||
import { getOrInsertDefault } from "../Utils/utils";
|
||||
import {NodePath} from '@babel/core';
|
||||
import * as t from '@babel/types';
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {ExternalFunction, GeneratedSource} from '../HIR';
|
||||
import {getOrInsertDefault} from '../Utils/utils';
|
||||
|
||||
export function addImportsToProgram(
|
||||
path: NodePath<t.Program>,
|
||||
importList: Array<ExternalFunction>
|
||||
importList: Array<ExternalFunction>,
|
||||
): void {
|
||||
const identifiers: Set<string> = new Set();
|
||||
const sortedImports: Map<string, Array<string>> = new Map();
|
||||
for (const { importSpecifierName, source } of importList) {
|
||||
for (const {importSpecifierName, source} of importList) {
|
||||
/*
|
||||
* Codegen currently does not rename import specifiers, so we do additional
|
||||
* validation here
|
||||
|
|
@ -35,28 +35,28 @@ export function addImportsToProgram(
|
|||
description: null,
|
||||
loc: GeneratedSource,
|
||||
suggestions: null,
|
||||
}
|
||||
},
|
||||
);
|
||||
identifiers.add(importSpecifierName);
|
||||
|
||||
const importSpecifierNameList = getOrInsertDefault(
|
||||
sortedImports,
|
||||
source,
|
||||
[]
|
||||
[],
|
||||
);
|
||||
importSpecifierNameList.push(importSpecifierName);
|
||||
}
|
||||
|
||||
const stmts: Array<t.ImportDeclaration> = [];
|
||||
for (const [source, importSpecifierNameList] of sortedImports) {
|
||||
const importSpecifiers = importSpecifierNameList.map((name) => {
|
||||
const importSpecifiers = importSpecifierNameList.map(name => {
|
||||
const id = t.identifier(name);
|
||||
return t.importSpecifier(id, id);
|
||||
});
|
||||
|
||||
stmts.push(t.importDeclaration(importSpecifiers, t.stringLiteral(source)));
|
||||
}
|
||||
path.unshiftContainer("body", stmts);
|
||||
path.unshiftContainer('body', stmts);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -65,21 +65,21 @@ export function addImportsToProgram(
|
|||
*/
|
||||
function isNonNamespacedImport(
|
||||
importDeclPath: NodePath<t.ImportDeclaration>,
|
||||
moduleName: string
|
||||
moduleName: string,
|
||||
): boolean {
|
||||
return (
|
||||
importDeclPath.get("source").node.value === moduleName &&
|
||||
importDeclPath.get('source').node.value === moduleName &&
|
||||
importDeclPath
|
||||
.get("specifiers")
|
||||
.every((specifier) => specifier.isImportSpecifier()) &&
|
||||
importDeclPath.node.importKind !== "type" &&
|
||||
importDeclPath.node.importKind !== "typeof"
|
||||
.get('specifiers')
|
||||
.every(specifier => specifier.isImportSpecifier()) &&
|
||||
importDeclPath.node.importKind !== 'type' &&
|
||||
importDeclPath.node.importKind !== 'typeof'
|
||||
);
|
||||
}
|
||||
|
||||
function hasExistingNonNamespacedImportOfModule(
|
||||
program: NodePath<t.Program>,
|
||||
moduleName: string
|
||||
moduleName: string,
|
||||
): boolean {
|
||||
let hasExistingImport = false;
|
||||
program.traverse({
|
||||
|
|
@ -100,7 +100,7 @@ function hasExistingNonNamespacedImportOfModule(
|
|||
function addMemoCacheFunctionSpecifierToExistingImport(
|
||||
program: NodePath<t.Program>,
|
||||
moduleName: string,
|
||||
identifierName: string
|
||||
identifierName: string,
|
||||
): boolean {
|
||||
let didInsertUseMemoCache = false;
|
||||
program.traverse({
|
||||
|
|
@ -110,8 +110,8 @@ function addMemoCacheFunctionSpecifierToExistingImport(
|
|||
isNonNamespacedImport(importDeclPath, moduleName)
|
||||
) {
|
||||
importDeclPath.pushContainer(
|
||||
"specifiers",
|
||||
t.importSpecifier(t.identifier(identifierName), t.identifier("c"))
|
||||
'specifiers',
|
||||
t.importSpecifier(t.identifier(identifierName), t.identifier('c')),
|
||||
);
|
||||
didInsertUseMemoCache = true;
|
||||
}
|
||||
|
|
@ -123,7 +123,7 @@ function addMemoCacheFunctionSpecifierToExistingImport(
|
|||
export function updateMemoCacheFunctionImport(
|
||||
program: NodePath<t.Program>,
|
||||
moduleName: string,
|
||||
useMemoCacheIdentifier: string
|
||||
useMemoCacheIdentifier: string,
|
||||
): void {
|
||||
/*
|
||||
* If there isn't already an import of * as React, insert it so useMemoCache doesn't
|
||||
|
|
@ -131,25 +131,25 @@ export function updateMemoCacheFunctionImport(
|
|||
*/
|
||||
const hasExistingImport = hasExistingNonNamespacedImportOfModule(
|
||||
program,
|
||||
moduleName
|
||||
moduleName,
|
||||
);
|
||||
|
||||
if (hasExistingImport) {
|
||||
const didUpdateImport = addMemoCacheFunctionSpecifierToExistingImport(
|
||||
program,
|
||||
moduleName,
|
||||
useMemoCacheIdentifier
|
||||
useMemoCacheIdentifier,
|
||||
);
|
||||
if (!didUpdateImport) {
|
||||
throw new Error(
|
||||
`Expected an ImportDeclaration of \`${moduleName}\` in order to update ImportSpecifiers with useMemoCache`
|
||||
`Expected an ImportDeclaration of \`${moduleName}\` in order to update ImportSpecifiers with useMemoCache`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
addMemoCacheFunctionImportDeclaration(
|
||||
program,
|
||||
moduleName,
|
||||
useMemoCacheIdentifier
|
||||
useMemoCacheIdentifier,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -157,13 +157,13 @@ export function updateMemoCacheFunctionImport(
|
|||
function addMemoCacheFunctionImportDeclaration(
|
||||
program: NodePath<t.Program>,
|
||||
moduleName: string,
|
||||
localName: string
|
||||
localName: string,
|
||||
): void {
|
||||
program.unshiftContainer(
|
||||
"body",
|
||||
'body',
|
||||
t.importDeclaration(
|
||||
[t.importSpecifier(t.identifier(localName), t.identifier("c"))],
|
||||
t.stringLiteral(moduleName)
|
||||
)
|
||||
[t.importSpecifier(t.identifier(localName), t.identifier('c'))],
|
||||
t.stringLiteral(moduleName),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import * as t from "@babel/types";
|
||||
import { z } from "zod";
|
||||
import { CompilerErrorDetailOptions } from "../CompilerError";
|
||||
import { ExternalFunction, PartialEnvironmentConfig } from "../HIR/Environment";
|
||||
import { hasOwnProperty } from "../Utils/utils";
|
||||
import * as t from '@babel/types';
|
||||
import {z} from 'zod';
|
||||
import {CompilerErrorDetailOptions} from '../CompilerError';
|
||||
import {ExternalFunction, PartialEnvironmentConfig} from '../HIR/Environment';
|
||||
import {hasOwnProperty} from '../Utils/utils';
|
||||
|
||||
const PanicThresholdOptionsSchema = z.enum([
|
||||
/*
|
||||
|
|
@ -18,15 +18,15 @@ const PanicThresholdOptionsSchema = z.enum([
|
|||
* If Forget is invoked through `BabelPluginReactCompiler`, this will at the least
|
||||
* skip Forget compilation for the rest of current file.
|
||||
*/
|
||||
"all_errors",
|
||||
'all_errors',
|
||||
/*
|
||||
* Panic by throwing an exception only on critical or unrecognized errors.
|
||||
* For all other errors, skip the erroring function without inserting
|
||||
* a Forget-compiled version (i.e. same behavior as noEmit).
|
||||
*/
|
||||
"critical_errors",
|
||||
'critical_errors',
|
||||
// Never panic by throwing an exception.
|
||||
"none",
|
||||
'none',
|
||||
]);
|
||||
|
||||
export type PanicThresholdOptions = z.infer<typeof PanicThresholdOptionsSchema>;
|
||||
|
|
@ -130,13 +130,13 @@ const CompilationModeSchema = z.enum([
|
|||
* false positives, since compilation has a greater impact than linting.
|
||||
* This is the default mode
|
||||
*/
|
||||
"infer",
|
||||
'infer',
|
||||
// Compile only components using Flow component syntax and hooks using hook syntax.
|
||||
"syntax",
|
||||
'syntax',
|
||||
// Compile only functions which are explicitly annotated with "use forget"
|
||||
"annotation",
|
||||
'annotation',
|
||||
// Compile all top-level functions
|
||||
"all",
|
||||
'all',
|
||||
]);
|
||||
|
||||
export type CompilationMode = z.infer<typeof CompilationModeSchema>;
|
||||
|
|
@ -156,17 +156,17 @@ export type CompilationMode = z.infer<typeof CompilationModeSchema>;
|
|||
*/
|
||||
export type LoggerEvent =
|
||||
| {
|
||||
kind: "CompileError";
|
||||
kind: 'CompileError';
|
||||
fnLoc: t.SourceLocation | null;
|
||||
detail: CompilerErrorDetailOptions;
|
||||
}
|
||||
| {
|
||||
kind: "CompileDiagnostic";
|
||||
kind: 'CompileDiagnostic';
|
||||
fnLoc: t.SourceLocation | null;
|
||||
detail: Omit<Omit<CompilerErrorDetailOptions, "severity">, "suggestions">;
|
||||
detail: Omit<Omit<CompilerErrorDetailOptions, 'severity'>, 'suggestions'>;
|
||||
}
|
||||
| {
|
||||
kind: "CompileSuccess";
|
||||
kind: 'CompileSuccess';
|
||||
fnLoc: t.SourceLocation | null;
|
||||
fnName: string | null;
|
||||
memoSlots: number;
|
||||
|
|
@ -176,7 +176,7 @@ export type LoggerEvent =
|
|||
prunedMemoValues: number;
|
||||
}
|
||||
| {
|
||||
kind: "PipelineError";
|
||||
kind: 'PipelineError';
|
||||
fnLoc: t.SourceLocation | null;
|
||||
data: string;
|
||||
};
|
||||
|
|
@ -186,8 +186,8 @@ export type Logger = {
|
|||
};
|
||||
|
||||
export const defaultOptions: PluginOptions = {
|
||||
compilationMode: "infer",
|
||||
panicThreshold: "none",
|
||||
compilationMode: 'infer',
|
||||
panicThreshold: 'none',
|
||||
environment: {},
|
||||
logger: null,
|
||||
gating: null,
|
||||
|
|
@ -196,19 +196,19 @@ export const defaultOptions: PluginOptions = {
|
|||
eslintSuppressionRules: null,
|
||||
flowSuppressions: false,
|
||||
ignoreUseNoForget: false,
|
||||
sources: (filename) => {
|
||||
return filename.indexOf("node_modules") === -1;
|
||||
sources: filename => {
|
||||
return filename.indexOf('node_modules') === -1;
|
||||
},
|
||||
enableReanimatedCheck: true,
|
||||
} as const;
|
||||
|
||||
export function parsePluginOptions(obj: unknown): PluginOptions {
|
||||
if (obj == null || typeof obj !== "object") {
|
||||
if (obj == null || typeof obj !== 'object') {
|
||||
return defaultOptions;
|
||||
}
|
||||
const parsedOptions = Object.create(null);
|
||||
for (let [key, value] of Object.entries(obj)) {
|
||||
if (typeof value === "string") {
|
||||
if (typeof value === 'string') {
|
||||
// normalize string configs to be case insensitive
|
||||
value = value.toLowerCase();
|
||||
}
|
||||
|
|
@ -216,7 +216,7 @@ export function parsePluginOptions(obj: unknown): PluginOptions {
|
|||
parsedOptions[key] = value;
|
||||
}
|
||||
}
|
||||
return { ...defaultOptions, ...parsedOptions };
|
||||
return {...defaultOptions, ...parsedOptions};
|
||||
}
|
||||
|
||||
function isCompilerFlag(s: string): s is keyof PluginOptions {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { NodePath } from "@babel/traverse";
|
||||
import * as t from "@babel/types";
|
||||
import prettyFormat from "pretty-format";
|
||||
import { Logger } from ".";
|
||||
import {NodePath} from '@babel/traverse';
|
||||
import * as t from '@babel/types';
|
||||
import prettyFormat from 'pretty-format';
|
||||
import {Logger} from '.';
|
||||
import {
|
||||
HIRFunction,
|
||||
ReactiveFunction,
|
||||
|
|
@ -22,13 +22,13 @@ import {
|
|||
mergeConsecutiveBlocks,
|
||||
mergeOverlappingReactiveScopesHIR,
|
||||
pruneUnusedLabelsHIR,
|
||||
} from "../HIR";
|
||||
} from '../HIR';
|
||||
import {
|
||||
Environment,
|
||||
EnvironmentConfig,
|
||||
ReactFunctionType,
|
||||
} from "../HIR/Environment";
|
||||
import { findContextIdentifiers } from "../HIR/FindContextIdentifiers";
|
||||
} from '../HIR/Environment';
|
||||
import {findContextIdentifiers} from '../HIR/FindContextIdentifiers';
|
||||
import {
|
||||
analyseFunctions,
|
||||
dropManualMemoization,
|
||||
|
|
@ -36,13 +36,13 @@ import {
|
|||
inferReactivePlaces,
|
||||
inferReferenceEffects,
|
||||
inlineImmediatelyInvokedFunctionExpressions,
|
||||
} from "../Inference";
|
||||
} from '../Inference';
|
||||
import {
|
||||
constantPropagation,
|
||||
deadCodeElimination,
|
||||
pruneMaybeThrows,
|
||||
} from "../Optimization";
|
||||
import { instructionReordering } from "../Optimization/InstructionReordering";
|
||||
} from '../Optimization';
|
||||
import {instructionReordering} from '../Optimization/InstructionReordering';
|
||||
import {
|
||||
CodegenFunction,
|
||||
alignObjectMethodScopes,
|
||||
|
|
@ -69,23 +69,23 @@ import {
|
|||
pruneUnusedLabels,
|
||||
pruneUnusedScopes,
|
||||
renameVariables,
|
||||
} from "../ReactiveScopes";
|
||||
import { alignMethodCallScopes } from "../ReactiveScopes/AlignMethodCallScopes";
|
||||
import { alignReactiveScopesToBlockScopesHIR } from "../ReactiveScopes/AlignReactiveScopesToBlockScopesHIR";
|
||||
import { flattenReactiveLoopsHIR } from "../ReactiveScopes/FlattenReactiveLoopsHIR";
|
||||
import { flattenScopesWithHooksOrUseHIR } from "../ReactiveScopes/FlattenScopesWithHooksOrUseHIR";
|
||||
import { pruneAlwaysInvalidatingScopes } from "../ReactiveScopes/PruneAlwaysInvalidatingScopes";
|
||||
import pruneInitializationDependencies from "../ReactiveScopes/PruneInitializationDependencies";
|
||||
import { stabilizeBlockIds } from "../ReactiveScopes/StabilizeBlockIds";
|
||||
import { eliminateRedundantPhi, enterSSA, leaveSSA } from "../SSA";
|
||||
import { inferTypes } from "../TypeInference";
|
||||
} from '../ReactiveScopes';
|
||||
import {alignMethodCallScopes} from '../ReactiveScopes/AlignMethodCallScopes';
|
||||
import {alignReactiveScopesToBlockScopesHIR} from '../ReactiveScopes/AlignReactiveScopesToBlockScopesHIR';
|
||||
import {flattenReactiveLoopsHIR} from '../ReactiveScopes/FlattenReactiveLoopsHIR';
|
||||
import {flattenScopesWithHooksOrUseHIR} from '../ReactiveScopes/FlattenScopesWithHooksOrUseHIR';
|
||||
import {pruneAlwaysInvalidatingScopes} from '../ReactiveScopes/PruneAlwaysInvalidatingScopes';
|
||||
import pruneInitializationDependencies from '../ReactiveScopes/PruneInitializationDependencies';
|
||||
import {stabilizeBlockIds} from '../ReactiveScopes/StabilizeBlockIds';
|
||||
import {eliminateRedundantPhi, enterSSA, leaveSSA} from '../SSA';
|
||||
import {inferTypes} from '../TypeInference';
|
||||
import {
|
||||
logCodegenFunction,
|
||||
logDebug,
|
||||
logHIRFunction,
|
||||
logReactiveFunction,
|
||||
} from "../Utils/logger";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
} from '../Utils/logger';
|
||||
import {assertExhaustive} from '../Utils/utils';
|
||||
import {
|
||||
validateContextVariableLValues,
|
||||
validateHooksUsage,
|
||||
|
|
@ -95,15 +95,15 @@ import {
|
|||
validateNoSetStateInRender,
|
||||
validatePreservedManualMemoization,
|
||||
validateUseMemo,
|
||||
} from "../Validation";
|
||||
import { validateLocalsNotReassignedAfterRender } from "../Validation/ValidateLocalsNotReassignedAfterRender";
|
||||
import { outlineFunctions } from "../Optimization/OutlineFunctions";
|
||||
} from '../Validation';
|
||||
import {validateLocalsNotReassignedAfterRender} from '../Validation/ValidateLocalsNotReassignedAfterRender';
|
||||
import {outlineFunctions} from '../Optimization/OutlineFunctions';
|
||||
|
||||
export type CompilerPipelineValue =
|
||||
| { kind: "ast"; name: string; value: CodegenFunction }
|
||||
| { kind: "hir"; name: string; value: HIRFunction }
|
||||
| { kind: "reactive"; name: string; value: ReactiveFunction }
|
||||
| { kind: "debug"; name: string; value: string };
|
||||
| {kind: 'ast'; name: string; value: CodegenFunction}
|
||||
| {kind: 'hir'; name: string; value: HIRFunction}
|
||||
| {kind: 'reactive'; name: string; value: ReactiveFunction}
|
||||
| {kind: 'debug'; name: string; value: string};
|
||||
|
||||
export function* run(
|
||||
func: NodePath<
|
||||
|
|
@ -114,7 +114,7 @@ export function* run(
|
|||
useMemoCacheIdentifier: string,
|
||||
logger: Logger | null,
|
||||
filename: string | null,
|
||||
code: string | null
|
||||
code: string | null,
|
||||
): Generator<CompilerPipelineValue, CodegenFunction> {
|
||||
const contextIdentifiers = findContextIdentifiers(func);
|
||||
const env = new Environment(
|
||||
|
|
@ -125,11 +125,11 @@ export function* run(
|
|||
logger,
|
||||
filename,
|
||||
code,
|
||||
useMemoCacheIdentifier
|
||||
useMemoCacheIdentifier,
|
||||
);
|
||||
yield {
|
||||
kind: "debug",
|
||||
name: "EnvironmentConfig",
|
||||
kind: 'debug',
|
||||
name: 'EnvironmentConfig',
|
||||
value: prettyFormat(env.config),
|
||||
};
|
||||
const ast = yield* runWithEnvironment(func, env);
|
||||
|
|
@ -144,13 +144,13 @@ function* runWithEnvironment(
|
|||
func: NodePath<
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
>,
|
||||
env: Environment
|
||||
env: Environment,
|
||||
): Generator<CompilerPipelineValue, CodegenFunction> {
|
||||
const hir = lower(func, env).unwrap();
|
||||
yield log({ kind: "hir", name: "HIR", value: hir });
|
||||
yield log({kind: 'hir', name: 'HIR', value: hir});
|
||||
|
||||
pruneMaybeThrows(hir);
|
||||
yield log({ kind: "hir", name: "PruneMaybeThrows", value: hir });
|
||||
yield log({kind: 'hir', name: 'PruneMaybeThrows', value: hir});
|
||||
|
||||
validateContextVariableLValues(hir);
|
||||
validateUseMemo(hir);
|
||||
|
|
@ -161,35 +161,35 @@ function* runWithEnvironment(
|
|||
!env.config.enableChangeDetectionForDebugging
|
||||
) {
|
||||
dropManualMemoization(hir);
|
||||
yield log({ kind: "hir", name: "DropManualMemoization", value: hir });
|
||||
yield log({kind: 'hir', name: 'DropManualMemoization', value: hir});
|
||||
}
|
||||
|
||||
inlineImmediatelyInvokedFunctionExpressions(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
name: "InlineImmediatelyInvokedFunctionExpressions",
|
||||
kind: 'hir',
|
||||
name: 'InlineImmediatelyInvokedFunctionExpressions',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
mergeConsecutiveBlocks(hir);
|
||||
yield log({ kind: "hir", name: "MergeConsecutiveBlocks", value: hir });
|
||||
yield log({kind: 'hir', name: 'MergeConsecutiveBlocks', value: hir});
|
||||
|
||||
assertConsistentIdentifiers(hir);
|
||||
assertTerminalSuccessorsExist(hir);
|
||||
|
||||
enterSSA(hir);
|
||||
yield log({ kind: "hir", name: "SSA", value: hir });
|
||||
yield log({kind: 'hir', name: 'SSA', value: hir});
|
||||
|
||||
eliminateRedundantPhi(hir);
|
||||
yield log({ kind: "hir", name: "EliminateRedundantPhi", value: hir });
|
||||
yield log({kind: 'hir', name: 'EliminateRedundantPhi', value: hir});
|
||||
|
||||
assertConsistentIdentifiers(hir);
|
||||
|
||||
constantPropagation(hir);
|
||||
yield log({ kind: "hir", name: "ConstantPropagation", value: hir });
|
||||
yield log({kind: 'hir', name: 'ConstantPropagation', value: hir});
|
||||
|
||||
inferTypes(hir);
|
||||
yield log({ kind: "hir", name: "InferTypes", value: hir });
|
||||
yield log({kind: 'hir', name: 'InferTypes', value: hir});
|
||||
|
||||
if (env.config.validateHooksUsage) {
|
||||
validateHooksUsage(hir);
|
||||
|
|
@ -200,27 +200,27 @@ function* runWithEnvironment(
|
|||
}
|
||||
|
||||
analyseFunctions(hir);
|
||||
yield log({ kind: "hir", name: "AnalyseFunctions", value: hir });
|
||||
yield log({kind: 'hir', name: 'AnalyseFunctions', value: hir});
|
||||
|
||||
inferReferenceEffects(hir);
|
||||
yield log({ kind: "hir", name: "InferReferenceEffects", value: hir });
|
||||
yield log({kind: 'hir', name: 'InferReferenceEffects', value: hir});
|
||||
|
||||
validateLocalsNotReassignedAfterRender(hir);
|
||||
|
||||
// Note: Has to come after infer reference effects because "dead" code may still affect inference
|
||||
deadCodeElimination(hir);
|
||||
yield log({ kind: "hir", name: "DeadCodeElimination", value: hir });
|
||||
yield log({kind: 'hir', name: 'DeadCodeElimination', value: hir});
|
||||
|
||||
if (env.config.enableInstructionReordering) {
|
||||
instructionReordering(hir);
|
||||
yield log({ kind: "hir", name: "InstructionReordering", value: hir });
|
||||
yield log({kind: 'hir', name: 'InstructionReordering', value: hir});
|
||||
}
|
||||
|
||||
pruneMaybeThrows(hir);
|
||||
yield log({ kind: "hir", name: "PruneMaybeThrows", value: hir });
|
||||
yield log({kind: 'hir', name: 'PruneMaybeThrows', value: hir});
|
||||
|
||||
inferMutableRanges(hir);
|
||||
yield log({ kind: "hir", name: "InferMutableRanges", value: hir });
|
||||
yield log({kind: 'hir', name: 'InferMutableRanges', value: hir});
|
||||
|
||||
if (env.config.assertValidMutableRanges) {
|
||||
assertValidMutableRanges(hir);
|
||||
|
|
@ -235,67 +235,67 @@ function* runWithEnvironment(
|
|||
}
|
||||
|
||||
inferReactivePlaces(hir);
|
||||
yield log({ kind: "hir", name: "InferReactivePlaces", value: hir });
|
||||
yield log({kind: 'hir', name: 'InferReactivePlaces', value: hir});
|
||||
|
||||
leaveSSA(hir);
|
||||
yield log({ kind: "hir", name: "LeaveSSA", value: hir });
|
||||
yield log({kind: 'hir', name: 'LeaveSSA', value: hir});
|
||||
|
||||
inferReactiveScopeVariables(hir);
|
||||
yield log({ kind: "hir", name: "InferReactiveScopeVariables", value: hir });
|
||||
yield log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir});
|
||||
|
||||
if (env.config.enableFunctionOutlining) {
|
||||
outlineFunctions(hir);
|
||||
yield log({ kind: "hir", name: "OutlineFunctions", value: hir });
|
||||
yield log({kind: 'hir', name: 'OutlineFunctions', value: hir});
|
||||
}
|
||||
|
||||
alignMethodCallScopes(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
name: "AlignMethodCallScopes",
|
||||
kind: 'hir',
|
||||
name: 'AlignMethodCallScopes',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
alignObjectMethodScopes(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
name: "AlignObjectMethodScopes",
|
||||
kind: 'hir',
|
||||
name: 'AlignObjectMethodScopes',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
const fbtOperands = memoizeFbtOperandsInSameScope(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
name: "MemoizeFbtAndMacroOperandsInSameScope",
|
||||
kind: 'hir',
|
||||
name: 'MemoizeFbtAndMacroOperandsInSameScope',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
if (env.config.enableReactiveScopesInHIR) {
|
||||
pruneUnusedLabelsHIR(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
name: "PruneUnusedLabelsHIR",
|
||||
kind: 'hir',
|
||||
name: 'PruneUnusedLabelsHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
alignReactiveScopesToBlockScopesHIR(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
name: "AlignReactiveScopesToBlockScopesHIR",
|
||||
kind: 'hir',
|
||||
name: 'AlignReactiveScopesToBlockScopesHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
mergeOverlappingReactiveScopesHIR(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
name: "MergeOverlappingReactiveScopesHIR",
|
||||
kind: 'hir',
|
||||
name: 'MergeOverlappingReactiveScopesHIR',
|
||||
value: hir,
|
||||
});
|
||||
assertValidBlockNesting(hir);
|
||||
|
||||
buildReactiveScopeTerminalsHIR(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
name: "BuildReactiveScopeTerminalsHIR",
|
||||
kind: 'hir',
|
||||
name: 'BuildReactiveScopeTerminalsHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
|
|
@ -303,15 +303,15 @@ function* runWithEnvironment(
|
|||
|
||||
flattenReactiveLoopsHIR(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
name: "FlattenReactiveLoopsHIR",
|
||||
kind: 'hir',
|
||||
name: 'FlattenReactiveLoopsHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
flattenScopesWithHooksOrUseHIR(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
name: "FlattenScopesWithHooksOrUseHIR",
|
||||
kind: 'hir',
|
||||
name: 'FlattenScopesWithHooksOrUseHIR',
|
||||
value: hir,
|
||||
});
|
||||
assertTerminalSuccessorsExist(hir);
|
||||
|
|
@ -320,8 +320,8 @@ function* runWithEnvironment(
|
|||
|
||||
const reactiveFunction = buildReactiveFunction(hir);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "BuildReactiveFunction",
|
||||
kind: 'reactive',
|
||||
name: 'BuildReactiveFunction',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
|
|
@ -329,44 +329,44 @@ function* runWithEnvironment(
|
|||
|
||||
pruneUnusedLabels(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PruneUnusedLabels",
|
||||
kind: 'reactive',
|
||||
name: 'PruneUnusedLabels',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
if (!env.config.enableReactiveScopesInHIR) {
|
||||
alignReactiveScopesToBlockScopes(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "AlignReactiveScopesToBlockScopes",
|
||||
kind: 'reactive',
|
||||
name: 'AlignReactiveScopesToBlockScopes',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
mergeOverlappingReactiveScopes(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "MergeOverlappingReactiveScopes",
|
||||
kind: 'reactive',
|
||||
name: 'MergeOverlappingReactiveScopes',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
buildReactiveBlocks(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "BuildReactiveBlocks",
|
||||
kind: 'reactive',
|
||||
name: 'BuildReactiveBlocks',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
flattenReactiveLoops(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "FlattenReactiveLoops",
|
||||
kind: 'reactive',
|
||||
name: 'FlattenReactiveLoops',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
flattenScopesWithHooksOrUse(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "FlattenScopesWithHooks",
|
||||
kind: 'reactive',
|
||||
name: 'FlattenScopesWithHooks',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
}
|
||||
|
|
@ -375,101 +375,101 @@ function* runWithEnvironment(
|
|||
|
||||
propagateScopeDependencies(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PropagateScopeDependencies",
|
||||
kind: 'reactive',
|
||||
name: 'PropagateScopeDependencies',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
pruneNonEscapingScopes(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PruneNonEscapingScopes",
|
||||
kind: 'reactive',
|
||||
name: 'PruneNonEscapingScopes',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
pruneNonReactiveDependencies(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PruneNonReactiveDependencies",
|
||||
kind: 'reactive',
|
||||
name: 'PruneNonReactiveDependencies',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
pruneUnusedScopes(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PruneUnusedScopes",
|
||||
kind: 'reactive',
|
||||
name: 'PruneUnusedScopes',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
mergeReactiveScopesThatInvalidateTogether(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "MergeReactiveScopesThatInvalidateTogether",
|
||||
kind: 'reactive',
|
||||
name: 'MergeReactiveScopesThatInvalidateTogether',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
pruneAlwaysInvalidatingScopes(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PruneAlwaysInvalidatingScopes",
|
||||
kind: 'reactive',
|
||||
name: 'PruneAlwaysInvalidatingScopes',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
if (env.config.enableChangeDetectionForDebugging != null) {
|
||||
pruneInitializationDependencies(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PruneInitializationDependencies",
|
||||
kind: 'reactive',
|
||||
name: 'PruneInitializationDependencies',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
}
|
||||
|
||||
propagateEarlyReturns(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PropagateEarlyReturns",
|
||||
kind: 'reactive',
|
||||
name: 'PropagateEarlyReturns',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
promoteUsedTemporaries(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PromoteUsedTemporaries",
|
||||
kind: 'reactive',
|
||||
name: 'PromoteUsedTemporaries',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
pruneUnusedLValues(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PruneUnusedLValues",
|
||||
kind: 'reactive',
|
||||
name: 'PruneUnusedLValues',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
extractScopeDeclarationsFromDestructuring(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "ExtractScopeDeclarationsFromDestructuring",
|
||||
kind: 'reactive',
|
||||
name: 'ExtractScopeDeclarationsFromDestructuring',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
stabilizeBlockIds(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "StabilizeBlockIds",
|
||||
kind: 'reactive',
|
||||
name: 'StabilizeBlockIds',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
const uniqueIdentifiers = renameVariables(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "RenameVariables",
|
||||
kind: 'reactive',
|
||||
name: 'RenameVariables',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
pruneHoistedContexts(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PruneHoistedContexts",
|
||||
kind: 'reactive',
|
||||
name: 'PruneHoistedContexts',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
|
|
@ -488,9 +488,9 @@ function* runWithEnvironment(
|
|||
uniqueIdentifiers,
|
||||
fbtOperands,
|
||||
}).unwrap();
|
||||
yield log({ kind: "ast", name: "Codegen", value: ast });
|
||||
yield log({kind: 'ast', name: 'Codegen', value: ast});
|
||||
for (const outlined of ast.outlined) {
|
||||
yield log({ kind: "ast", name: "Codegen (outlined)", value: outlined.fn });
|
||||
yield log({kind: 'ast', name: 'Codegen (outlined)', value: outlined.fn});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -499,7 +499,7 @@ function* runWithEnvironment(
|
|||
* thrown by babel functions or other unexpected exceptions).
|
||||
*/
|
||||
if (env.config.throwUnknownException__testonly) {
|
||||
throw new Error("unexpected error");
|
||||
throw new Error('unexpected error');
|
||||
}
|
||||
|
||||
return ast;
|
||||
|
|
@ -514,7 +514,7 @@ export function compileFn(
|
|||
useMemoCacheIdentifier: string,
|
||||
logger: Logger | null,
|
||||
filename: string | null,
|
||||
code: string | null
|
||||
code: string | null,
|
||||
): CodegenFunction {
|
||||
let generator = run(
|
||||
func,
|
||||
|
|
@ -523,7 +523,7 @@ export function compileFn(
|
|||
useMemoCacheIdentifier,
|
||||
logger,
|
||||
filename,
|
||||
code
|
||||
code,
|
||||
);
|
||||
while (true) {
|
||||
const next = generator.next();
|
||||
|
|
@ -535,24 +535,24 @@ export function compileFn(
|
|||
|
||||
export function log(value: CompilerPipelineValue): CompilerPipelineValue {
|
||||
switch (value.kind) {
|
||||
case "ast": {
|
||||
case 'ast': {
|
||||
logCodegenFunction(value.name, value.value);
|
||||
break;
|
||||
}
|
||||
case "hir": {
|
||||
case 'hir': {
|
||||
logHIRFunction(value.name, value.value);
|
||||
break;
|
||||
}
|
||||
case "reactive": {
|
||||
case 'reactive': {
|
||||
logReactiveFunction(value.name, value.value);
|
||||
break;
|
||||
}
|
||||
case "debug": {
|
||||
case 'debug': {
|
||||
logDebug(value.name, value.value);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(value, "Unexpected compilation kind");
|
||||
assertExhaustive(value, 'Unexpected compilation kind');
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
|
|
|||
|
|
@ -5,32 +5,32 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { NodePath } from "@babel/core";
|
||||
import * as t from "@babel/types";
|
||||
import {NodePath} from '@babel/core';
|
||||
import * as t from '@babel/types';
|
||||
import {
|
||||
CompilerError,
|
||||
CompilerErrorDetail,
|
||||
ErrorSeverity,
|
||||
} from "../CompilerError";
|
||||
} from '../CompilerError';
|
||||
import {
|
||||
ExternalFunction,
|
||||
ReactFunctionType,
|
||||
parseEnvironmentConfig,
|
||||
tryParseExternalFunction,
|
||||
} from "../HIR/Environment";
|
||||
import { CodegenFunction } from "../ReactiveScopes";
|
||||
import { isComponentDeclaration } from "../Utils/ComponentDeclaration";
|
||||
import { isHookDeclaration } from "../Utils/HookDeclaration";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
import { insertGatedFunctionDeclaration } from "./Gating";
|
||||
import { addImportsToProgram, updateMemoCacheFunctionImport } from "./Imports";
|
||||
import { PluginOptions } from "./Options";
|
||||
import { compileFn } from "./Pipeline";
|
||||
} from '../HIR/Environment';
|
||||
import {CodegenFunction} from '../ReactiveScopes';
|
||||
import {isComponentDeclaration} from '../Utils/ComponentDeclaration';
|
||||
import {isHookDeclaration} from '../Utils/HookDeclaration';
|
||||
import {assertExhaustive} from '../Utils/utils';
|
||||
import {insertGatedFunctionDeclaration} from './Gating';
|
||||
import {addImportsToProgram, updateMemoCacheFunctionImport} from './Imports';
|
||||
import {PluginOptions} from './Options';
|
||||
import {compileFn} from './Pipeline';
|
||||
import {
|
||||
filterSuppressionsThatAffectFunction,
|
||||
findProgramSuppressions,
|
||||
suppressionsToCompilerError,
|
||||
} from "./Suppression";
|
||||
} from './Suppression';
|
||||
|
||||
export type CompilerPass = {
|
||||
opts: PluginOptions;
|
||||
|
|
@ -40,11 +40,11 @@ export type CompilerPass = {
|
|||
};
|
||||
|
||||
function findDirectiveEnablingMemoization(
|
||||
directives: Array<t.Directive>
|
||||
directives: Array<t.Directive>,
|
||||
): t.Directive | null {
|
||||
for (const directive of directives) {
|
||||
const directiveValue = directive.value.value;
|
||||
if (directiveValue === "use forget" || directiveValue === "use memo") {
|
||||
if (directiveValue === 'use forget' || directiveValue === 'use memo') {
|
||||
return directive;
|
||||
}
|
||||
}
|
||||
|
|
@ -53,13 +53,13 @@ function findDirectiveEnablingMemoization(
|
|||
|
||||
function findDirectiveDisablingMemoization(
|
||||
directives: Array<t.Directive>,
|
||||
options: PluginOptions
|
||||
options: PluginOptions,
|
||||
): t.Directive | null {
|
||||
for (const directive of directives) {
|
||||
const directiveValue = directive.value.value;
|
||||
if (
|
||||
(directiveValue === "use no forget" ||
|
||||
directiveValue === "use no memo") &&
|
||||
(directiveValue === 'use no forget' ||
|
||||
directiveValue === 'use no memo') &&
|
||||
!options.ignoreUseNoForget
|
||||
) {
|
||||
return directive;
|
||||
|
|
@ -75,7 +75,7 @@ function isCriticalError(err: unknown): boolean {
|
|||
function isConfigError(err: unknown): boolean {
|
||||
if (err instanceof CompilerError) {
|
||||
return err.details.some(
|
||||
(detail) => detail.severity === ErrorSeverity.InvalidConfig
|
||||
detail => detail.severity === ErrorSeverity.InvalidConfig,
|
||||
);
|
||||
}
|
||||
return false;
|
||||
|
|
@ -92,7 +92,7 @@ export type CompileResult = {
|
|||
* functions which were outlined. Only original functions need to be gated
|
||||
* if gating mode is enabled.
|
||||
*/
|
||||
kind: "original" | "outlined";
|
||||
kind: 'original' | 'outlined';
|
||||
originalFn: BabelFn;
|
||||
compiledFn: CodegenFunction;
|
||||
};
|
||||
|
|
@ -100,13 +100,13 @@ export type CompileResult = {
|
|||
function handleError(
|
||||
err: unknown,
|
||||
pass: CompilerPass,
|
||||
fnLoc: t.SourceLocation | null
|
||||
fnLoc: t.SourceLocation | null,
|
||||
): void {
|
||||
if (pass.opts.logger) {
|
||||
if (err instanceof CompilerError) {
|
||||
for (const detail of err.details) {
|
||||
pass.opts.logger.logEvent(pass.filename, {
|
||||
kind: "CompileError",
|
||||
kind: 'CompileError',
|
||||
fnLoc,
|
||||
detail: detail.options,
|
||||
});
|
||||
|
|
@ -116,19 +116,19 @@ function handleError(
|
|||
if (err instanceof Error) {
|
||||
stringifiedError = err.stack ?? err.message;
|
||||
} else {
|
||||
stringifiedError = err?.toString() ?? "[ null ]";
|
||||
stringifiedError = err?.toString() ?? '[ null ]';
|
||||
}
|
||||
|
||||
pass.opts.logger.logEvent(pass.filename, {
|
||||
kind: "PipelineError",
|
||||
kind: 'PipelineError',
|
||||
fnLoc,
|
||||
data: stringifiedError,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (
|
||||
pass.opts.panicThreshold === "all_errors" ||
|
||||
(pass.opts.panicThreshold === "critical_errors" && isCriticalError(err)) ||
|
||||
pass.opts.panicThreshold === 'all_errors' ||
|
||||
(pass.opts.panicThreshold === 'critical_errors' && isCriticalError(err)) ||
|
||||
isConfigError(err) // Always throws regardless of panic threshold
|
||||
) {
|
||||
throw err;
|
||||
|
|
@ -137,16 +137,16 @@ function handleError(
|
|||
|
||||
export function createNewFunctionNode(
|
||||
originalFn: BabelFn,
|
||||
compiledFn: CodegenFunction
|
||||
compiledFn: CodegenFunction,
|
||||
): t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression {
|
||||
let transformedFn:
|
||||
| t.FunctionDeclaration
|
||||
| t.ArrowFunctionExpression
|
||||
| t.FunctionExpression;
|
||||
switch (originalFn.node.type) {
|
||||
case "FunctionDeclaration": {
|
||||
case 'FunctionDeclaration': {
|
||||
const fn: t.FunctionDeclaration = {
|
||||
type: "FunctionDeclaration",
|
||||
type: 'FunctionDeclaration',
|
||||
id: compiledFn.id,
|
||||
loc: originalFn.node.loc ?? null,
|
||||
async: compiledFn.async,
|
||||
|
|
@ -157,9 +157,9 @@ export function createNewFunctionNode(
|
|||
transformedFn = fn;
|
||||
break;
|
||||
}
|
||||
case "ArrowFunctionExpression": {
|
||||
case 'ArrowFunctionExpression': {
|
||||
const fn: t.ArrowFunctionExpression = {
|
||||
type: "ArrowFunctionExpression",
|
||||
type: 'ArrowFunctionExpression',
|
||||
loc: originalFn.node.loc ?? null,
|
||||
async: compiledFn.async,
|
||||
generator: compiledFn.generator,
|
||||
|
|
@ -170,9 +170,9 @@ export function createNewFunctionNode(
|
|||
transformedFn = fn;
|
||||
break;
|
||||
}
|
||||
case "FunctionExpression": {
|
||||
case 'FunctionExpression': {
|
||||
const fn: t.FunctionExpression = {
|
||||
type: "FunctionExpression",
|
||||
type: 'FunctionExpression',
|
||||
id: compiledFn.id,
|
||||
loc: originalFn.node.loc ?? null,
|
||||
async: compiledFn.async,
|
||||
|
|
@ -198,15 +198,15 @@ export function createNewFunctionNode(
|
|||
const ALREADY_COMPILED: WeakSet<object> | Set<object> = new (WeakSet ?? Set)();
|
||||
|
||||
const DEFAULT_ESLINT_SUPPRESSIONS = [
|
||||
"react-hooks/exhaustive-deps",
|
||||
"react-hooks/rules-of-hooks",
|
||||
'react-hooks/exhaustive-deps',
|
||||
'react-hooks/rules-of-hooks',
|
||||
];
|
||||
|
||||
function isFilePartOfSources(
|
||||
sources: Array<string> | ((filename: string) => boolean),
|
||||
filename: string
|
||||
filename: string,
|
||||
): boolean {
|
||||
if (typeof sources === "function") {
|
||||
if (typeof sources === 'function') {
|
||||
return sources(filename);
|
||||
}
|
||||
|
||||
|
|
@ -221,7 +221,7 @@ function isFilePartOfSources(
|
|||
|
||||
export function compileProgram(
|
||||
program: NodePath<t.Program>,
|
||||
pass: CompilerPass
|
||||
pass: CompilerPass,
|
||||
): void {
|
||||
if (pass.opts.sources) {
|
||||
if (pass.filename === null) {
|
||||
|
|
@ -233,7 +233,7 @@ export function compileProgram(
|
|||
"When the 'sources' config options is specified, the React compiler will only compile files with a name",
|
||||
severity: ErrorSeverity.InvalidConfig,
|
||||
loc: null,
|
||||
})
|
||||
}),
|
||||
);
|
||||
handleError(error, pass, null);
|
||||
return;
|
||||
|
|
@ -253,8 +253,8 @@ export function compileProgram(
|
|||
}
|
||||
|
||||
const environment = parseEnvironmentConfig(pass.opts.environment ?? {});
|
||||
const useMemoCacheIdentifier = program.scope.generateUidIdentifier("c");
|
||||
const moduleName = pass.opts.runtimeModule ?? "react/compiler-runtime";
|
||||
const useMemoCacheIdentifier = program.scope.generateUidIdentifier('c');
|
||||
const moduleName = pass.opts.runtimeModule ?? 'react/compiler-runtime';
|
||||
if (hasMemoCacheFunctionImport(program, moduleName)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -267,12 +267,12 @@ export function compileProgram(
|
|||
const suppressions = findProgramSuppressions(
|
||||
pass.comments,
|
||||
pass.opts.eslintSuppressionRules ?? DEFAULT_ESLINT_SUPPRESSIONS,
|
||||
pass.opts.flowSuppressions
|
||||
pass.opts.flowSuppressions,
|
||||
);
|
||||
const lintError = suppressionsToCompilerError(suppressions);
|
||||
let hasCriticalError = lintError != null;
|
||||
const queue: Array<{
|
||||
kind: "original" | "outlined";
|
||||
kind: 'original' | 'outlined';
|
||||
fn: BabelFn;
|
||||
fnType: ReactFunctionType;
|
||||
}> = [];
|
||||
|
|
@ -292,7 +292,7 @@ export function compileProgram(
|
|||
ALREADY_COMPILED.add(fn.node);
|
||||
fn.skip();
|
||||
|
||||
queue.push({ kind: "original", fn, fnType });
|
||||
queue.push({kind: 'original', fn, fnType});
|
||||
};
|
||||
|
||||
// Main traversal to compile with Forget
|
||||
|
|
@ -324,14 +324,14 @@ export function compileProgram(
|
|||
},
|
||||
{
|
||||
...pass,
|
||||
opts: { ...pass.opts, ...pass.opts },
|
||||
opts: {...pass.opts, ...pass.opts},
|
||||
filename: pass.filename ?? null,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const processFn = (
|
||||
fn: BabelFn,
|
||||
fnType: ReactFunctionType
|
||||
fnType: ReactFunctionType,
|
||||
): null | CodegenFunction => {
|
||||
if (lintError != null) {
|
||||
/**
|
||||
|
|
@ -341,7 +341,7 @@ export function compileProgram(
|
|||
*/
|
||||
const suppressionsInFunction = filterSuppressionsThatAffectFunction(
|
||||
suppressions,
|
||||
fn
|
||||
fn,
|
||||
);
|
||||
if (suppressionsInFunction.length > 0) {
|
||||
handleError(lintError, pass, fn.node.loc ?? null);
|
||||
|
|
@ -357,7 +357,7 @@ export function compileProgram(
|
|||
if (environment.isErr()) {
|
||||
CompilerError.throwInvalidConfig({
|
||||
reason:
|
||||
"Error in validating environment config. This is an advanced setting and not meant to be used directly",
|
||||
'Error in validating environment config. This is an advanced setting and not meant to be used directly',
|
||||
description: environment.unwrapErr().toString(),
|
||||
suggestions: null,
|
||||
loc: null,
|
||||
|
|
@ -372,10 +372,10 @@ export function compileProgram(
|
|||
useMemoCacheIdentifier.name,
|
||||
pass.opts.logger,
|
||||
pass.filename,
|
||||
pass.code
|
||||
pass.code,
|
||||
);
|
||||
pass.opts.logger?.logEvent(pass.filename, {
|
||||
kind: "CompileSuccess",
|
||||
kind: 'CompileSuccess',
|
||||
fnLoc: fn.node.loc ?? null,
|
||||
fnName: compiledFn.id?.name ?? null,
|
||||
memoSlots: compiledFn.memoSlotsUsed,
|
||||
|
|
@ -404,11 +404,11 @@ export function compileProgram(
|
|||
}
|
||||
for (const outlined of compiled.outlined) {
|
||||
CompilerError.invariant(outlined.fn.outlined.length === 0, {
|
||||
reason: "Unexpected nested outlined functions",
|
||||
reason: 'Unexpected nested outlined functions',
|
||||
loc: outlined.fn.loc,
|
||||
});
|
||||
const fn = current.fn.insertAfter(
|
||||
createNewFunctionNode(current.fn, outlined.fn)
|
||||
createNewFunctionNode(current.fn, outlined.fn),
|
||||
)[0]!;
|
||||
fn.skip();
|
||||
ALREADY_COMPILED.add(fn.node);
|
||||
|
|
@ -437,9 +437,9 @@ export function compileProgram(
|
|||
if (pass.opts.gating != null) {
|
||||
const error = checkFunctionReferencedBeforeDeclarationAtTopLevel(
|
||||
program,
|
||||
compiledFns.map((result) => {
|
||||
compiledFns.map(result => {
|
||||
return result.originalFn;
|
||||
})
|
||||
}),
|
||||
);
|
||||
if (error) {
|
||||
handleError(error, pass, null);
|
||||
|
|
@ -460,32 +460,32 @@ export function compileProgram(
|
|||
pass.opts.environment?.enableEmitInstrumentForget;
|
||||
if (enableEmitInstrumentForget != null) {
|
||||
externalFunctions.push(
|
||||
tryParseExternalFunction(enableEmitInstrumentForget.fn)
|
||||
tryParseExternalFunction(enableEmitInstrumentForget.fn),
|
||||
);
|
||||
if (enableEmitInstrumentForget.gating != null) {
|
||||
externalFunctions.push(
|
||||
tryParseExternalFunction(enableEmitInstrumentForget.gating)
|
||||
tryParseExternalFunction(enableEmitInstrumentForget.gating),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (pass.opts.environment?.enableEmitFreeze != null) {
|
||||
const enableEmitFreeze = tryParseExternalFunction(
|
||||
pass.opts.environment.enableEmitFreeze
|
||||
pass.opts.environment.enableEmitFreeze,
|
||||
);
|
||||
externalFunctions.push(enableEmitFreeze);
|
||||
}
|
||||
|
||||
if (pass.opts.environment?.enableEmitHookGuards != null) {
|
||||
const enableEmitHookGuards = tryParseExternalFunction(
|
||||
pass.opts.environment.enableEmitHookGuards
|
||||
pass.opts.environment.enableEmitHookGuards,
|
||||
);
|
||||
externalFunctions.push(enableEmitHookGuards);
|
||||
}
|
||||
|
||||
if (pass.opts.environment?.enableChangeDetectionForDebugging != null) {
|
||||
const enableChangeDetectionForDebugging = tryParseExternalFunction(
|
||||
pass.opts.environment.enableChangeDetectionForDebugging
|
||||
pass.opts.environment.enableChangeDetectionForDebugging,
|
||||
);
|
||||
externalFunctions.push(enableChangeDetectionForDebugging);
|
||||
}
|
||||
|
|
@ -499,10 +499,10 @@ export function compileProgram(
|
|||
* error elsewhere in the file, regardless of bailout mode.
|
||||
*/
|
||||
for (const result of compiledFns) {
|
||||
const { kind, originalFn, compiledFn } = result;
|
||||
const {kind, originalFn, compiledFn} = result;
|
||||
const transformedFn = createNewFunctionNode(originalFn, compiledFn);
|
||||
|
||||
if (gating != null && kind === "original") {
|
||||
if (gating != null && kind === 'original') {
|
||||
insertGatedFunctionDeclaration(originalFn, transformedFn, gating);
|
||||
} else {
|
||||
originalFn.replaceWith(transformedFn);
|
||||
|
|
@ -523,7 +523,7 @@ export function compileProgram(
|
|||
updateMemoCacheFunctionImport(
|
||||
program,
|
||||
moduleName,
|
||||
useMemoCacheIdentifier.name
|
||||
useMemoCacheIdentifier.name,
|
||||
);
|
||||
}
|
||||
addImportsToProgram(program, externalFunctions);
|
||||
|
|
@ -532,18 +532,18 @@ export function compileProgram(
|
|||
|
||||
function getReactFunctionType(
|
||||
fn: BabelFn,
|
||||
pass: CompilerPass
|
||||
pass: CompilerPass,
|
||||
): ReactFunctionType | null {
|
||||
const hookPattern = pass.opts.environment?.hookPattern ?? null;
|
||||
if (fn.node.body.type === "BlockStatement") {
|
||||
if (fn.node.body.type === 'BlockStatement') {
|
||||
// Opt-outs disable compilation regardless of mode
|
||||
const useNoForget = findDirectiveDisablingMemoization(
|
||||
fn.node.body.directives,
|
||||
pass.opts
|
||||
pass.opts,
|
||||
);
|
||||
if (useNoForget != null) {
|
||||
pass.opts.logger?.logEvent(pass.filename, {
|
||||
kind: "CompileError",
|
||||
kind: 'CompileError',
|
||||
fnLoc: fn.node.body.loc ?? null,
|
||||
detail: {
|
||||
severity: ErrorSeverity.Todo,
|
||||
|
|
@ -556,7 +556,7 @@ function getReactFunctionType(
|
|||
}
|
||||
// Otherwise opt-ins enable compilation regardless of mode
|
||||
if (findDirectiveEnablingMemoization(fn.node.body.directives) != null) {
|
||||
return getComponentOrHookLike(fn, hookPattern) ?? "Other";
|
||||
return getComponentOrHookLike(fn, hookPattern) ?? 'Other';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -564,36 +564,36 @@ function getReactFunctionType(
|
|||
let componentSyntaxType: ReactFunctionType | null = null;
|
||||
if (fn.isFunctionDeclaration()) {
|
||||
if (isComponentDeclaration(fn.node)) {
|
||||
componentSyntaxType = "Component";
|
||||
componentSyntaxType = 'Component';
|
||||
} else if (isHookDeclaration(fn.node)) {
|
||||
componentSyntaxType = "Hook";
|
||||
componentSyntaxType = 'Hook';
|
||||
}
|
||||
}
|
||||
|
||||
switch (pass.opts.compilationMode) {
|
||||
case "annotation": {
|
||||
case 'annotation': {
|
||||
// opt-ins are checked above
|
||||
return null;
|
||||
}
|
||||
case "infer": {
|
||||
case 'infer': {
|
||||
// Check if this is a component or hook-like function
|
||||
return componentSyntaxType ?? getComponentOrHookLike(fn, hookPattern);
|
||||
}
|
||||
case "syntax": {
|
||||
case 'syntax': {
|
||||
return componentSyntaxType;
|
||||
}
|
||||
case "all": {
|
||||
case 'all': {
|
||||
// Compile only top level functions
|
||||
if (fn.scope.getProgramParent() !== fn.scope.parent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getComponentOrHookLike(fn, hookPattern) ?? "Other";
|
||||
return getComponentOrHookLike(fn, hookPattern) ?? 'Other';
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
pass.opts.compilationMode,
|
||||
`Unexpected compilationMode \`${pass.opts.compilationMode}\``
|
||||
`Unexpected compilationMode \`${pass.opts.compilationMode}\``,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -606,12 +606,12 @@ function getReactFunctionType(
|
|||
*/
|
||||
function hasMemoCacheFunctionImport(
|
||||
program: NodePath<t.Program>,
|
||||
moduleName: string
|
||||
moduleName: string,
|
||||
): boolean {
|
||||
let hasUseMemoCache = false;
|
||||
program.traverse({
|
||||
ImportSpecifier(path) {
|
||||
const imported = path.get("imported");
|
||||
const imported = path.get('imported');
|
||||
let importedName: string | null = null;
|
||||
if (imported.isIdentifier()) {
|
||||
importedName = imported.node.name;
|
||||
|
|
@ -619,9 +619,9 @@ function hasMemoCacheFunctionImport(
|
|||
importedName = imported.node.value;
|
||||
}
|
||||
if (
|
||||
importedName === "c" &&
|
||||
importedName === 'c' &&
|
||||
path.parentPath.isImportDeclaration() &&
|
||||
path.parentPath.get("source").node.value === moduleName
|
||||
path.parentPath.get('source').node.value === moduleName
|
||||
) {
|
||||
hasUseMemoCache = true;
|
||||
}
|
||||
|
|
@ -644,18 +644,18 @@ function isHookName(s: string, hookPattern: string | null): boolean {
|
|||
|
||||
function isHook(
|
||||
path: NodePath<t.Expression | t.PrivateName>,
|
||||
hookPattern: string | null
|
||||
hookPattern: string | null,
|
||||
): boolean {
|
||||
if (path.isIdentifier()) {
|
||||
return isHookName(path.node.name, hookPattern);
|
||||
} else if (
|
||||
path.isMemberExpression() &&
|
||||
!path.node.computed &&
|
||||
isHook(path.get("property"), hookPattern)
|
||||
isHook(path.get('property'), hookPattern)
|
||||
) {
|
||||
const obj = path.get("object").node;
|
||||
const obj = path.get('object').node;
|
||||
const isPascalCaseNameSpace = /^[A-Z].*/;
|
||||
return obj.type === "Identifier" && isPascalCaseNameSpace.test(obj.name);
|
||||
return obj.type === 'Identifier' && isPascalCaseNameSpace.test(obj.name);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -672,15 +672,15 @@ function isComponentName(path: NodePath<t.Expression>): boolean {
|
|||
|
||||
function isReactAPI(
|
||||
path: NodePath<t.Expression | t.PrivateName | t.V8IntrinsicIdentifier>,
|
||||
functionName: string
|
||||
functionName: string,
|
||||
): boolean {
|
||||
const node = path.node;
|
||||
return (
|
||||
(node.type === "Identifier" && node.name === functionName) ||
|
||||
(node.type === "MemberExpression" &&
|
||||
node.object.type === "Identifier" &&
|
||||
node.object.name === "React" &&
|
||||
node.property.type === "Identifier" &&
|
||||
(node.type === 'Identifier' && node.name === functionName) ||
|
||||
(node.type === 'MemberExpression' &&
|
||||
node.object.type === 'Identifier' &&
|
||||
node.object.name === 'React' &&
|
||||
node.property.type === 'Identifier' &&
|
||||
node.property.name === functionName)
|
||||
);
|
||||
}
|
||||
|
|
@ -693,8 +693,8 @@ function isReactAPI(
|
|||
function isForwardRefCallback(path: NodePath<t.Expression>): boolean {
|
||||
return !!(
|
||||
path.parentPath.isCallExpression() &&
|
||||
path.parentPath.get("callee").isExpression() &&
|
||||
isReactAPI(path.parentPath.get("callee"), "forwardRef")
|
||||
path.parentPath.get('callee').isExpression() &&
|
||||
isReactAPI(path.parentPath.get('callee'), 'forwardRef')
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -706,50 +706,50 @@ function isForwardRefCallback(path: NodePath<t.Expression>): boolean {
|
|||
function isMemoCallback(path: NodePath<t.Expression>): boolean {
|
||||
return (
|
||||
path.parentPath.isCallExpression() &&
|
||||
path.parentPath.get("callee").isExpression() &&
|
||||
isReactAPI(path.parentPath.get("callee"), "memo")
|
||||
path.parentPath.get('callee').isExpression() &&
|
||||
isReactAPI(path.parentPath.get('callee'), 'memo')
|
||||
);
|
||||
}
|
||||
|
||||
function isValidPropsAnnotation(
|
||||
annot: t.TypeAnnotation | t.TSTypeAnnotation | t.Noop | null | undefined
|
||||
annot: t.TypeAnnotation | t.TSTypeAnnotation | t.Noop | null | undefined,
|
||||
): boolean {
|
||||
if (annot == null) {
|
||||
return true;
|
||||
} else if (annot.type === "TSTypeAnnotation") {
|
||||
} else if (annot.type === 'TSTypeAnnotation') {
|
||||
switch (annot.typeAnnotation.type) {
|
||||
case "TSArrayType":
|
||||
case "TSBigIntKeyword":
|
||||
case "TSBooleanKeyword":
|
||||
case "TSConstructorType":
|
||||
case "TSFunctionType":
|
||||
case "TSLiteralType":
|
||||
case "TSNeverKeyword":
|
||||
case "TSNumberKeyword":
|
||||
case "TSStringKeyword":
|
||||
case "TSSymbolKeyword":
|
||||
case "TSTupleType":
|
||||
case 'TSArrayType':
|
||||
case 'TSBigIntKeyword':
|
||||
case 'TSBooleanKeyword':
|
||||
case 'TSConstructorType':
|
||||
case 'TSFunctionType':
|
||||
case 'TSLiteralType':
|
||||
case 'TSNeverKeyword':
|
||||
case 'TSNumberKeyword':
|
||||
case 'TSStringKeyword':
|
||||
case 'TSSymbolKeyword':
|
||||
case 'TSTupleType':
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (annot.type === "TypeAnnotation") {
|
||||
} else if (annot.type === 'TypeAnnotation') {
|
||||
switch (annot.typeAnnotation.type) {
|
||||
case "ArrayTypeAnnotation":
|
||||
case "BooleanLiteralTypeAnnotation":
|
||||
case "BooleanTypeAnnotation":
|
||||
case "EmptyTypeAnnotation":
|
||||
case "FunctionTypeAnnotation":
|
||||
case "NumberLiteralTypeAnnotation":
|
||||
case "NumberTypeAnnotation":
|
||||
case "StringLiteralTypeAnnotation":
|
||||
case "StringTypeAnnotation":
|
||||
case "SymbolTypeAnnotation":
|
||||
case "ThisTypeAnnotation":
|
||||
case "TupleTypeAnnotation":
|
||||
case 'ArrayTypeAnnotation':
|
||||
case 'BooleanLiteralTypeAnnotation':
|
||||
case 'BooleanTypeAnnotation':
|
||||
case 'EmptyTypeAnnotation':
|
||||
case 'FunctionTypeAnnotation':
|
||||
case 'NumberLiteralTypeAnnotation':
|
||||
case 'NumberTypeAnnotation':
|
||||
case 'StringLiteralTypeAnnotation':
|
||||
case 'StringTypeAnnotation':
|
||||
case 'SymbolTypeAnnotation':
|
||||
case 'ThisTypeAnnotation':
|
||||
case 'TupleTypeAnnotation':
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (annot.type === "Noop") {
|
||||
} else if (annot.type === 'Noop') {
|
||||
return true;
|
||||
} else {
|
||||
assertExhaustive(annot, `Unexpected annotation node \`${annot}\``);
|
||||
|
|
@ -757,7 +757,7 @@ function isValidPropsAnnotation(
|
|||
}
|
||||
|
||||
function isValidComponentParams(
|
||||
params: Array<NodePath<t.Identifier | t.Pattern | t.RestElement>>
|
||||
params: Array<NodePath<t.Identifier | t.Pattern | t.RestElement>>,
|
||||
): boolean {
|
||||
if (params.length === 0) {
|
||||
return true;
|
||||
|
|
@ -770,8 +770,8 @@ function isValidComponentParams(
|
|||
return !params[0].isRestElement();
|
||||
} else if (params[1].isIdentifier()) {
|
||||
// check if second param might be a ref
|
||||
const { name } = params[1].node;
|
||||
return name.includes("ref") || name.includes("Ref");
|
||||
const {name} = params[1].node;
|
||||
return name.includes('ref') || name.includes('Ref');
|
||||
} else {
|
||||
/**
|
||||
* Otherwise, avoid helper functions that take more than one argument.
|
||||
|
|
@ -792,19 +792,19 @@ function getComponentOrHookLike(
|
|||
node: NodePath<
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
>,
|
||||
hookPattern: string | null
|
||||
hookPattern: string | null,
|
||||
): ReactFunctionType | null {
|
||||
const functionName = getFunctionName(node);
|
||||
// Check if the name is component or hook like:
|
||||
if (functionName !== null && isComponentName(functionName)) {
|
||||
let isComponent =
|
||||
callsHooksOrCreatesJsx(node, hookPattern) &&
|
||||
isValidComponentParams(node.get("params")) &&
|
||||
isValidComponentParams(node.get('params')) &&
|
||||
!returnsNonNode(node);
|
||||
return isComponent ? "Component" : null;
|
||||
return isComponent ? 'Component' : null;
|
||||
} else if (functionName !== null && isHook(functionName, hookPattern)) {
|
||||
// Hooks have hook invocations or JSX, but can take any # of arguments
|
||||
return callsHooksOrCreatesJsx(node, hookPattern) ? "Hook" : null;
|
||||
return callsHooksOrCreatesJsx(node, hookPattern) ? 'Hook' : null;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -814,7 +814,7 @@ function getComponentOrHookLike(
|
|||
if (node.isFunctionExpression() || node.isArrowFunctionExpression()) {
|
||||
if (isForwardRefCallback(node) || isMemoCallback(node)) {
|
||||
// As an added check we also look for hook invocations or JSX
|
||||
return callsHooksOrCreatesJsx(node, hookPattern) ? "Component" : null;
|
||||
return callsHooksOrCreatesJsx(node, hookPattern) ? 'Component' : null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
@ -823,12 +823,12 @@ function getComponentOrHookLike(
|
|||
function skipNestedFunctions(
|
||||
node: NodePath<
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
>
|
||||
>,
|
||||
) {
|
||||
return (
|
||||
fn: NodePath<
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
>
|
||||
>,
|
||||
): void => {
|
||||
if (fn.node !== node.node) {
|
||||
fn.skip();
|
||||
|
|
@ -840,7 +840,7 @@ function callsHooksOrCreatesJsx(
|
|||
node: NodePath<
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
>,
|
||||
hookPattern: string | null
|
||||
hookPattern: string | null,
|
||||
): boolean {
|
||||
let invokesHooks = false;
|
||||
let createsJsx = false;
|
||||
|
|
@ -850,7 +850,7 @@ function callsHooksOrCreatesJsx(
|
|||
createsJsx = true;
|
||||
},
|
||||
CallExpression(call) {
|
||||
const callee = call.get("callee");
|
||||
const callee = call.get('callee');
|
||||
if (callee.isExpression() && isHook(callee, hookPattern)) {
|
||||
invokesHooks = true;
|
||||
}
|
||||
|
|
@ -866,7 +866,7 @@ function callsHooksOrCreatesJsx(
|
|||
function returnsNonNode(
|
||||
node: NodePath<
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
>
|
||||
>,
|
||||
): boolean {
|
||||
let hasReturn = false;
|
||||
let returnsNonNode = false;
|
||||
|
|
@ -879,12 +879,12 @@ function returnsNonNode(
|
|||
returnsNonNode = true;
|
||||
} else {
|
||||
switch (argument.type) {
|
||||
case "ObjectExpression":
|
||||
case "ArrowFunctionExpression":
|
||||
case "FunctionExpression":
|
||||
case "BigIntLiteral":
|
||||
case "ClassExpression":
|
||||
case "NewExpression": // technically `new Array()` is legit, but unlikely
|
||||
case 'ObjectExpression':
|
||||
case 'ArrowFunctionExpression':
|
||||
case 'FunctionExpression':
|
||||
case 'BigIntLiteral':
|
||||
case 'ClassExpression':
|
||||
case 'NewExpression': // technically `new Array()` is legit, but unlikely
|
||||
returnsNonNode = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -908,10 +908,10 @@ function returnsNonNode(
|
|||
function getFunctionName(
|
||||
path: NodePath<
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
>
|
||||
>,
|
||||
): NodePath<t.Expression> | null {
|
||||
if (path.isFunctionDeclaration()) {
|
||||
const id = path.get("id");
|
||||
const id = path.get('id');
|
||||
if (id.isIdentifier()) {
|
||||
return id;
|
||||
}
|
||||
|
|
@ -919,31 +919,31 @@ function getFunctionName(
|
|||
}
|
||||
let id: NodePath<t.LVal | t.Expression | t.PrivateName> | null = null;
|
||||
const parent = path.parentPath;
|
||||
if (parent.isVariableDeclarator() && parent.get("init").node === path.node) {
|
||||
if (parent.isVariableDeclarator() && parent.get('init').node === path.node) {
|
||||
// const useHook = () => {};
|
||||
id = parent.get("id");
|
||||
id = parent.get('id');
|
||||
} else if (
|
||||
parent.isAssignmentExpression() &&
|
||||
parent.get("right").node === path.node &&
|
||||
parent.get("operator") === "="
|
||||
parent.get('right').node === path.node &&
|
||||
parent.get('operator') === '='
|
||||
) {
|
||||
// useHook = () => {};
|
||||
id = parent.get("left");
|
||||
id = parent.get('left');
|
||||
} else if (
|
||||
parent.isProperty() &&
|
||||
parent.get("value").node === path.node &&
|
||||
!parent.get("computed") &&
|
||||
parent.get("key").isLVal()
|
||||
parent.get('value').node === path.node &&
|
||||
!parent.get('computed') &&
|
||||
parent.get('key').isLVal()
|
||||
) {
|
||||
/*
|
||||
* {useHook: () => {}}
|
||||
* {useHook() {}}
|
||||
*/
|
||||
id = parent.get("key");
|
||||
id = parent.get('key');
|
||||
} else if (
|
||||
parent.isAssignmentPattern() &&
|
||||
parent.get("right").node === path.node &&
|
||||
!parent.get("computed")
|
||||
parent.get('right').node === path.node &&
|
||||
!parent.get('computed')
|
||||
) {
|
||||
/*
|
||||
* const {useHook = () => {}} = {};
|
||||
|
|
@ -952,7 +952,7 @@ function getFunctionName(
|
|||
* Kinda clowny, but we'd said we'd follow spec convention for
|
||||
* `IsAnonymousFunctionDefinition()` usage.
|
||||
*/
|
||||
id = parent.get("left");
|
||||
id = parent.get('left');
|
||||
}
|
||||
if (id !== null && (id.isIdentifier() || id.isMemberExpression())) {
|
||||
return id;
|
||||
|
|
@ -963,17 +963,17 @@ function getFunctionName(
|
|||
|
||||
function checkFunctionReferencedBeforeDeclarationAtTopLevel(
|
||||
program: NodePath<t.Program>,
|
||||
fns: Array<BabelFn>
|
||||
fns: Array<BabelFn>,
|
||||
): CompilerError | null {
|
||||
const fnIds = new Set(
|
||||
fns
|
||||
.map((fn) => getFunctionName(fn))
|
||||
.map(fn => getFunctionName(fn))
|
||||
.filter(
|
||||
(name): name is NodePath<t.Identifier> => !!name && name.isIdentifier()
|
||||
(name): name is NodePath<t.Identifier> => !!name && name.isIdentifier(),
|
||||
)
|
||||
.map((name) => name.node)
|
||||
.map(name => name.node),
|
||||
);
|
||||
const fnNames = new Map([...fnIds].map((id) => [id.name, id]));
|
||||
const fnNames = new Map([...fnIds].map(id => [id.name, id]));
|
||||
const errors = new CompilerError();
|
||||
|
||||
program.traverse({
|
||||
|
|
@ -1019,7 +1019,7 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel(
|
|||
loc: fn.loc ?? null,
|
||||
suggestions: null,
|
||||
severity: ErrorSeverity.Invariant,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import type * as BabelCore from "@babel/core";
|
||||
import { hasOwnProperty } from "../Utils/utils";
|
||||
import { PluginOptions } from "./Options";
|
||||
import type * as BabelCore from '@babel/core';
|
||||
import {hasOwnProperty} from '../Utils/utils';
|
||||
import {PluginOptions} from './Options';
|
||||
|
||||
function hasModule(name: string): boolean {
|
||||
try {
|
||||
return !!require.resolve(name);
|
||||
} catch (error: any) {
|
||||
if (
|
||||
error.code === "MODULE_NOT_FOUND" &&
|
||||
error.code === 'MODULE_NOT_FOUND' &&
|
||||
error.message.indexOf(name) !== -1
|
||||
) {
|
||||
return false;
|
||||
|
|
@ -24,22 +24,22 @@ function hasModule(name: string): boolean {
|
|||
* See https://github.com/expo/expo/blob/e4b8d86442482c7316365a6b7ec1141eec73409d/packages/babel-preset-expo/src/index.ts#L300-L301
|
||||
*/
|
||||
export function pipelineUsesReanimatedPlugin(
|
||||
plugins: Array<BabelCore.PluginItem> | null | undefined
|
||||
plugins: Array<BabelCore.PluginItem> | null | undefined,
|
||||
): boolean {
|
||||
if (Array.isArray(plugins)) {
|
||||
for (const plugin of plugins) {
|
||||
if (hasOwnProperty(plugin, "key")) {
|
||||
if (hasOwnProperty(plugin, 'key')) {
|
||||
const key = (plugin as any).key; // already checked
|
||||
if (
|
||||
typeof key === "string" &&
|
||||
key.indexOf("react-native-reanimated") !== -1
|
||||
typeof key === 'string' &&
|
||||
key.indexOf('react-native-reanimated') !== -1
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasModule("react-native-reanimated");
|
||||
return hasModule('react-native-reanimated');
|
||||
}
|
||||
|
||||
export function injectReanimatedFlag(options: PluginOptions): PluginOptions {
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { NodePath } from "@babel/core";
|
||||
import * as t from "@babel/types";
|
||||
import {NodePath} from '@babel/core';
|
||||
import * as t from '@babel/types';
|
||||
import {
|
||||
CompilerError,
|
||||
CompilerErrorDetail,
|
||||
CompilerSuggestionOperation,
|
||||
ErrorSeverity,
|
||||
} from "../CompilerError";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
} from '../CompilerError';
|
||||
import {assertExhaustive} from '../Utils/utils';
|
||||
|
||||
/**
|
||||
* Captures the start and end range of a pair of eslint-disable ... eslint-enable comments. In the
|
||||
|
|
@ -29,7 +29,7 @@ export type SuppressionRange = {
|
|||
source: SuppressionSource;
|
||||
};
|
||||
|
||||
type SuppressionSource = "Eslint" | "Flow";
|
||||
type SuppressionSource = 'Eslint' | 'Flow';
|
||||
|
||||
/**
|
||||
* An suppression affects a function if:
|
||||
|
|
@ -38,7 +38,7 @@ type SuppressionSource = "Eslint" | "Flow";
|
|||
*/
|
||||
export function filterSuppressionsThatAffectFunction(
|
||||
suppressionRanges: Array<SuppressionRange>,
|
||||
fn: NodePath<t.Function>
|
||||
fn: NodePath<t.Function>,
|
||||
): Array<SuppressionRange> {
|
||||
const suppressionsInScope: Array<SuppressionRange> = [];
|
||||
const fnNode = fn.node;
|
||||
|
|
@ -78,21 +78,21 @@ export function filterSuppressionsThatAffectFunction(
|
|||
export function findProgramSuppressions(
|
||||
programComments: Array<t.Comment>,
|
||||
ruleNames: Array<string>,
|
||||
flowSuppressions: boolean
|
||||
flowSuppressions: boolean,
|
||||
): Array<SuppressionRange> {
|
||||
const suppressionRanges: Array<SuppressionRange> = [];
|
||||
let disableComment: t.Comment | null = null;
|
||||
let enableComment: t.Comment | null = null;
|
||||
let source: SuppressionSource | null = null;
|
||||
|
||||
const rulePattern = `(${ruleNames.join("|")})`;
|
||||
const rulePattern = `(${ruleNames.join('|')})`;
|
||||
const disableNextLinePattern = new RegExp(
|
||||
`eslint-disable-next-line ${rulePattern}`
|
||||
`eslint-disable-next-line ${rulePattern}`,
|
||||
);
|
||||
const disablePattern = new RegExp(`eslint-disable ${rulePattern}`);
|
||||
const enablePattern = new RegExp(`eslint-enable ${rulePattern}`);
|
||||
const flowSuppressionPattern = new RegExp(
|
||||
"\\$(FlowFixMe\\w*|FlowExpectedError|FlowIssue)\\[react\\-rule"
|
||||
'\\$(FlowFixMe\\w*|FlowExpectedError|FlowIssue)\\[react\\-rule',
|
||||
);
|
||||
|
||||
for (const comment of programComments) {
|
||||
|
|
@ -110,7 +110,7 @@ export function findProgramSuppressions(
|
|||
) {
|
||||
disableComment = comment;
|
||||
enableComment = comment;
|
||||
source = "Eslint";
|
||||
source = 'Eslint';
|
||||
}
|
||||
|
||||
if (
|
||||
|
|
@ -120,15 +120,15 @@ export function findProgramSuppressions(
|
|||
) {
|
||||
disableComment = comment;
|
||||
enableComment = comment;
|
||||
source = "Flow";
|
||||
source = 'Flow';
|
||||
}
|
||||
|
||||
if (disablePattern.test(comment.value)) {
|
||||
disableComment = comment;
|
||||
source = "Eslint";
|
||||
source = 'Eslint';
|
||||
}
|
||||
|
||||
if (enablePattern.test(comment.value) && source === "Eslint") {
|
||||
if (enablePattern.test(comment.value) && source === 'Eslint') {
|
||||
enableComment = comment;
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ export function findProgramSuppressions(
|
|||
}
|
||||
|
||||
export function suppressionsToCompilerError(
|
||||
suppressionRanges: Array<SuppressionRange>
|
||||
suppressionRanges: Array<SuppressionRange>,
|
||||
): CompilerError | null {
|
||||
if (suppressionRanges.length === 0) {
|
||||
return null;
|
||||
|
|
@ -162,21 +162,21 @@ export function suppressionsToCompilerError(
|
|||
}
|
||||
let reason, suggestion;
|
||||
switch (suppressionRange.source) {
|
||||
case "Eslint":
|
||||
case 'Eslint':
|
||||
reason =
|
||||
"React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled";
|
||||
'React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled';
|
||||
suggestion =
|
||||
"Remove the ESLint suppression and address the React error";
|
||||
'Remove the ESLint suppression and address the React error';
|
||||
break;
|
||||
case "Flow":
|
||||
case 'Flow':
|
||||
reason =
|
||||
"React Compiler has skipped optimizing this component because one or more React rule violations were reported by Flow";
|
||||
suggestion = "Remove the Flow suppression and address the React error";
|
||||
'React Compiler has skipped optimizing this component because one or more React rule violations were reported by Flow';
|
||||
suggestion = 'Remove the Flow suppression and address the React error';
|
||||
break;
|
||||
default:
|
||||
assertExhaustive(
|
||||
suppressionRange.source,
|
||||
"Unhandled suppression source"
|
||||
'Unhandled suppression source',
|
||||
);
|
||||
}
|
||||
error.pushErrorDetail(
|
||||
|
|
@ -195,7 +195,7 @@ export function suppressionsToCompilerError(
|
|||
op: CompilerSuggestionOperation.Remove,
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
return error;
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
export * from "./Gating";
|
||||
export * from "./Imports";
|
||||
export * from "./Options";
|
||||
export * from "./Pipeline";
|
||||
export * from "./Program";
|
||||
export * from "./Suppression";
|
||||
export * from './Gating';
|
||||
export * from './Imports';
|
||||
export * from './Options';
|
||||
export * from './Pipeline';
|
||||
export * from './Program';
|
||||
export * from './Suppression';
|
||||
|
|
|
|||
|
|
@ -5,20 +5,20 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {
|
||||
GeneratedSource,
|
||||
HIRFunction,
|
||||
Identifier,
|
||||
IdentifierId,
|
||||
SourceLocation,
|
||||
} from "./HIR";
|
||||
import { printPlace } from "./PrintHIR";
|
||||
} from './HIR';
|
||||
import {printPlace} from './PrintHIR';
|
||||
import {
|
||||
eachInstructionLValue,
|
||||
eachInstructionValueOperand,
|
||||
eachTerminalOperand,
|
||||
} from "./visitors";
|
||||
} from './visitors';
|
||||
|
||||
/*
|
||||
* Validation pass to check that there is a 1:1 mapping between Identifier objects and IdentifierIds,
|
||||
|
|
@ -44,7 +44,7 @@ export function assertConsistentIdentifiers(fn: HIRFunction): void {
|
|||
CompilerError.invariant(!assignments.has(instr.lvalue.identifier.id), {
|
||||
reason: `Expected lvalues to be assigned exactly once`,
|
||||
description: `Found duplicate assignment of '${printPlace(
|
||||
instr.lvalue
|
||||
instr.lvalue,
|
||||
)}'`,
|
||||
loc: instr.lvalue.loc,
|
||||
suggestions: null,
|
||||
|
|
@ -68,7 +68,7 @@ type Identifiers = Map<IdentifierId, Identifier>;
|
|||
function validate(
|
||||
identifiers: Identifiers,
|
||||
identifier: Identifier,
|
||||
loc: SourceLocation | null = null
|
||||
loc: SourceLocation | null = null,
|
||||
): void {
|
||||
const previous = identifiers.get(identifier.id);
|
||||
if (previous === undefined) {
|
||||
|
|
|
|||
|
|
@ -5,18 +5,18 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import { GeneratedSource, HIRFunction } from "./HIR";
|
||||
import { printTerminal } from "./PrintHIR";
|
||||
import { eachTerminalSuccessor, mapTerminalSuccessors } from "./visitors";
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {GeneratedSource, HIRFunction} from './HIR';
|
||||
import {printTerminal} from './PrintHIR';
|
||||
import {eachTerminalSuccessor, mapTerminalSuccessors} from './visitors';
|
||||
|
||||
export function assertTerminalSuccessorsExist(fn: HIRFunction): void {
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
mapTerminalSuccessors(block.terminal, (successor) => {
|
||||
mapTerminalSuccessors(block.terminal, successor => {
|
||||
CompilerError.invariant(fn.body.blocks.has(successor), {
|
||||
reason: `Terminal successor references unknown block`,
|
||||
description: `Block bb${successor} does not exist for terminal '${printTerminal(
|
||||
block.terminal
|
||||
block.terminal,
|
||||
)}'`,
|
||||
loc: (block.terminal as any).loc ?? GeneratedSource,
|
||||
suggestions: null,
|
||||
|
|
@ -31,16 +31,16 @@ export function assertTerminalPredsExist(fn: HIRFunction): void {
|
|||
for (const pred of block.preds) {
|
||||
const predBlock = fn.body.blocks.get(pred);
|
||||
CompilerError.invariant(predBlock != null, {
|
||||
reason: "Expected predecessor block to exist",
|
||||
reason: 'Expected predecessor block to exist',
|
||||
description: `Block ${block.id} references non-existent ${pred}`,
|
||||
loc: GeneratedSource,
|
||||
});
|
||||
CompilerError.invariant(
|
||||
[...eachTerminalSuccessor(predBlock.terminal)].includes(block.id),
|
||||
{
|
||||
reason: "Terminal successor does not reference correct predecessor",
|
||||
reason: 'Terminal successor does not reference correct predecessor',
|
||||
loc: GeneratedSource,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { CompilerError } from "..";
|
||||
import {CompilerError} from '..';
|
||||
import {
|
||||
BlockId,
|
||||
GeneratedSource,
|
||||
|
|
@ -7,13 +7,13 @@ import {
|
|||
Place,
|
||||
ReactiveScope,
|
||||
ScopeId,
|
||||
} from "./HIR";
|
||||
} from './HIR';
|
||||
import {
|
||||
eachInstructionLValue,
|
||||
eachInstructionOperand,
|
||||
eachTerminalOperand,
|
||||
terminalFallthrough,
|
||||
} from "./visitors";
|
||||
} from './visitors';
|
||||
|
||||
/**
|
||||
* This pass asserts that program blocks and scopes properly form a tree hierarchy
|
||||
|
|
@ -41,11 +41,11 @@ import {
|
|||
*/
|
||||
type Block =
|
||||
| ({
|
||||
kind: "ProgramBlockSubtree";
|
||||
kind: 'ProgramBlockSubtree';
|
||||
id: BlockId;
|
||||
} & MutableRange)
|
||||
| ({
|
||||
kind: "Scope";
|
||||
kind: 'Scope';
|
||||
id: ScopeId;
|
||||
} & MutableRange);
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ export function getScopes(fn: HIRFunction): Set<ReactiveScope> {
|
|||
*/
|
||||
export function rangePreOrderComparator(
|
||||
a: MutableRange,
|
||||
b: MutableRange
|
||||
b: MutableRange,
|
||||
): number {
|
||||
const startDiff = a.start - b.start;
|
||||
if (startDiff !== 0) return startDiff;
|
||||
|
|
@ -108,7 +108,7 @@ export function recursivelyTraverseItems<T, TContext>(
|
|||
getRange: (val: T) => MutableRange,
|
||||
context: TContext,
|
||||
enter: (val: T, context: TContext) => void,
|
||||
exit: (val: T, context: TContext) => void
|
||||
exit: (val: T, context: TContext) => void,
|
||||
): void {
|
||||
items.sort((a, b) => rangePreOrderComparator(getRange(a), getRange(b)));
|
||||
let activeItems: Array<T> = [];
|
||||
|
|
@ -122,7 +122,7 @@ export function recursivelyTraverseItems<T, TContext>(
|
|||
const disjoint = currRange.start >= maybeParentRange.end;
|
||||
const nested = currRange.end <= maybeParentRange.end;
|
||||
CompilerError.invariant(disjoint || nested, {
|
||||
reason: "Invalid nesting in program blocks or scopes",
|
||||
reason: 'Invalid nesting in program blocks or scopes',
|
||||
description: `Items overlap but are not nested: ${maybeParentRange.start}:${maybeParentRange.end}(${currRange.start}:${currRange.end})`,
|
||||
loc: GeneratedSource,
|
||||
});
|
||||
|
|
@ -148,8 +148,8 @@ const no_op: () => void = () => {};
|
|||
export function assertValidBlockNesting(fn: HIRFunction): void {
|
||||
const scopes = getScopes(fn);
|
||||
|
||||
const blocks: Array<Block> = [...scopes].map((scope) => ({
|
||||
kind: "Scope",
|
||||
const blocks: Array<Block> = [...scopes].map(scope => ({
|
||||
kind: 'Scope',
|
||||
id: scope.id,
|
||||
...scope.range,
|
||||
})) as Array<Block>;
|
||||
|
|
@ -159,7 +159,7 @@ export function assertValidBlockNesting(fn: HIRFunction): void {
|
|||
const fallthrough = fn.body.blocks.get(fallthroughId)!;
|
||||
const end = fallthrough.instructions[0]?.id ?? fallthrough.terminal.id;
|
||||
blocks.push({
|
||||
kind: "ProgramBlockSubtree",
|
||||
kind: 'ProgramBlockSubtree',
|
||||
id: block.id,
|
||||
start: block.terminal.id,
|
||||
end,
|
||||
|
|
@ -167,5 +167,5 @@ export function assertValidBlockNesting(fn: HIRFunction): void {
|
|||
}
|
||||
}
|
||||
|
||||
recursivelyTraverseItems(blocks, (block) => block, null, no_op, no_op);
|
||||
recursivelyTraverseItems(blocks, block => block, null, no_op, no_op);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import invariant from "invariant";
|
||||
import { HIRFunction, Identifier, MutableRange } from "./HIR";
|
||||
import invariant from 'invariant';
|
||||
import {HIRFunction, Identifier, MutableRange} from './HIR';
|
||||
import {
|
||||
eachInstructionLValue,
|
||||
eachInstructionOperand,
|
||||
eachTerminalOperand,
|
||||
} from "./visitors";
|
||||
} from './visitors';
|
||||
|
||||
/*
|
||||
* Checks that all mutable ranges in the function are well-formed, with
|
||||
|
|
@ -49,8 +49,8 @@ function validateMutableRange(mutableRange: MutableRange): void {
|
|||
invariant(
|
||||
(mutableRange.start === 0 && mutableRange.end === 0) ||
|
||||
mutableRange.end > mutableRange.start,
|
||||
"Identifier scope mutableRange was invalid: [%s:%s]",
|
||||
'Identifier scope mutableRange was invalid: [%s:%s]',
|
||||
mutableRange.start,
|
||||
mutableRange.end
|
||||
mutableRange.end,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
|||
import { CompilerError } from "../CompilerError";
|
||||
import { getScopes, recursivelyTraverseItems } from "./AssertValidBlockNesting";
|
||||
import { Environment } from "./Environment";
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {getScopes, recursivelyTraverseItems} from './AssertValidBlockNesting';
|
||||
import {Environment} from './Environment';
|
||||
import {
|
||||
BasicBlock,
|
||||
BlockId,
|
||||
|
|
@ -12,7 +12,7 @@ import {
|
|||
ReactiveScope,
|
||||
ReactiveScopeTerminal,
|
||||
ScopeId,
|
||||
} from "./HIR";
|
||||
} from './HIR';
|
||||
|
||||
/**
|
||||
* This pass assumes that all program blocks are properly nested with respect to fallthroughs
|
||||
|
|
@ -72,14 +72,14 @@ export function buildReactiveScopeTerminalsHIR(fn: HIRFunction): void {
|
|||
const queuedRewrites: Array<TerminalRewriteInfo> = [];
|
||||
recursivelyTraverseItems(
|
||||
[...getScopes(fn)],
|
||||
(scope) => scope.range,
|
||||
scope => scope.range,
|
||||
{
|
||||
fallthroughs: new Map(),
|
||||
rewrites: queuedRewrites,
|
||||
env: fn.env,
|
||||
},
|
||||
pushStartScopeTerminal,
|
||||
pushEndScopeTerminal
|
||||
pushEndScopeTerminal,
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
@ -166,14 +166,14 @@ export function buildReactiveScopeTerminalsHIR(fn: HIRFunction): void {
|
|||
|
||||
type TerminalRewriteInfo =
|
||||
| {
|
||||
kind: "StartScope";
|
||||
kind: 'StartScope';
|
||||
blockId: BlockId;
|
||||
fallthroughId: BlockId;
|
||||
instrId: InstructionId;
|
||||
scope: ReactiveScope;
|
||||
}
|
||||
| {
|
||||
kind: "EndScope";
|
||||
kind: 'EndScope';
|
||||
instrId: InstructionId;
|
||||
fallthroughId: BlockId;
|
||||
};
|
||||
|
|
@ -190,12 +190,12 @@ type ScopeTraversalContext = {
|
|||
|
||||
function pushStartScopeTerminal(
|
||||
scope: ReactiveScope,
|
||||
context: ScopeTraversalContext
|
||||
context: ScopeTraversalContext,
|
||||
): void {
|
||||
const blockId = context.env.nextBlockId;
|
||||
const fallthroughId = context.env.nextBlockId;
|
||||
context.rewrites.push({
|
||||
kind: "StartScope",
|
||||
kind: 'StartScope',
|
||||
blockId,
|
||||
fallthroughId,
|
||||
instrId: scope.range.start,
|
||||
|
|
@ -206,15 +206,15 @@ function pushStartScopeTerminal(
|
|||
|
||||
function pushEndScopeTerminal(
|
||||
scope: ReactiveScope,
|
||||
context: ScopeTraversalContext
|
||||
context: ScopeTraversalContext,
|
||||
): void {
|
||||
const fallthroughId = context.fallthroughs.get(scope.id);
|
||||
CompilerError.invariant(fallthroughId != null, {
|
||||
reason: "Expected scope to exist",
|
||||
reason: 'Expected scope to exist',
|
||||
loc: GeneratedSource,
|
||||
});
|
||||
context.rewrites.push({
|
||||
kind: "EndScope",
|
||||
kind: 'EndScope',
|
||||
fallthroughId,
|
||||
instrId: scope.range.end,
|
||||
});
|
||||
|
|
@ -248,13 +248,13 @@ type RewriteContext = {
|
|||
function handleRewrite(
|
||||
terminalInfo: TerminalRewriteInfo,
|
||||
idx: number,
|
||||
context: RewriteContext
|
||||
context: RewriteContext,
|
||||
): void {
|
||||
// TODO make consistent instruction IDs instead of reusing
|
||||
const terminal: ReactiveScopeTerminal | GotoTerminal =
|
||||
terminalInfo.kind === "StartScope"
|
||||
terminalInfo.kind === 'StartScope'
|
||||
? {
|
||||
kind: "scope",
|
||||
kind: 'scope',
|
||||
fallthrough: terminalInfo.fallthroughId,
|
||||
block: terminalInfo.blockId,
|
||||
scope: terminalInfo.scope,
|
||||
|
|
@ -262,7 +262,7 @@ function handleRewrite(
|
|||
loc: GeneratedSource,
|
||||
}
|
||||
: {
|
||||
kind: "goto",
|
||||
kind: 'goto',
|
||||
variant: GotoVariant.Break,
|
||||
block: terminalInfo.fallthroughId,
|
||||
id: terminalInfo.instrId,
|
||||
|
|
@ -281,7 +281,7 @@ function handleRewrite(
|
|||
});
|
||||
context.nextPreds = new Set([currBlockId]);
|
||||
context.nextBlockId =
|
||||
terminalInfo.kind === "StartScope"
|
||||
terminalInfo.kind === 'StartScope'
|
||||
? terminalInfo.blockId
|
||||
: terminalInfo.fallthroughId;
|
||||
context.instrSliceIdx = idx;
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { BlockId, HIRFunction, computePostDominatorTree } from ".";
|
||||
import { CompilerError } from "..";
|
||||
import {BlockId, HIRFunction, computePostDominatorTree} from '.';
|
||||
import {CompilerError} from '..';
|
||||
|
||||
export function computeUnconditionalBlocks(fn: HIRFunction): Set<BlockId> {
|
||||
// Construct the set of blocks that is always reachable from the entry block.
|
||||
|
|
@ -23,7 +23,7 @@ export function computeUnconditionalBlocks(fn: HIRFunction): Set<BlockId> {
|
|||
while (current !== null && current !== exit) {
|
||||
CompilerError.invariant(!unconditionalBlocks.has(current), {
|
||||
reason:
|
||||
"Internal error: non-terminating loop in ComputeUnconditionalBlocks",
|
||||
'Internal error: non-terminating loop in ComputeUnconditionalBlocks',
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import prettyFormat from "pretty-format";
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import { BlockId, HIRFunction } from "./HIR";
|
||||
import { eachTerminalSuccessor } from "./visitors";
|
||||
import prettyFormat from 'pretty-format';
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {BlockId, HIRFunction} from './HIR';
|
||||
import {eachTerminalSuccessor} from './visitors';
|
||||
|
||||
/*
|
||||
* Computes the dominator tree of the given function. The returned `Dominator` stores the immediate
|
||||
|
|
@ -34,7 +34,7 @@ export function computeDominatorTree(fn: HIRFunction): Dominator<BlockId> {
|
|||
*/
|
||||
export function computePostDominatorTree(
|
||||
fn: HIRFunction,
|
||||
options: { includeThrowsAsExitNode: boolean }
|
||||
options: {includeThrowsAsExitNode: boolean},
|
||||
): PostDominator<BlockId> {
|
||||
const graph = buildReverseGraph(fn, options.includeThrowsAsExitNode);
|
||||
const nodes = computeImmediateDominators(graph);
|
||||
|
|
@ -87,7 +87,7 @@ export class Dominator<T> {
|
|||
get(id: T): T | null {
|
||||
const dominator = this.#nodes.get(id);
|
||||
CompilerError.invariant(dominator !== undefined, {
|
||||
reason: "Unknown node",
|
||||
reason: 'Unknown node',
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
|
|
@ -128,7 +128,7 @@ export class PostDominator<T> {
|
|||
get(id: T): T | null {
|
||||
const dominator = this.#nodes.get(id);
|
||||
CompilerError.invariant(dominator !== undefined, {
|
||||
reason: "Unknown node",
|
||||
reason: 'Unknown node',
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
|
|
@ -217,7 +217,7 @@ function intersect<T>(a: T, b: T, graph: Graph<T>, nodes: Map<T, T>): T {
|
|||
|
||||
// Turns the HIRFunction into a simplified internal form that is shared for dominator/post-dominator computation
|
||||
function buildGraph(fn: HIRFunction): Graph<BlockId> {
|
||||
const graph: Graph<BlockId> = { entry: fn.body.entry, nodes: new Map() };
|
||||
const graph: Graph<BlockId> = {entry: fn.body.entry, nodes: new Map()};
|
||||
let index = 0;
|
||||
for (const [id, block] of fn.body.blocks) {
|
||||
graph.nodes.set(id, {
|
||||
|
|
@ -237,7 +237,7 @@ function buildGraph(fn: HIRFunction): Graph<BlockId> {
|
|||
*/
|
||||
function buildReverseGraph(
|
||||
fn: HIRFunction,
|
||||
includeThrowsAsExitNode: boolean
|
||||
includeThrowsAsExitNode: boolean,
|
||||
): Graph<BlockId> {
|
||||
const nodes: Map<BlockId, Node<BlockId>> = new Map();
|
||||
const exitId = fn.env.nextBlockId;
|
||||
|
|
@ -256,10 +256,10 @@ function buildReverseGraph(
|
|||
preds: new Set(eachTerminalSuccessor(block.terminal)),
|
||||
succs: new Set(block.preds),
|
||||
};
|
||||
if (block.terminal.kind === "return") {
|
||||
if (block.terminal.kind === 'return') {
|
||||
node.preds.add(exitId);
|
||||
exit.succs.add(id);
|
||||
} else if (block.terminal.kind === "throw" && includeThrowsAsExitNode) {
|
||||
} else if (block.terminal.kind === 'throw' && includeThrowsAsExitNode) {
|
||||
node.preds.add(exitId);
|
||||
exit.succs.add(id);
|
||||
}
|
||||
|
|
@ -282,7 +282,7 @@ function buildReverseGraph(
|
|||
}
|
||||
visit(exitId);
|
||||
|
||||
const rpo: Graph<BlockId> = { entry: exitId, nodes: new Map() };
|
||||
const rpo: Graph<BlockId> = {entry: exitId, nodes: new Map()};
|
||||
let index = 0;
|
||||
for (const id of postorder.reverse()) {
|
||||
const node = nodes.get(id)!;
|
||||
|
|
|
|||
|
|
@ -5,19 +5,19 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import * as t from "@babel/types";
|
||||
import { ZodError, z } from "zod";
|
||||
import { fromZodError } from "zod-validation-error";
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import { Logger } from "../Entrypoint";
|
||||
import { Err, Ok, Result } from "../Utils/Result";
|
||||
import * as t from '@babel/types';
|
||||
import {ZodError, z} from 'zod';
|
||||
import {fromZodError} from 'zod-validation-error';
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {Logger} from '../Entrypoint';
|
||||
import {Err, Ok, Result} from '../Utils/Result';
|
||||
import {
|
||||
DEFAULT_GLOBALS,
|
||||
DEFAULT_SHAPES,
|
||||
Global,
|
||||
GlobalRegistry,
|
||||
installReAnimatedTypes,
|
||||
} from "./Globals";
|
||||
} from './Globals';
|
||||
import {
|
||||
BlockId,
|
||||
BuiltInType,
|
||||
|
|
@ -35,7 +35,7 @@ import {
|
|||
makeIdentifierId,
|
||||
makeIdentifierName,
|
||||
makeScopeId,
|
||||
} from "./HIR";
|
||||
} from './HIR';
|
||||
import {
|
||||
BuiltInMixedReadonlyId,
|
||||
DefaultMutatingHook,
|
||||
|
|
@ -43,8 +43,8 @@ import {
|
|||
FunctionSignature,
|
||||
ShapeRegistry,
|
||||
addHook,
|
||||
} from "./ObjectShape";
|
||||
import { Scope as BabelScope } from "@babel/traverse";
|
||||
} from './ObjectShape';
|
||||
import {Scope as BabelScope} from '@babel/traverse';
|
||||
|
||||
export const ExternalFunctionSchema = z.object({
|
||||
// Source for the imported module that exports the `importSpecifierName` functions
|
||||
|
|
@ -61,8 +61,8 @@ export const InstrumentationSchema = z
|
|||
globalGating: z.string().nullish(),
|
||||
})
|
||||
.refine(
|
||||
(opts) => opts.gating != null || opts.globalGating != null,
|
||||
"Expected at least one of gating or globalGating"
|
||||
opts => opts.gating != null || opts.globalGating != null,
|
||||
'Expected at least one of gating or globalGating',
|
||||
);
|
||||
|
||||
export type ExternalFunction = z.infer<typeof ExternalFunctionSchema>;
|
||||
|
|
@ -443,34 +443,34 @@ export function parseConfigPragma(pragma: string): EnvironmentConfig {
|
|||
// Get the defaults to programmatically check for boolean properties
|
||||
const defaultConfig = EnvironmentConfigSchema.parse({});
|
||||
|
||||
for (const token of pragma.split(" ")) {
|
||||
if (!token.startsWith("@")) {
|
||||
for (const token of pragma.split(' ')) {
|
||||
if (!token.startsWith('@')) {
|
||||
continue;
|
||||
}
|
||||
const keyVal = token.slice(1);
|
||||
let [key, val]: any = keyVal.split(":");
|
||||
let [key, val]: any = keyVal.split(':');
|
||||
|
||||
if (key === "validateNoCapitalizedCalls") {
|
||||
if (key === 'validateNoCapitalizedCalls') {
|
||||
maybeConfig[key] = [];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
key === "enableChangeDetectionForDebugging" &&
|
||||
(val === undefined || val === "true")
|
||||
key === 'enableChangeDetectionForDebugging' &&
|
||||
(val === undefined || val === 'true')
|
||||
) {
|
||||
maybeConfig[key] = {
|
||||
source: "react-compiler-runtime",
|
||||
importSpecifierName: "$structuralCheck",
|
||||
source: 'react-compiler-runtime',
|
||||
importSpecifierName: '$structuralCheck',
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof defaultConfig[key as keyof EnvironmentConfig] !== "boolean") {
|
||||
if (typeof defaultConfig[key as keyof EnvironmentConfig] !== 'boolean') {
|
||||
// skip parsing non-boolean properties
|
||||
continue;
|
||||
}
|
||||
if (val === undefined || val === "true") {
|
||||
if (val === undefined || val === 'true') {
|
||||
val = true;
|
||||
} else {
|
||||
val = false;
|
||||
|
|
@ -483,7 +483,7 @@ export function parseConfigPragma(pragma: string): EnvironmentConfig {
|
|||
return config.data;
|
||||
}
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Internal error, could not parse config from pragma string",
|
||||
reason: 'Internal error, could not parse config from pragma string',
|
||||
description: `${fromZodError(config.error)}`,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
|
|
@ -492,18 +492,18 @@ export function parseConfigPragma(pragma: string): EnvironmentConfig {
|
|||
|
||||
export type PartialEnvironmentConfig = Partial<EnvironmentConfig>;
|
||||
|
||||
export type ReactFunctionType = "Component" | "Hook" | "Other";
|
||||
export type ReactFunctionType = 'Component' | 'Hook' | 'Other';
|
||||
|
||||
export function printFunctionType(type: ReactFunctionType): string {
|
||||
switch (type) {
|
||||
case "Component": {
|
||||
return "component";
|
||||
case 'Component': {
|
||||
return 'component';
|
||||
}
|
||||
case "Hook": {
|
||||
return "hook";
|
||||
case 'Hook': {
|
||||
return 'hook';
|
||||
}
|
||||
default: {
|
||||
return "function";
|
||||
return 'function';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -537,7 +537,7 @@ export class Environment {
|
|||
logger: Logger | null,
|
||||
filename: string | null,
|
||||
code: string | null,
|
||||
useMemoCacheIdentifier: string
|
||||
useMemoCacheIdentifier: string,
|
||||
) {
|
||||
this.#scope = scope;
|
||||
this.fnType = fnType;
|
||||
|
|
@ -574,13 +574,13 @@ export class Environment {
|
|||
positionalParams: [],
|
||||
restParam: hook.effectKind,
|
||||
returnType: hook.transitiveMixedData
|
||||
? { kind: "Object", shapeId: BuiltInMixedReadonlyId }
|
||||
: { kind: "Poly" },
|
||||
? {kind: 'Object', shapeId: BuiltInMixedReadonlyId}
|
||||
: {kind: 'Poly'},
|
||||
returnValueKind: hook.valueKind,
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "Custom",
|
||||
hookKind: 'Custom',
|
||||
noAlias: hook.noAlias,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -613,14 +613,14 @@ export class Environment {
|
|||
}
|
||||
|
||||
generateGloballyUniqueIdentifierName(
|
||||
name: string | null
|
||||
name: string | null,
|
||||
): ValidatedIdentifier {
|
||||
const identifierNode = this.#scope.generateUidIdentifier(name ?? undefined);
|
||||
return makeIdentifierName(identifierNode.name);
|
||||
}
|
||||
|
||||
outlineFunction(fn: HIRFunction, type: ReactFunctionType | null): void {
|
||||
this.#outlinedFunctions.push({ fn, type });
|
||||
this.#outlinedFunctions.push({fn, type});
|
||||
}
|
||||
|
||||
getOutlinedFunctions(): Array<{
|
||||
|
|
@ -635,7 +635,7 @@ export class Environment {
|
|||
const match = new RegExp(this.config.hookPattern).exec(binding.name);
|
||||
if (
|
||||
match != null &&
|
||||
typeof match[1] === "string" &&
|
||||
typeof match[1] === 'string' &&
|
||||
isHookName(match[1])
|
||||
) {
|
||||
const resolvedName = match[1];
|
||||
|
|
@ -644,17 +644,17 @@ export class Environment {
|
|||
}
|
||||
|
||||
switch (binding.kind) {
|
||||
case "ModuleLocal": {
|
||||
case 'ModuleLocal': {
|
||||
// don't resolve module locals
|
||||
return isHookName(binding.name) ? this.#getCustomHookType() : null;
|
||||
}
|
||||
case "Global": {
|
||||
case 'Global': {
|
||||
return (
|
||||
this.#globals.get(binding.name) ??
|
||||
(isHookName(binding.name) ? this.#getCustomHookType() : null)
|
||||
);
|
||||
}
|
||||
case "ImportSpecifier": {
|
||||
case 'ImportSpecifier': {
|
||||
if (this.#isKnownReactModule(binding.module)) {
|
||||
/**
|
||||
* For `import {imported as name} from "..."` form, we use the `imported`
|
||||
|
|
@ -681,8 +681,8 @@ export class Environment {
|
|||
: null;
|
||||
}
|
||||
}
|
||||
case "ImportDefault":
|
||||
case "ImportNamespace": {
|
||||
case 'ImportDefault':
|
||||
case 'ImportNamespace': {
|
||||
if (this.#isKnownReactModule(binding.module)) {
|
||||
// only resolve imports to modules we know about
|
||||
return (
|
||||
|
|
@ -698,19 +698,19 @@ export class Environment {
|
|||
|
||||
#isKnownReactModule(moduleName: string): boolean {
|
||||
return (
|
||||
moduleName.toLowerCase() === "react" ||
|
||||
moduleName.toLowerCase() === "react-dom" ||
|
||||
moduleName.toLowerCase() === 'react' ||
|
||||
moduleName.toLowerCase() === 'react-dom' ||
|
||||
(this.config.enableSharedRuntime__testonly &&
|
||||
moduleName === "shared-runtime")
|
||||
moduleName === 'shared-runtime')
|
||||
);
|
||||
}
|
||||
|
||||
getPropertyType(
|
||||
receiver: Type,
|
||||
property: string
|
||||
property: string,
|
||||
): BuiltInType | PolyType | null {
|
||||
let shapeId = null;
|
||||
if (receiver.kind === "Object" || receiver.kind === "Function") {
|
||||
if (receiver.kind === 'Object' || receiver.kind === 'Function') {
|
||||
shapeId = receiver.shapeId;
|
||||
}
|
||||
if (shapeId !== null) {
|
||||
|
|
@ -726,7 +726,7 @@ export class Environment {
|
|||
suggestions: null,
|
||||
});
|
||||
let value =
|
||||
shape.properties.get(property) ?? shape.properties.get("*") ?? null;
|
||||
shape.properties.get(property) ?? shape.properties.get('*') ?? null;
|
||||
if (value === null && isHookName(property)) {
|
||||
value = this.#getCustomHookType();
|
||||
}
|
||||
|
|
@ -739,7 +739,7 @@ export class Environment {
|
|||
}
|
||||
|
||||
getFunctionSignature(type: FunctionType): FunctionSignature | null {
|
||||
const { shapeId } = type;
|
||||
const {shapeId} = type;
|
||||
if (shapeId !== null) {
|
||||
const shape = this.#shapes.get(shapeId);
|
||||
CompilerError.invariant(shape !== undefined, {
|
||||
|
|
@ -773,7 +773,7 @@ export function isHookName(name: string): boolean {
|
|||
}
|
||||
|
||||
export function parseEnvironmentConfig(
|
||||
partialConfig: PartialEnvironmentConfig
|
||||
partialConfig: PartialEnvironmentConfig,
|
||||
): Result<EnvironmentConfig, ZodError<PartialEnvironmentConfig>> {
|
||||
const config = EnvironmentConfigSchema.safeParse(partialConfig);
|
||||
if (config.success) {
|
||||
|
|
@ -784,7 +784,7 @@ export function parseEnvironmentConfig(
|
|||
}
|
||||
|
||||
export function validateEnvironmentConfig(
|
||||
partialConfig: PartialEnvironmentConfig
|
||||
partialConfig: PartialEnvironmentConfig,
|
||||
): EnvironmentConfig {
|
||||
const config = EnvironmentConfigSchema.safeParse(partialConfig);
|
||||
if (config.success) {
|
||||
|
|
@ -793,7 +793,7 @@ export function validateEnvironmentConfig(
|
|||
|
||||
CompilerError.throwInvalidConfig({
|
||||
reason:
|
||||
"Could not validate environment config. Update React Compiler config to fix the error",
|
||||
'Could not validate environment config. Update React Compiler config to fix the error',
|
||||
description: `${fromZodError(config.error)}`,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
|
|
@ -801,10 +801,10 @@ export function validateEnvironmentConfig(
|
|||
}
|
||||
|
||||
export function tryParseExternalFunction(
|
||||
maybeExternalFunction: any
|
||||
maybeExternalFunction: any,
|
||||
): ExternalFunction {
|
||||
const externalFunction = ExternalFunctionSchema.safeParse(
|
||||
maybeExternalFunction
|
||||
maybeExternalFunction,
|
||||
);
|
||||
if (externalFunction.success) {
|
||||
return externalFunction.data;
|
||||
|
|
@ -812,7 +812,7 @@ export function tryParseExternalFunction(
|
|||
|
||||
CompilerError.throwInvalidConfig({
|
||||
reason:
|
||||
"Could not parse external function. Update React Compiler config to fix the error",
|
||||
'Could not parse external function. Update React Compiler config to fix the error',
|
||||
description: `${fromZodError(externalFunction.error)}`,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type { NodePath } from "@babel/traverse";
|
||||
import type * as t from "@babel/types";
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import { getOrInsertDefault } from "../Utils/utils";
|
||||
import { GeneratedSource } from "./HIR";
|
||||
import type {NodePath} from '@babel/traverse';
|
||||
import type * as t from '@babel/types';
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {getOrInsertDefault} from '../Utils/utils';
|
||||
import {GeneratedSource} from './HIR';
|
||||
|
||||
type IdentifierInfo = {
|
||||
reassigned: boolean;
|
||||
|
|
@ -35,7 +35,7 @@ type FindContextIdentifierState = {
|
|||
const withFunctionScope = {
|
||||
enter: function (
|
||||
path: BabelFunction,
|
||||
state: FindContextIdentifierState
|
||||
state: FindContextIdentifierState,
|
||||
): void {
|
||||
state.currentFn.push(path);
|
||||
},
|
||||
|
|
@ -45,7 +45,7 @@ const withFunctionScope = {
|
|||
};
|
||||
|
||||
export function findContextIdentifiers(
|
||||
func: NodePath<t.Function>
|
||||
func: NodePath<t.Function>,
|
||||
): Set<t.Identifier> {
|
||||
const state: FindContextIdentifierState = {
|
||||
currentFn: [],
|
||||
|
|
@ -60,17 +60,17 @@ export function findContextIdentifiers(
|
|||
ObjectMethod: withFunctionScope,
|
||||
AssignmentExpression(
|
||||
path: NodePath<t.AssignmentExpression>,
|
||||
state: FindContextIdentifierState
|
||||
state: FindContextIdentifierState,
|
||||
): void {
|
||||
const left = path.get("left");
|
||||
const left = path.get('left');
|
||||
const currentFn = state.currentFn.at(-1) ?? null;
|
||||
handleAssignment(currentFn, state.identifiers, left);
|
||||
},
|
||||
UpdateExpression(
|
||||
path: NodePath<t.UpdateExpression>,
|
||||
state: FindContextIdentifierState
|
||||
state: FindContextIdentifierState,
|
||||
): void {
|
||||
const argument = path.get("argument");
|
||||
const argument = path.get('argument');
|
||||
const currentFn = state.currentFn.at(-1) ?? null;
|
||||
if (argument.isLVal()) {
|
||||
handleAssignment(currentFn, state.identifiers, argument);
|
||||
|
|
@ -78,7 +78,7 @@ export function findContextIdentifiers(
|
|||
},
|
||||
Identifier(
|
||||
path: NodePath<t.Identifier>,
|
||||
state: FindContextIdentifierState
|
||||
state: FindContextIdentifierState,
|
||||
): void {
|
||||
const currentFn = state.currentFn.at(-1) ?? null;
|
||||
if (path.isReferencedIdentifier()) {
|
||||
|
|
@ -86,7 +86,7 @@ export function findContextIdentifiers(
|
|||
}
|
||||
},
|
||||
},
|
||||
state
|
||||
state,
|
||||
);
|
||||
|
||||
const result = new Set<t.Identifier>();
|
||||
|
|
@ -103,7 +103,7 @@ export function findContextIdentifiers(
|
|||
function handleIdentifier(
|
||||
currentFn: BabelFunction | null,
|
||||
identifiers: Map<t.Identifier, IdentifierInfo>,
|
||||
path: NodePath<t.Identifier>
|
||||
path: NodePath<t.Identifier>,
|
||||
): void {
|
||||
const name = path.node.name;
|
||||
const binding = path.scope.getBinding(name);
|
||||
|
|
@ -126,7 +126,7 @@ function handleIdentifier(
|
|||
function handleAssignment(
|
||||
currentFn: BabelFunction | null,
|
||||
identifiers: Map<t.Identifier, IdentifierInfo>,
|
||||
lvalPath: NodePath<t.LVal>
|
||||
lvalPath: NodePath<t.LVal>,
|
||||
): void {
|
||||
/*
|
||||
* Find all reassignments to identifiers declared outside of currentFn
|
||||
|
|
@ -134,7 +134,7 @@ function handleAssignment(
|
|||
*/
|
||||
const lvalNode = lvalPath.node;
|
||||
switch (lvalNode.type) {
|
||||
case "Identifier": {
|
||||
case 'Identifier': {
|
||||
const path = lvalPath as NodePath<t.Identifier>;
|
||||
const name = path.node.name;
|
||||
const binding = path.scope.getBinding(name);
|
||||
|
|
@ -155,20 +155,20 @@ function handleAssignment(
|
|||
}
|
||||
break;
|
||||
}
|
||||
case "ArrayPattern": {
|
||||
case 'ArrayPattern': {
|
||||
const path = lvalPath as NodePath<t.ArrayPattern>;
|
||||
for (const element of path.get("elements")) {
|
||||
for (const element of path.get('elements')) {
|
||||
if (nonNull(element)) {
|
||||
handleAssignment(currentFn, identifiers, element);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "ObjectPattern": {
|
||||
case 'ObjectPattern': {
|
||||
const path = lvalPath as NodePath<t.ObjectPattern>;
|
||||
for (const property of path.get("properties")) {
|
||||
for (const property of path.get('properties')) {
|
||||
if (property.isObjectProperty()) {
|
||||
const valuePath = property.get("value");
|
||||
const valuePath = property.get('value');
|
||||
CompilerError.invariant(valuePath.isLVal(), {
|
||||
reason: `[FindContextIdentifiers] Expected object property value to be an LVal, got: ${valuePath.type}`,
|
||||
description: null,
|
||||
|
|
@ -188,18 +188,18 @@ function handleAssignment(
|
|||
}
|
||||
break;
|
||||
}
|
||||
case "AssignmentPattern": {
|
||||
case 'AssignmentPattern': {
|
||||
const path = lvalPath as NodePath<t.AssignmentPattern>;
|
||||
const left = path.get("left");
|
||||
const left = path.get('left');
|
||||
handleAssignment(currentFn, identifiers, left);
|
||||
break;
|
||||
}
|
||||
case "RestElement": {
|
||||
case 'RestElement': {
|
||||
const path = lvalPath as NodePath<t.RestElement>;
|
||||
handleAssignment(currentFn, identifiers, path.get("argument"));
|
||||
handleAssignment(currentFn, identifiers, path.get('argument'));
|
||||
break;
|
||||
}
|
||||
case "MemberExpression": {
|
||||
case 'MemberExpression': {
|
||||
// Interior mutability (not a reassign)
|
||||
break;
|
||||
}
|
||||
|
|
@ -215,7 +215,7 @@ function handleAssignment(
|
|||
}
|
||||
|
||||
function nonNull<T extends NonNullable<t.Node>>(
|
||||
t: NodePath<T | null>
|
||||
t: NodePath<T | null>,
|
||||
): t is NodePath<T> {
|
||||
return t.node != null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { Effect, ValueKind, ValueReason } from "./HIR";
|
||||
import {Effect, ValueKind, ValueReason} from './HIR';
|
||||
import {
|
||||
BUILTIN_SHAPES,
|
||||
BuiltInArrayId,
|
||||
|
|
@ -21,8 +21,8 @@ import {
|
|||
addFunction,
|
||||
addHook,
|
||||
addObject,
|
||||
} from "./ObjectShape";
|
||||
import { BuiltInType, PolyType } from "./Types";
|
||||
} from './ObjectShape';
|
||||
import {BuiltInType, PolyType} from './Types';
|
||||
|
||||
/*
|
||||
* This file exports types and defaults for JavaScript global objects.
|
||||
|
|
@ -37,59 +37,59 @@ export const DEFAULT_SHAPES: ShapeRegistry = new Map(BUILTIN_SHAPES);
|
|||
|
||||
// Hack until we add ObjectShapes for all globals
|
||||
const UNTYPED_GLOBALS: Set<string> = new Set([
|
||||
"String",
|
||||
"Object",
|
||||
"Function",
|
||||
"Number",
|
||||
"RegExp",
|
||||
"Date",
|
||||
"Error",
|
||||
"Function",
|
||||
"TypeError",
|
||||
"RangeError",
|
||||
"ReferenceError",
|
||||
"SyntaxError",
|
||||
"URIError",
|
||||
"EvalError",
|
||||
"Boolean",
|
||||
"DataView",
|
||||
"Float32Array",
|
||||
"Float64Array",
|
||||
"Int8Array",
|
||||
"Int16Array",
|
||||
"Int32Array",
|
||||
"Map",
|
||||
"Set",
|
||||
"WeakMap",
|
||||
"Uint8Array",
|
||||
"Uint8ClampedArray",
|
||||
"Uint16Array",
|
||||
"Uint32Array",
|
||||
"ArrayBuffer",
|
||||
"JSON",
|
||||
"parseFloat",
|
||||
"parseInt",
|
||||
"console",
|
||||
"isNaN",
|
||||
"eval",
|
||||
"isFinite",
|
||||
"encodeURI",
|
||||
"decodeURI",
|
||||
"encodeURIComponent",
|
||||
"decodeURIComponent",
|
||||
'String',
|
||||
'Object',
|
||||
'Function',
|
||||
'Number',
|
||||
'RegExp',
|
||||
'Date',
|
||||
'Error',
|
||||
'Function',
|
||||
'TypeError',
|
||||
'RangeError',
|
||||
'ReferenceError',
|
||||
'SyntaxError',
|
||||
'URIError',
|
||||
'EvalError',
|
||||
'Boolean',
|
||||
'DataView',
|
||||
'Float32Array',
|
||||
'Float64Array',
|
||||
'Int8Array',
|
||||
'Int16Array',
|
||||
'Int32Array',
|
||||
'Map',
|
||||
'Set',
|
||||
'WeakMap',
|
||||
'Uint8Array',
|
||||
'Uint8ClampedArray',
|
||||
'Uint16Array',
|
||||
'Uint32Array',
|
||||
'ArrayBuffer',
|
||||
'JSON',
|
||||
'parseFloat',
|
||||
'parseInt',
|
||||
'console',
|
||||
'isNaN',
|
||||
'eval',
|
||||
'isFinite',
|
||||
'encodeURI',
|
||||
'decodeURI',
|
||||
'encodeURIComponent',
|
||||
'decodeURIComponent',
|
||||
]);
|
||||
|
||||
const TYPED_GLOBALS: Array<[string, BuiltInType]> = [
|
||||
[
|
||||
"Array",
|
||||
addObject(DEFAULT_SHAPES, "Array", [
|
||||
'Array',
|
||||
addObject(DEFAULT_SHAPES, 'Array', [
|
||||
[
|
||||
"isArray",
|
||||
'isArray',
|
||||
// Array.isArray(value)
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [Effect.Read],
|
||||
restParam: null,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
|
|
@ -106,12 +106,12 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [
|
|||
* function)
|
||||
*/
|
||||
[
|
||||
"of",
|
||||
'of',
|
||||
// Array.of(element0, ..., elementN)
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Object", shapeId: BuiltInArrayId },
|
||||
returnType: {kind: 'Object', shapeId: BuiltInArrayId},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
}),
|
||||
|
|
@ -119,85 +119,85 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [
|
|||
]),
|
||||
],
|
||||
[
|
||||
"Math",
|
||||
addObject(DEFAULT_SHAPES, "Math", [
|
||||
'Math',
|
||||
addObject(DEFAULT_SHAPES, 'Math', [
|
||||
// Static properties (TODO)
|
||||
["PI", { kind: "Primitive" }],
|
||||
['PI', {kind: 'Primitive'}],
|
||||
// Static methods (TODO)
|
||||
[
|
||||
"max",
|
||||
'max',
|
||||
// Math.max(value0, ..., valueN)
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
],
|
||||
]),
|
||||
],
|
||||
["Infinity", { kind: "Primitive" }],
|
||||
["NaN", { kind: "Primitive" }],
|
||||
['Infinity', {kind: 'Primitive'}],
|
||||
['NaN', {kind: 'Primitive'}],
|
||||
[
|
||||
"console",
|
||||
addObject(DEFAULT_SHAPES, "console", [
|
||||
'console',
|
||||
addObject(DEFAULT_SHAPES, 'console', [
|
||||
[
|
||||
"error",
|
||||
'error',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"info",
|
||||
'info',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"log",
|
||||
'log',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"table",
|
||||
'table',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"trace",
|
||||
'trace',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"warn",
|
||||
'warn',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
|
|
@ -205,31 +205,31 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [
|
|||
]),
|
||||
],
|
||||
[
|
||||
"Boolean",
|
||||
'Boolean',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"Number",
|
||||
'Number',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"String",
|
||||
'String',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
|
|
@ -244,179 +244,179 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [
|
|||
*/
|
||||
const REACT_APIS: Array<[string, BuiltInType]> = [
|
||||
[
|
||||
"useContext",
|
||||
'useContext',
|
||||
addHook(DEFAULT_SHAPES, {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "useContext",
|
||||
hookKind: 'useContext',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
returnValueReason: ValueReason.Context,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"useState",
|
||||
'useState',
|
||||
addHook(DEFAULT_SHAPES, {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Object", shapeId: BuiltInUseStateId },
|
||||
returnType: {kind: 'Object', shapeId: BuiltInUseStateId},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "useState",
|
||||
hookKind: 'useState',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
returnValueReason: ValueReason.State,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"useActionState",
|
||||
'useActionState',
|
||||
addHook(DEFAULT_SHAPES, {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Object", shapeId: BuiltInUseActionStateId },
|
||||
returnType: {kind: 'Object', shapeId: BuiltInUseActionStateId},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "useActionState",
|
||||
hookKind: 'useActionState',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
returnValueReason: ValueReason.State,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"useReducer",
|
||||
'useReducer',
|
||||
addHook(DEFAULT_SHAPES, {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Object", shapeId: BuiltInUseReducerId },
|
||||
returnType: {kind: 'Object', shapeId: BuiltInUseReducerId},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "useReducer",
|
||||
hookKind: 'useReducer',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
returnValueReason: ValueReason.ReducerState,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"useRef",
|
||||
'useRef',
|
||||
addHook(DEFAULT_SHAPES, {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Capture,
|
||||
returnType: { kind: "Object", shapeId: BuiltInUseRefId },
|
||||
returnType: {kind: 'Object', shapeId: BuiltInUseRefId},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "useRef",
|
||||
hookKind: 'useRef',
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"useMemo",
|
||||
'useMemo',
|
||||
addHook(DEFAULT_SHAPES, {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "useMemo",
|
||||
hookKind: 'useMemo',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"useCallback",
|
||||
'useCallback',
|
||||
addHook(DEFAULT_SHAPES, {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "useCallback",
|
||||
hookKind: 'useCallback',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"useEffect",
|
||||
'useEffect',
|
||||
addHook(
|
||||
DEFAULT_SHAPES,
|
||||
{
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "useEffect",
|
||||
hookKind: 'useEffect',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
},
|
||||
BuiltInUseEffectHookId
|
||||
BuiltInUseEffectHookId,
|
||||
),
|
||||
],
|
||||
[
|
||||
"useLayoutEffect",
|
||||
'useLayoutEffect',
|
||||
addHook(
|
||||
DEFAULT_SHAPES,
|
||||
{
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "useLayoutEffect",
|
||||
hookKind: 'useLayoutEffect',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
},
|
||||
BuiltInUseLayoutEffectHookId
|
||||
BuiltInUseLayoutEffectHookId,
|
||||
),
|
||||
],
|
||||
[
|
||||
"useInsertionEffect",
|
||||
'useInsertionEffect',
|
||||
addHook(
|
||||
DEFAULT_SHAPES,
|
||||
{
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "useInsertionEffect",
|
||||
hookKind: 'useInsertionEffect',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
},
|
||||
BuiltInUseInsertionEffectHookId
|
||||
BuiltInUseInsertionEffectHookId,
|
||||
),
|
||||
],
|
||||
[
|
||||
"use",
|
||||
'use',
|
||||
addFunction(
|
||||
DEFAULT_SHAPES,
|
||||
[],
|
||||
{
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
},
|
||||
BuiltInUseOperatorId
|
||||
BuiltInUseOperatorId,
|
||||
),
|
||||
],
|
||||
];
|
||||
|
||||
TYPED_GLOBALS.push(
|
||||
[
|
||||
"React",
|
||||
'React',
|
||||
addObject(DEFAULT_SHAPES, null, [
|
||||
...REACT_APIS,
|
||||
[
|
||||
"createElement",
|
||||
'createElement',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"cloneElement",
|
||||
'cloneElement',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"createRef",
|
||||
'createRef',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Capture, // createRef takes no paramters
|
||||
returnType: { kind: "Object", shapeId: BuiltInUseRefId },
|
||||
returnType: {kind: 'Object', shapeId: BuiltInUseRefId},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
}),
|
||||
|
|
@ -424,15 +424,15 @@ TYPED_GLOBALS.push(
|
|||
]),
|
||||
],
|
||||
[
|
||||
"_jsx",
|
||||
'_jsx',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
}),
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
export type Global = BuiltInType | PolyType;
|
||||
|
|
@ -442,7 +442,7 @@ export const DEFAULT_GLOBALS: GlobalRegistry = new Map(REACT_APIS);
|
|||
// Hack until we add ObjectShapes for all globals
|
||||
for (const name of UNTYPED_GLOBALS) {
|
||||
DEFAULT_GLOBALS.set(name, {
|
||||
kind: "Poly",
|
||||
kind: 'Poly',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -452,22 +452,22 @@ for (const [name, type_] of TYPED_GLOBALS) {
|
|||
|
||||
// Recursive global type
|
||||
DEFAULT_GLOBALS.set(
|
||||
"globalThis",
|
||||
addObject(DEFAULT_SHAPES, "globalThis", TYPED_GLOBALS)
|
||||
'globalThis',
|
||||
addObject(DEFAULT_SHAPES, 'globalThis', TYPED_GLOBALS),
|
||||
);
|
||||
|
||||
export function installReAnimatedTypes(
|
||||
globals: GlobalRegistry,
|
||||
registry: ShapeRegistry
|
||||
registry: ShapeRegistry,
|
||||
): void {
|
||||
// hooks that freeze args and return frozen value
|
||||
const frozenHooks = [
|
||||
"useFrameCallback",
|
||||
"useAnimatedStyle",
|
||||
"useAnimatedProps",
|
||||
"useAnimatedScrollHandler",
|
||||
"useAnimatedReaction",
|
||||
"useWorkletCallback",
|
||||
'useFrameCallback',
|
||||
'useAnimatedStyle',
|
||||
'useAnimatedProps',
|
||||
'useAnimatedScrollHandler',
|
||||
'useAnimatedReaction',
|
||||
'useWorkletCallback',
|
||||
];
|
||||
for (const hook of frozenHooks) {
|
||||
globals.set(
|
||||
|
|
@ -475,12 +475,12 @@ export function installReAnimatedTypes(
|
|||
addHook(registry, {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
noAlias: true,
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "Custom",
|
||||
})
|
||||
hookKind: 'Custom',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -488,31 +488,31 @@ export function installReAnimatedTypes(
|
|||
* hooks that return a mutable value. ideally these should be modelled as a
|
||||
* ref, but this works for now.
|
||||
*/
|
||||
const mutableHooks = ["useSharedValue", "useDerivedValue"];
|
||||
const mutableHooks = ['useSharedValue', 'useDerivedValue'];
|
||||
for (const hook of mutableHooks) {
|
||||
globals.set(
|
||||
hook,
|
||||
addHook(registry, {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
noAlias: true,
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "Custom",
|
||||
})
|
||||
hookKind: 'Custom',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// functions that return mutable value
|
||||
const funcs = [
|
||||
"withTiming",
|
||||
"withSpring",
|
||||
"createAnimatedPropAdapter",
|
||||
"withDecay",
|
||||
"withRepeat",
|
||||
"runOnUI",
|
||||
"executeOnUIRuntimeSync",
|
||||
'withTiming',
|
||||
'withSpring',
|
||||
'createAnimatedPropAdapter',
|
||||
'withDecay',
|
||||
'withRepeat',
|
||||
'runOnUI',
|
||||
'executeOnUIRuntimeSync',
|
||||
];
|
||||
for (const fn of funcs) {
|
||||
globals.set(
|
||||
|
|
@ -520,11 +520,11 @@ export function installReAnimatedTypes(
|
|||
addFunction(registry, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
noAlias: true,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -5,10 +5,10 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { Binding, NodePath } from "@babel/traverse";
|
||||
import * as t from "@babel/types";
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import { Environment } from "./Environment";
|
||||
import {Binding, NodePath} from '@babel/traverse';
|
||||
import * as t from '@babel/types';
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {Environment} from './Environment';
|
||||
import {
|
||||
BasicBlock,
|
||||
BlockId,
|
||||
|
|
@ -28,13 +28,13 @@ import {
|
|||
makeIdentifierName,
|
||||
makeInstructionId,
|
||||
makeType,
|
||||
} from "./HIR";
|
||||
import { printInstruction } from "./PrintHIR";
|
||||
} from './HIR';
|
||||
import {printInstruction} from './PrintHIR';
|
||||
import {
|
||||
eachTerminalSuccessor,
|
||||
mapTerminalSuccessors,
|
||||
terminalFallthrough,
|
||||
} from "./visitors";
|
||||
} from './visitors';
|
||||
|
||||
/*
|
||||
* *******************************************************************************************
|
||||
|
|
@ -54,31 +54,31 @@ export type WipBlock = {
|
|||
type Scope = LoopScope | LabelScope | SwitchScope;
|
||||
|
||||
type LoopScope = {
|
||||
kind: "loop";
|
||||
kind: 'loop';
|
||||
label: string | null;
|
||||
continueBlock: BlockId;
|
||||
breakBlock: BlockId;
|
||||
};
|
||||
|
||||
type SwitchScope = {
|
||||
kind: "switch";
|
||||
kind: 'switch';
|
||||
breakBlock: BlockId;
|
||||
label: string | null;
|
||||
};
|
||||
|
||||
type LabelScope = {
|
||||
kind: "label";
|
||||
kind: 'label';
|
||||
label: string;
|
||||
breakBlock: BlockId;
|
||||
};
|
||||
|
||||
function newBlock(id: BlockId, kind: BlockKind): WipBlock {
|
||||
return { id, kind, instructions: [] };
|
||||
return {id, kind, instructions: []};
|
||||
}
|
||||
|
||||
export type Bindings = Map<
|
||||
string,
|
||||
{ node: t.Identifier; identifier: Identifier }
|
||||
{node: t.Identifier; identifier: Identifier}
|
||||
>;
|
||||
|
||||
/*
|
||||
|
|
@ -90,13 +90,13 @@ export type ExceptionsMode =
|
|||
* Mode used for code not covered by explicit exception handling, any
|
||||
* errors are assumed to be thrown out of the function
|
||||
*/
|
||||
| { kind: "ThrowExceptions" }
|
||||
| {kind: 'ThrowExceptions'}
|
||||
/*
|
||||
* Mode used for code that *is* covered by explicit exception handling
|
||||
* (ie try/catch), which requires modeling the possibility of control
|
||||
* flow to the exception handler.
|
||||
*/
|
||||
| { kind: "CatchExceptions"; handler: BlockId };
|
||||
| {kind: 'CatchExceptions'; handler: BlockId};
|
||||
|
||||
// Helper class for constructing a CFG
|
||||
export default class HIRBuilder {
|
||||
|
|
@ -131,14 +131,14 @@ export default class HIRBuilder {
|
|||
env: Environment,
|
||||
parentFunction: NodePath<t.Function>, // the outermost function being compiled
|
||||
bindings: Bindings | null = null,
|
||||
context: Array<t.Identifier> | null = null
|
||||
context: Array<t.Identifier> | null = null,
|
||||
) {
|
||||
this.#env = env;
|
||||
this.#bindings = bindings ?? new Map();
|
||||
this.parentFunction = parentFunction;
|
||||
this.#context = context ?? [];
|
||||
this.#entry = makeBlockId(env.nextBlockId);
|
||||
this.#current = newBlock(this.#entry, "block");
|
||||
this.#current = newBlock(this.#entry, 'block');
|
||||
}
|
||||
|
||||
currentBlockKind(): BlockKind {
|
||||
|
|
@ -153,13 +153,13 @@ export default class HIRBuilder {
|
|||
const continuationBlock = this.reserve(this.currentBlockKind());
|
||||
this.terminateWithContinuation(
|
||||
{
|
||||
kind: "maybe-throw",
|
||||
kind: 'maybe-throw',
|
||||
continuation: continuationBlock.id,
|
||||
handler: exceptionHandler,
|
||||
id: makeInstructionId(0),
|
||||
loc: instruction.loc,
|
||||
},
|
||||
continuationBlock
|
||||
continuationBlock,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -180,7 +180,7 @@ export default class HIRBuilder {
|
|||
return {
|
||||
id,
|
||||
name: null,
|
||||
mutableRange: { start: makeInstructionId(0), end: makeInstructionId(0) },
|
||||
mutableRange: {start: makeInstructionId(0), end: makeInstructionId(0)},
|
||||
scope: null,
|
||||
type: makeType(),
|
||||
loc,
|
||||
|
|
@ -188,7 +188,7 @@ export default class HIRBuilder {
|
|||
}
|
||||
|
||||
#resolveBabelBinding(
|
||||
path: NodePath<t.Identifier | t.JSXIdentifier>
|
||||
path: NodePath<t.Identifier | t.JSXIdentifier>,
|
||||
): Binding | null {
|
||||
const originalName = path.node.name;
|
||||
const binding = path.scope.getBinding(originalName);
|
||||
|
|
@ -229,12 +229,12 @@ export default class HIRBuilder {
|
|||
* ```
|
||||
*/
|
||||
resolveIdentifier(
|
||||
path: NodePath<t.Identifier | t.JSXIdentifier>
|
||||
path: NodePath<t.Identifier | t.JSXIdentifier>,
|
||||
): VariableBinding {
|
||||
const originalName = path.node.name;
|
||||
const babelBinding = this.#resolveBabelBinding(path);
|
||||
if (babelBinding == null) {
|
||||
return { kind: "Global", name: originalName };
|
||||
return {kind: 'Global', name: originalName};
|
||||
}
|
||||
|
||||
// Check if the binding is from module scope
|
||||
|
|
@ -246,7 +246,7 @@ export default class HIRBuilder {
|
|||
const importDeclaration =
|
||||
path.parentPath as NodePath<t.ImportDeclaration>;
|
||||
return {
|
||||
kind: "ImportDefault",
|
||||
kind: 'ImportDefault',
|
||||
name: originalName,
|
||||
module: importDeclaration.node.source.value,
|
||||
};
|
||||
|
|
@ -254,11 +254,11 @@ export default class HIRBuilder {
|
|||
const importDeclaration =
|
||||
path.parentPath as NodePath<t.ImportDeclaration>;
|
||||
return {
|
||||
kind: "ImportSpecifier",
|
||||
kind: 'ImportSpecifier',
|
||||
name: originalName,
|
||||
module: importDeclaration.node.source.value,
|
||||
imported:
|
||||
path.node.imported.type === "Identifier"
|
||||
path.node.imported.type === 'Identifier'
|
||||
? path.node.imported.name
|
||||
: path.node.imported.value,
|
||||
};
|
||||
|
|
@ -266,13 +266,13 @@ export default class HIRBuilder {
|
|||
const importDeclaration =
|
||||
path.parentPath as NodePath<t.ImportDeclaration>;
|
||||
return {
|
||||
kind: "ImportNamespace",
|
||||
kind: 'ImportNamespace',
|
||||
name: originalName,
|
||||
module: importDeclaration.node.source.value,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
kind: "ModuleLocal",
|
||||
kind: 'ModuleLocal',
|
||||
name: originalName,
|
||||
};
|
||||
}
|
||||
|
|
@ -283,7 +283,7 @@ export default class HIRBuilder {
|
|||
babelBinding.scope.rename(originalName, resolvedBinding.name.value);
|
||||
}
|
||||
return {
|
||||
kind: "Identifier",
|
||||
kind: 'Identifier',
|
||||
identifier: resolvedBinding,
|
||||
bindingKind: babelBinding.kind,
|
||||
};
|
||||
|
|
@ -294,7 +294,7 @@ export default class HIRBuilder {
|
|||
if (binding) {
|
||||
// Check if the binding is from module scope, if so return null
|
||||
const outerBinding = this.parentFunction.scope.parent.getBinding(
|
||||
path.node.name
|
||||
path.node.name,
|
||||
);
|
||||
if (binding === outerBinding) {
|
||||
return false;
|
||||
|
|
@ -324,7 +324,7 @@ export default class HIRBuilder {
|
|||
type: makeType(),
|
||||
loc: node.loc ?? GeneratedSource,
|
||||
};
|
||||
this.#bindings.set(name, { node, identifier });
|
||||
this.#bindings.set(name, {node, identifier});
|
||||
return identifier;
|
||||
} else if (mapping.node === node) {
|
||||
return mapping.identifier;
|
||||
|
|
@ -345,7 +345,7 @@ export default class HIRBuilder {
|
|||
if (
|
||||
!rpoBlocks.has(id) &&
|
||||
block.instructions.some(
|
||||
(instr) => instr.value.kind === "FunctionExpression"
|
||||
instr => instr.value.kind === 'FunctionExpression',
|
||||
)
|
||||
) {
|
||||
CompilerError.throwTodo({
|
||||
|
|
@ -369,7 +369,7 @@ export default class HIRBuilder {
|
|||
|
||||
// Terminate the current block w the given terminal, and start a new block
|
||||
terminate(terminal: Terminal, nextBlockKind: BlockKind | null): void {
|
||||
const { id: blockId, kind, instructions } = this.#current;
|
||||
const {id: blockId, kind, instructions} = this.#current;
|
||||
this.#completed.set(blockId, {
|
||||
kind,
|
||||
id: blockId,
|
||||
|
|
@ -389,7 +389,7 @@ export default class HIRBuilder {
|
|||
* reserved block as the new current block
|
||||
*/
|
||||
terminateWithContinuation(terminal: Terminal, continuation: WipBlock): void {
|
||||
const { id: blockId, kind, instructions } = this.#current;
|
||||
const {id: blockId, kind, instructions} = this.#current;
|
||||
this.#completed.set(blockId, {
|
||||
kind: kind,
|
||||
id: blockId,
|
||||
|
|
@ -412,7 +412,7 @@ export default class HIRBuilder {
|
|||
|
||||
// Save a previously reserved block as completed
|
||||
complete(block: WipBlock, terminal: Terminal): void {
|
||||
const { id: blockId, kind, instructions } = block;
|
||||
const {id: blockId, kind, instructions} = block;
|
||||
this.#completed.set(blockId, {
|
||||
kind,
|
||||
id: blockId,
|
||||
|
|
@ -431,7 +431,7 @@ export default class HIRBuilder {
|
|||
const current = this.#current;
|
||||
this.#current = wip;
|
||||
const terminal = fn();
|
||||
const { id: blockId, kind, instructions } = this.#current;
|
||||
const {id: blockId, kind, instructions} = this.#current;
|
||||
this.#completed.set(blockId, {
|
||||
kind,
|
||||
id: blockId,
|
||||
|
|
@ -459,7 +459,7 @@ export default class HIRBuilder {
|
|||
|
||||
label<T>(label: string, breakBlock: BlockId, fn: () => T): T {
|
||||
this.#scopes.push({
|
||||
kind: "label",
|
||||
kind: 'label',
|
||||
breakBlock,
|
||||
label,
|
||||
});
|
||||
|
|
@ -467,22 +467,22 @@ export default class HIRBuilder {
|
|||
const last = this.#scopes.pop();
|
||||
CompilerError.invariant(
|
||||
last != null &&
|
||||
last.kind === "label" &&
|
||||
last.kind === 'label' &&
|
||||
last.label === label &&
|
||||
last.breakBlock === breakBlock,
|
||||
{
|
||||
reason: "Mismatched label",
|
||||
reason: 'Mismatched label',
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}
|
||||
},
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
switch<T>(label: string | null, breakBlock: BlockId, fn: () => T): T {
|
||||
this.#scopes.push({
|
||||
kind: "switch",
|
||||
kind: 'switch',
|
||||
breakBlock,
|
||||
label,
|
||||
});
|
||||
|
|
@ -490,15 +490,15 @@ export default class HIRBuilder {
|
|||
const last = this.#scopes.pop();
|
||||
CompilerError.invariant(
|
||||
last != null &&
|
||||
last.kind === "switch" &&
|
||||
last.kind === 'switch' &&
|
||||
last.label === label &&
|
||||
last.breakBlock === breakBlock,
|
||||
{
|
||||
reason: "Mismatched label",
|
||||
reason: 'Mismatched label',
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}
|
||||
},
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
|
@ -513,10 +513,10 @@ export default class HIRBuilder {
|
|||
continueBlock: BlockId,
|
||||
// block following the loop. "break" jumps here.
|
||||
breakBlock: BlockId,
|
||||
fn: () => T
|
||||
fn: () => T,
|
||||
): T {
|
||||
this.#scopes.push({
|
||||
kind: "loop",
|
||||
kind: 'loop',
|
||||
label,
|
||||
continueBlock,
|
||||
breakBlock,
|
||||
|
|
@ -525,16 +525,16 @@ export default class HIRBuilder {
|
|||
const last = this.#scopes.pop();
|
||||
CompilerError.invariant(
|
||||
last != null &&
|
||||
last.kind === "loop" &&
|
||||
last.kind === 'loop' &&
|
||||
last.label === label &&
|
||||
last.continueBlock === continueBlock &&
|
||||
last.breakBlock === breakBlock,
|
||||
{
|
||||
reason: "Mismatched loops",
|
||||
reason: 'Mismatched loops',
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}
|
||||
},
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
|
@ -548,14 +548,14 @@ export default class HIRBuilder {
|
|||
const scope = this.#scopes[ii];
|
||||
if (
|
||||
(label === null &&
|
||||
(scope.kind === "loop" || scope.kind === "switch")) ||
|
||||
(scope.kind === 'loop' || scope.kind === 'switch')) ||
|
||||
label === scope.label
|
||||
) {
|
||||
return scope.breakBlock;
|
||||
}
|
||||
}
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Expected a loop or switch to be in scope",
|
||||
reason: 'Expected a loop or switch to be in scope',
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
|
|
@ -570,13 +570,13 @@ export default class HIRBuilder {
|
|||
lookupContinue(label: string | null): BlockId {
|
||||
for (let ii = this.#scopes.length - 1; ii >= 0; ii--) {
|
||||
const scope = this.#scopes[ii];
|
||||
if (scope.kind === "loop") {
|
||||
if (scope.kind === 'loop') {
|
||||
if (label === null || label === scope.label) {
|
||||
return scope.continueBlock;
|
||||
}
|
||||
} else if (label !== null && scope.label === label) {
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Continue may only refer to a labeled loop",
|
||||
reason: 'Continue may only refer to a labeled loop',
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
|
|
@ -584,7 +584,7 @@ export default class HIRBuilder {
|
|||
}
|
||||
}
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Expected a loop to be in scope",
|
||||
reason: 'Expected a loop to be in scope',
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
|
|
@ -633,7 +633,7 @@ function _shrink(func: HIR): void {
|
|||
}
|
||||
reachable.add(blockId);
|
||||
const block = func.blocks.get(blockId)!;
|
||||
block.terminal = mapTerminalSuccessors(block.terminal, (prevTarget) => {
|
||||
block.terminal = mapTerminalSuccessors(block.terminal, prevTarget => {
|
||||
const target = resolveBlockTarget(prevTarget);
|
||||
queue.push(target);
|
||||
return target;
|
||||
|
|
@ -649,7 +649,7 @@ function _shrink(func: HIR): void {
|
|||
export function removeUnreachableForUpdates(fn: HIR): void {
|
||||
for (const [, block] of fn.blocks) {
|
||||
if (
|
||||
block.terminal.kind === "for" &&
|
||||
block.terminal.kind === 'for' &&
|
||||
block.terminal.update !== null &&
|
||||
!fn.blocks.has(block.terminal.update)
|
||||
) {
|
||||
|
|
@ -670,10 +670,10 @@ export function removeDeadDoWhileStatements(func: HIR): void {
|
|||
* MergeConsecutiveBlocks figures out how to merge as appropriate.
|
||||
*/
|
||||
for (const [_, block] of func.blocks) {
|
||||
if (block.terminal.kind === "do-while") {
|
||||
if (block.terminal.kind === 'do-while') {
|
||||
if (!visited.has(block.terminal.test)) {
|
||||
block.terminal = {
|
||||
kind: "goto",
|
||||
kind: 'goto',
|
||||
block: block.terminal.loop,
|
||||
variant: GotoVariant.Break,
|
||||
id: block.terminal.id,
|
||||
|
|
@ -700,7 +700,7 @@ export function reversePostorderBlocks(func: HIR): void {
|
|||
* may be in the output: blocks will be removed in the case of unreachable code in
|
||||
* the input.
|
||||
*/
|
||||
function getReversePostorderedBlocks(func: HIR): HIR["blocks"] {
|
||||
function getReversePostorderedBlocks(func: HIR): HIR['blocks'] {
|
||||
const visited: Set<BlockId> = new Set();
|
||||
const used: Set<BlockId> = new Set();
|
||||
const usedFallthroughs: Set<BlockId> = new Set();
|
||||
|
|
@ -772,7 +772,7 @@ function getReversePostorderedBlocks(func: HIR): HIR["blocks"] {
|
|||
...block,
|
||||
instructions: [],
|
||||
terminal: {
|
||||
kind: "unreachable",
|
||||
kind: 'unreachable',
|
||||
id: block.terminal.id,
|
||||
loc: block.terminal.loc,
|
||||
},
|
||||
|
|
@ -813,7 +813,7 @@ export function markPredecessors(func: HIR): void {
|
|||
return;
|
||||
}
|
||||
CompilerError.invariant(block != null, {
|
||||
reason: "unexpected missing block",
|
||||
reason: 'unexpected missing block',
|
||||
description: `block ${blockId}`,
|
||||
loc: GeneratedSource,
|
||||
});
|
||||
|
|
@ -826,7 +826,7 @@ export function markPredecessors(func: HIR): void {
|
|||
}
|
||||
visited.add(blockId);
|
||||
|
||||
const { terminal } = block;
|
||||
const {terminal} = block;
|
||||
|
||||
for (const successor of eachTerminalSuccessor(terminal)) {
|
||||
visit(successor, block);
|
||||
|
|
@ -841,7 +841,7 @@ export function markPredecessors(func: HIR): void {
|
|||
*/
|
||||
function getTargetIfIndirection(block: BasicBlock): number | null {
|
||||
return block.instructions.length === 0 &&
|
||||
block.terminal.kind === "goto" &&
|
||||
block.terminal.kind === 'goto' &&
|
||||
block.terminal.variant === GotoVariant.Break
|
||||
? block.terminal.block
|
||||
: null;
|
||||
|
|
@ -854,14 +854,14 @@ function getTargetIfIndirection(block: BasicBlock): number | null {
|
|||
export function removeUnnecessaryTryCatch(fn: HIR): void {
|
||||
for (const [, block] of fn.blocks) {
|
||||
if (
|
||||
block.terminal.kind === "try" &&
|
||||
block.terminal.kind === 'try' &&
|
||||
!fn.blocks.has(block.terminal.handler)
|
||||
) {
|
||||
const handlerId = block.terminal.handler;
|
||||
const fallthroughId = block.terminal.fallthrough;
|
||||
const fallthrough = fn.blocks.get(fallthroughId);
|
||||
block.terminal = {
|
||||
kind: "goto",
|
||||
kind: 'goto',
|
||||
block: block.terminal.block,
|
||||
id: makeInstructionId(0),
|
||||
loc: block.terminal.loc,
|
||||
|
|
@ -882,13 +882,13 @@ export function removeUnnecessaryTryCatch(fn: HIR): void {
|
|||
|
||||
export function createTemporaryPlace(
|
||||
env: Environment,
|
||||
loc: SourceLocation
|
||||
loc: SourceLocation,
|
||||
): Place {
|
||||
return {
|
||||
kind: "Identifier",
|
||||
kind: 'Identifier',
|
||||
identifier: {
|
||||
id: env.nextIdentifierId,
|
||||
mutableRange: { start: makeInstructionId(0), end: makeInstructionId(0) },
|
||||
mutableRange: {start: makeInstructionId(0), end: makeInstructionId(0)},
|
||||
name: null,
|
||||
scope: null,
|
||||
type: makeType(),
|
||||
|
|
|
|||
|
|
@ -5,16 +5,16 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {
|
||||
BlockId,
|
||||
Effect,
|
||||
GeneratedSource,
|
||||
HIRFunction,
|
||||
Instruction,
|
||||
} from "./HIR";
|
||||
import { markPredecessors } from "./HIRBuilder";
|
||||
import { terminalFallthrough, terminalHasFallthrough } from "./visitors";
|
||||
} from './HIR';
|
||||
import {markPredecessors} from './HIRBuilder';
|
||||
import {terminalFallthrough, terminalHasFallthrough} from './visitors';
|
||||
|
||||
/*
|
||||
* Merges sequences of blocks that will always execute consecutively —
|
||||
|
|
@ -39,8 +39,8 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
|
|||
|
||||
for (const instr of block.instructions) {
|
||||
if (
|
||||
instr.value.kind === "FunctionExpression" ||
|
||||
instr.value.kind === "ObjectMethod"
|
||||
instr.value.kind === 'FunctionExpression' ||
|
||||
instr.value.kind === 'ObjectMethod'
|
||||
) {
|
||||
mergeConsecutiveBlocks(instr.value.loweredFunc.func);
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
|
|||
// Can only merge blocks with a single predecessor
|
||||
block.preds.size !== 1 ||
|
||||
// Value blocks cannot merge
|
||||
block.kind !== "block" ||
|
||||
block.kind !== 'block' ||
|
||||
// Merging across fallthroughs could move the predecessor out of its block scope
|
||||
fallthroughBlocks.has(block.id)
|
||||
) {
|
||||
|
|
@ -65,7 +65,7 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
|
|||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
if (predecessor.terminal.kind !== "goto" || predecessor.kind !== "block") {
|
||||
if (predecessor.terminal.kind !== 'goto' || predecessor.kind !== 'block') {
|
||||
/*
|
||||
* The predecessor is not guaranteed to transfer control to this block,
|
||||
* they aren't consecutive.
|
||||
|
|
@ -85,16 +85,16 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
|
|||
const instr: Instruction = {
|
||||
id: predecessor.terminal.id,
|
||||
lvalue: {
|
||||
kind: "Identifier",
|
||||
kind: 'Identifier',
|
||||
identifier: phi.id,
|
||||
effect: Effect.ConditionallyMutate,
|
||||
reactive: false,
|
||||
loc: GeneratedSource,
|
||||
},
|
||||
value: {
|
||||
kind: "LoadLocal",
|
||||
kind: 'LoadLocal',
|
||||
place: {
|
||||
kind: "Identifier",
|
||||
kind: 'Identifier',
|
||||
identifier: operand,
|
||||
effect: Effect.Read,
|
||||
reactive: false,
|
||||
|
|
@ -113,7 +113,7 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
|
|||
fn.body.blocks.delete(block.id);
|
||||
}
|
||||
markPredecessors(fn.body);
|
||||
for (const [, { terminal }] of fn.body.blocks) {
|
||||
for (const [, {terminal}] of fn.body.blocks) {
|
||||
if (terminalHasFallthrough(terminal)) {
|
||||
terminal.fallthrough = merged.get(terminal.fallthrough);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@ import {
|
|||
Place,
|
||||
ReactiveScope,
|
||||
makeInstructionId,
|
||||
} from ".";
|
||||
import { getPlaceScope } from "../ReactiveScopes/BuildReactiveBlocks";
|
||||
import { isMutable } from "../ReactiveScopes/InferReactiveScopeVariables";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
import { getOrInsertDefault } from "../Utils/utils";
|
||||
} from '.';
|
||||
import {getPlaceScope} from '../ReactiveScopes/BuildReactiveBlocks';
|
||||
import {isMutable} from '../ReactiveScopes/InferReactiveScopeVariables';
|
||||
import DisjointSet from '../Utils/DisjointSet';
|
||||
import {getOrInsertDefault} from '../Utils/utils';
|
||||
import {
|
||||
eachInstructionLValue,
|
||||
eachInstructionOperand,
|
||||
eachTerminalOperand,
|
||||
} from "./visitors";
|
||||
} from './visitors';
|
||||
|
||||
/**
|
||||
* While previous passes ensure that reactive scopes span valid sets of program
|
||||
|
|
@ -113,10 +113,10 @@ export function mergeOverlappingReactiveScopesHIR(fn: HIRFunction): void {
|
|||
joinedScopes.forEach((scope, groupScope) => {
|
||||
if (scope !== groupScope) {
|
||||
groupScope.range.start = makeInstructionId(
|
||||
Math.min(groupScope.range.start, scope.range.start)
|
||||
Math.min(groupScope.range.start, scope.range.start),
|
||||
);
|
||||
groupScope.range.end = makeInstructionId(
|
||||
Math.max(groupScope.range.end, scope.range.end)
|
||||
Math.max(groupScope.range.end, scope.range.end),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -129,8 +129,8 @@ export function mergeOverlappingReactiveScopesHIR(fn: HIRFunction): void {
|
|||
}
|
||||
|
||||
type ScopeInfo = {
|
||||
scopeStarts: Array<{ id: InstructionId; scopes: Set<ReactiveScope> }>;
|
||||
scopeEnds: Array<{ id: InstructionId; scopes: Set<ReactiveScope> }>;
|
||||
scopeStarts: Array<{id: InstructionId; scopes: Set<ReactiveScope>}>;
|
||||
scopeEnds: Array<{id: InstructionId; scopes: Set<ReactiveScope>}>;
|
||||
placeScopes: Map<Place, ReactiveScope>;
|
||||
};
|
||||
|
||||
|
|
@ -150,7 +150,7 @@ function collectScopeInfo(fn: HIRFunction): ScopeInfo {
|
|||
placeScopes.set(place, scope);
|
||||
if (scope.range.start !== scope.range.end) {
|
||||
getOrInsertDefault(scopeStarts, scope.range.start, new Set()).add(
|
||||
scope
|
||||
scope,
|
||||
);
|
||||
getOrInsertDefault(scopeEnds, scope.range.end, new Set()).add(scope);
|
||||
}
|
||||
|
|
@ -173,10 +173,10 @@ function collectScopeInfo(fn: HIRFunction): ScopeInfo {
|
|||
|
||||
return {
|
||||
scopeStarts: [...scopeStarts.entries()]
|
||||
.map(([id, scopes]) => ({ id, scopes }))
|
||||
.map(([id, scopes]) => ({id, scopes}))
|
||||
.sort((a, b) => b.id - a.id),
|
||||
scopeEnds: [...scopeEnds.entries()]
|
||||
.map(([id, scopes]) => ({ id, scopes }))
|
||||
.map(([id, scopes]) => ({id, scopes}))
|
||||
.sort((a, b) => b.id - a.id),
|
||||
placeScopes,
|
||||
};
|
||||
|
|
@ -184,8 +184,8 @@ function collectScopeInfo(fn: HIRFunction): ScopeInfo {
|
|||
|
||||
function visitInstructionId(
|
||||
id: InstructionId,
|
||||
{ scopeEnds, scopeStarts }: ScopeInfo,
|
||||
{ activeScopes, joined }: TraversalState
|
||||
{scopeEnds, scopeStarts}: ScopeInfo,
|
||||
{activeScopes, joined}: TraversalState,
|
||||
): void {
|
||||
/**
|
||||
* Handle all scopes that end at this instruction.
|
||||
|
|
@ -200,7 +200,7 @@ function visitInstructionId(
|
|||
* order of start IDs because the scopes stack is ordered as such
|
||||
*/
|
||||
const scopesSortedStartDescending = [...scopeEndTop.scopes].sort(
|
||||
(a, b) => b.range.start - a.range.start
|
||||
(a, b) => b.range.start - a.range.start,
|
||||
);
|
||||
for (const scope of scopesSortedStartDescending) {
|
||||
const idx = activeScopes.indexOf(scope);
|
||||
|
|
@ -227,7 +227,7 @@ function visitInstructionId(
|
|||
scopeStarts.pop();
|
||||
|
||||
const scopesSortedEndDescending = [...scopeStartTop.scopes].sort(
|
||||
(a, b) => b.range.end - a.range.end
|
||||
(a, b) => b.range.end - a.range.end,
|
||||
);
|
||||
activeScopes.push(...scopesSortedEndDescending);
|
||||
/**
|
||||
|
|
@ -247,14 +247,14 @@ function visitInstructionId(
|
|||
function visitPlace(
|
||||
id: InstructionId,
|
||||
place: Place,
|
||||
{ activeScopes, joined }: TraversalState
|
||||
{activeScopes, joined}: TraversalState,
|
||||
): void {
|
||||
/**
|
||||
* If an instruction mutates an outer scope, flatten all scopes from the top
|
||||
* of the stack to the mutated outer scope.
|
||||
*/
|
||||
const placeScope = getPlaceScope(id, place);
|
||||
if (placeScope != null && isMutable({ id } as any, place)) {
|
||||
if (placeScope != null && isMutable({id} as any, place)) {
|
||||
const placeScopeIdx = activeScopes.indexOf(placeScope);
|
||||
if (placeScopeIdx !== -1 && placeScopeIdx !== activeScopes.length - 1) {
|
||||
joined.union([placeScope, ...activeScopes.slice(placeScopeIdx + 1)]);
|
||||
|
|
@ -264,7 +264,7 @@ function visitPlace(
|
|||
|
||||
function getOverlappingReactiveScopes(
|
||||
fn: HIRFunction,
|
||||
context: ScopeInfo
|
||||
context: ScopeInfo,
|
||||
): DisjointSet<ReactiveScope> {
|
||||
const state: TraversalState = {
|
||||
joined: new DisjointSet<ReactiveScope>(),
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import { Effect, ValueKind, ValueReason } from "./HIR";
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {Effect, ValueKind, ValueReason} from './HIR';
|
||||
import {
|
||||
BuiltInType,
|
||||
FunctionType,
|
||||
ObjectType,
|
||||
PolyType,
|
||||
PrimitiveType,
|
||||
} from "./Types";
|
||||
} from './Types';
|
||||
|
||||
/*
|
||||
* This file exports types and defaults for JavaScript object shapes. These are
|
||||
|
|
@ -22,7 +22,7 @@ import {
|
|||
*/
|
||||
|
||||
const PRIMITIVE_TYPE: PrimitiveType = {
|
||||
kind: "Primitive",
|
||||
kind: 'Primitive',
|
||||
};
|
||||
|
||||
let nextAnonId = 0;
|
||||
|
|
@ -42,8 +42,8 @@ function createAnonId(): string {
|
|||
export function addFunction(
|
||||
registry: ShapeRegistry,
|
||||
properties: Iterable<[string, BuiltInType | PolyType]>,
|
||||
fn: Omit<FunctionSignature, "hookKind">,
|
||||
id: string | null = null
|
||||
fn: Omit<FunctionSignature, 'hookKind'>,
|
||||
id: string | null = null,
|
||||
): FunctionType {
|
||||
const shapeId = id ?? createAnonId();
|
||||
addShape(registry, shapeId, properties, {
|
||||
|
|
@ -51,7 +51,7 @@ export function addFunction(
|
|||
hookKind: null,
|
||||
});
|
||||
return {
|
||||
kind: "Function",
|
||||
kind: 'Function',
|
||||
return: fn.returnType,
|
||||
shapeId,
|
||||
};
|
||||
|
|
@ -64,13 +64,13 @@ export function addFunction(
|
|||
*/
|
||||
export function addHook(
|
||||
registry: ShapeRegistry,
|
||||
fn: FunctionSignature & { hookKind: HookKind },
|
||||
id: string | null = null
|
||||
fn: FunctionSignature & {hookKind: HookKind},
|
||||
id: string | null = null,
|
||||
): FunctionType {
|
||||
const shapeId = id ?? createAnonId();
|
||||
addShape(registry, shapeId, [], fn);
|
||||
return {
|
||||
kind: "Function",
|
||||
kind: 'Function',
|
||||
return: fn.returnType,
|
||||
shapeId,
|
||||
};
|
||||
|
|
@ -84,12 +84,12 @@ export function addHook(
|
|||
export function addObject(
|
||||
registry: ShapeRegistry,
|
||||
id: string | null,
|
||||
properties: Iterable<[string, BuiltInType | PolyType]>
|
||||
properties: Iterable<[string, BuiltInType | PolyType]>,
|
||||
): ObjectType {
|
||||
const shapeId = id ?? createAnonId();
|
||||
addShape(registry, shapeId, properties, null);
|
||||
return {
|
||||
kind: "Object",
|
||||
kind: 'Object',
|
||||
shapeId,
|
||||
};
|
||||
}
|
||||
|
|
@ -98,7 +98,7 @@ function addShape(
|
|||
registry: ShapeRegistry,
|
||||
id: string,
|
||||
properties: Iterable<[string, BuiltInType | PolyType]>,
|
||||
functionType: FunctionSignature | null
|
||||
functionType: FunctionSignature | null,
|
||||
): ObjectShape {
|
||||
const shape: ObjectShape = {
|
||||
properties: new Map(properties),
|
||||
|
|
@ -116,17 +116,17 @@ function addShape(
|
|||
}
|
||||
|
||||
export type HookKind =
|
||||
| "useContext"
|
||||
| "useState"
|
||||
| "useActionState"
|
||||
| "useReducer"
|
||||
| "useRef"
|
||||
| "useEffect"
|
||||
| "useLayoutEffect"
|
||||
| "useInsertionEffect"
|
||||
| "useMemo"
|
||||
| "useCallback"
|
||||
| "Custom";
|
||||
| 'useContext'
|
||||
| 'useState'
|
||||
| 'useActionState'
|
||||
| 'useReducer'
|
||||
| 'useRef'
|
||||
| 'useEffect'
|
||||
| 'useLayoutEffect'
|
||||
| 'useInsertionEffect'
|
||||
| 'useMemo'
|
||||
| 'useCallback'
|
||||
| 'Custom';
|
||||
|
||||
/*
|
||||
* Call signature of a function, used for type and effect inference.
|
||||
|
|
@ -190,91 +190,91 @@ export type ObjectShape = {
|
|||
* the inferred types for [] and {}.
|
||||
*/
|
||||
export type ShapeRegistry = Map<string, ObjectShape>;
|
||||
export const BuiltInPropsId = "BuiltInProps";
|
||||
export const BuiltInArrayId = "BuiltInArray";
|
||||
export const BuiltInFunctionId = "BuiltInFunction";
|
||||
export const BuiltInJsxId = "BuiltInJsx";
|
||||
export const BuiltInObjectId = "BuiltInObject";
|
||||
export const BuiltInUseStateId = "BuiltInUseState";
|
||||
export const BuiltInSetStateId = "BuiltInSetState";
|
||||
export const BuiltInUseActionStateId = "BuiltInUseActionState";
|
||||
export const BuiltInSetActionStateId = "BuiltInSetActionState";
|
||||
export const BuiltInUseRefId = "BuiltInUseRefId";
|
||||
export const BuiltInRefValueId = "BuiltInRefValue";
|
||||
export const BuiltInMixedReadonlyId = "BuiltInMixedReadonly";
|
||||
export const BuiltInUseEffectHookId = "BuiltInUseEffectHook";
|
||||
export const BuiltInUseLayoutEffectHookId = "BuiltInUseLayoutEffectHook";
|
||||
export const BuiltInUseInsertionEffectHookId = "BuiltInUseInsertionEffectHook";
|
||||
export const BuiltInUseOperatorId = "BuiltInUseOperator";
|
||||
export const BuiltInUseReducerId = "BuiltInUseReducer";
|
||||
export const BuiltInDispatchId = "BuiltInDispatch";
|
||||
export const BuiltInPropsId = 'BuiltInProps';
|
||||
export const BuiltInArrayId = 'BuiltInArray';
|
||||
export const BuiltInFunctionId = 'BuiltInFunction';
|
||||
export const BuiltInJsxId = 'BuiltInJsx';
|
||||
export const BuiltInObjectId = 'BuiltInObject';
|
||||
export const BuiltInUseStateId = 'BuiltInUseState';
|
||||
export const BuiltInSetStateId = 'BuiltInSetState';
|
||||
export const BuiltInUseActionStateId = 'BuiltInUseActionState';
|
||||
export const BuiltInSetActionStateId = 'BuiltInSetActionState';
|
||||
export const BuiltInUseRefId = 'BuiltInUseRefId';
|
||||
export const BuiltInRefValueId = 'BuiltInRefValue';
|
||||
export const BuiltInMixedReadonlyId = 'BuiltInMixedReadonly';
|
||||
export const BuiltInUseEffectHookId = 'BuiltInUseEffectHook';
|
||||
export const BuiltInUseLayoutEffectHookId = 'BuiltInUseLayoutEffectHook';
|
||||
export const BuiltInUseInsertionEffectHookId = 'BuiltInUseInsertionEffectHook';
|
||||
export const BuiltInUseOperatorId = 'BuiltInUseOperator';
|
||||
export const BuiltInUseReducerId = 'BuiltInUseReducer';
|
||||
export const BuiltInDispatchId = 'BuiltInDispatch';
|
||||
|
||||
// ShapeRegistry with default definitions for built-ins.
|
||||
export const BUILTIN_SHAPES: ShapeRegistry = new Map();
|
||||
|
||||
// If the `ref` prop exists, it has the ref type
|
||||
addObject(BUILTIN_SHAPES, BuiltInPropsId, [
|
||||
["ref", { kind: "Object", shapeId: BuiltInUseRefId }],
|
||||
['ref', {kind: 'Object', shapeId: BuiltInUseRefId}],
|
||||
]);
|
||||
|
||||
/* Built-in array shape */
|
||||
addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
||||
[
|
||||
"indexOf",
|
||||
'indexOf',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"includes",
|
||||
'includes',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"pop",
|
||||
'pop',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: null,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Store,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"at",
|
||||
'at',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [Effect.Read],
|
||||
restParam: null,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Capture,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"concat",
|
||||
'concat',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Capture,
|
||||
returnType: {
|
||||
kind: "Object",
|
||||
kind: 'Object',
|
||||
shapeId: BuiltInArrayId,
|
||||
},
|
||||
calleeEffect: Effect.Capture,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
}),
|
||||
],
|
||||
["length", PRIMITIVE_TYPE],
|
||||
['length', PRIMITIVE_TYPE],
|
||||
[
|
||||
"push",
|
||||
'push',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Capture,
|
||||
|
|
@ -284,12 +284,12 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"slice",
|
||||
'slice',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: {
|
||||
kind: "Object",
|
||||
kind: 'Object',
|
||||
shapeId: BuiltInArrayId,
|
||||
},
|
||||
calleeEffect: Effect.Capture,
|
||||
|
|
@ -297,11 +297,11 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"map",
|
||||
'map',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.ConditionallyMutate,
|
||||
returnType: { kind: "Object", shapeId: BuiltInArrayId },
|
||||
returnType: {kind: 'Object', shapeId: BuiltInArrayId},
|
||||
/*
|
||||
* callee is ConditionallyMutate because items of the array
|
||||
* flow into the lambda and may be mutated there, even though
|
||||
|
|
@ -314,11 +314,11 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"filter",
|
||||
'filter',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.ConditionallyMutate,
|
||||
returnType: { kind: "Object", shapeId: BuiltInArrayId },
|
||||
returnType: {kind: 'Object', shapeId: BuiltInArrayId},
|
||||
/*
|
||||
* callee is ConditionallyMutate because items of the array
|
||||
* flow into the lambda and may be mutated there, even though
|
||||
|
|
@ -331,11 +331,11 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"every",
|
||||
'every',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.ConditionallyMutate,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
/*
|
||||
* callee is ConditionallyMutate because items of the array
|
||||
* flow into the lambda and may be mutated there, even though
|
||||
|
|
@ -348,11 +348,11 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"some",
|
||||
'some',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.ConditionallyMutate,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
/*
|
||||
* callee is ConditionallyMutate because items of the array
|
||||
* flow into the lambda and may be mutated there, even though
|
||||
|
|
@ -365,11 +365,11 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"find",
|
||||
'find',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.ConditionallyMutate,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.ConditionallyMutate,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
noAlias: true,
|
||||
|
|
@ -377,11 +377,11 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"findIndex",
|
||||
'findIndex',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.ConditionallyMutate,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
/*
|
||||
* callee is ConditionallyMutate because items of the array
|
||||
* flow into the lambda and may be mutated there, even though
|
||||
|
|
@ -394,7 +394,7 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"join",
|
||||
'join',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
|
|
@ -409,7 +409,7 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
|||
/* Built-in Object shape */
|
||||
addObject(BUILTIN_SHAPES, BuiltInObjectId, [
|
||||
[
|
||||
"toString",
|
||||
'toString',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: null,
|
||||
|
|
@ -425,9 +425,9 @@ addObject(BUILTIN_SHAPES, BuiltInObjectId, [
|
|||
]);
|
||||
|
||||
addObject(BUILTIN_SHAPES, BuiltInUseStateId, [
|
||||
["0", { kind: "Poly" }],
|
||||
['0', {kind: 'Poly'}],
|
||||
[
|
||||
"1",
|
||||
'1',
|
||||
addFunction(
|
||||
BUILTIN_SHAPES,
|
||||
[],
|
||||
|
|
@ -438,15 +438,15 @@ addObject(BUILTIN_SHAPES, BuiltInUseStateId, [
|
|||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
},
|
||||
BuiltInSetStateId
|
||||
BuiltInSetStateId,
|
||||
),
|
||||
],
|
||||
]);
|
||||
|
||||
addObject(BUILTIN_SHAPES, BuiltInUseActionStateId, [
|
||||
["0", { kind: "Poly" }],
|
||||
['0', {kind: 'Poly'}],
|
||||
[
|
||||
"1",
|
||||
'1',
|
||||
addFunction(
|
||||
BUILTIN_SHAPES,
|
||||
[],
|
||||
|
|
@ -457,15 +457,15 @@ addObject(BUILTIN_SHAPES, BuiltInUseActionStateId, [
|
|||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
},
|
||||
BuiltInSetActionStateId
|
||||
BuiltInSetActionStateId,
|
||||
),
|
||||
],
|
||||
]);
|
||||
|
||||
addObject(BUILTIN_SHAPES, BuiltInUseReducerId, [
|
||||
["0", { kind: "Poly" }],
|
||||
['0', {kind: 'Poly'}],
|
||||
[
|
||||
"1",
|
||||
'1',
|
||||
addFunction(
|
||||
BUILTIN_SHAPES,
|
||||
[],
|
||||
|
|
@ -476,22 +476,22 @@ addObject(BUILTIN_SHAPES, BuiltInUseReducerId, [
|
|||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
},
|
||||
BuiltInDispatchId
|
||||
BuiltInDispatchId,
|
||||
),
|
||||
],
|
||||
]);
|
||||
|
||||
addObject(BUILTIN_SHAPES, BuiltInUseRefId, [
|
||||
["current", { kind: "Object", shapeId: BuiltInRefValueId }],
|
||||
['current', {kind: 'Object', shapeId: BuiltInRefValueId}],
|
||||
]);
|
||||
|
||||
addObject(BUILTIN_SHAPES, BuiltInRefValueId, [
|
||||
["*", { kind: "Object", shapeId: BuiltInRefValueId }],
|
||||
['*', {kind: 'Object', shapeId: BuiltInRefValueId}],
|
||||
]);
|
||||
|
||||
addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
||||
[
|
||||
"toString",
|
||||
'toString',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
|
|
@ -501,34 +501,34 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"map",
|
||||
'map',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Object", shapeId: BuiltInArrayId },
|
||||
returnType: {kind: 'Object', shapeId: BuiltInArrayId},
|
||||
calleeEffect: Effect.ConditionallyMutate,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
noAlias: true,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"filter",
|
||||
'filter',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: { kind: "Object", shapeId: BuiltInArrayId },
|
||||
returnType: {kind: 'Object', shapeId: BuiltInArrayId},
|
||||
calleeEffect: Effect.ConditionallyMutate,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
noAlias: true,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"concat",
|
||||
'concat',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Capture,
|
||||
returnType: {
|
||||
kind: "Object",
|
||||
kind: 'Object',
|
||||
shapeId: BuiltInArrayId,
|
||||
},
|
||||
calleeEffect: Effect.Capture,
|
||||
|
|
@ -536,12 +536,12 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"slice",
|
||||
'slice',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: {
|
||||
kind: "Object",
|
||||
kind: 'Object',
|
||||
shapeId: BuiltInArrayId,
|
||||
},
|
||||
calleeEffect: Effect.Capture,
|
||||
|
|
@ -549,11 +549,11 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"every",
|
||||
'every',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.ConditionallyMutate,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.ConditionallyMutate,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
noAlias: true,
|
||||
|
|
@ -561,11 +561,11 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"some",
|
||||
'some',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.ConditionallyMutate,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.ConditionallyMutate,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
noAlias: true,
|
||||
|
|
@ -573,11 +573,11 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"find",
|
||||
'find',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.ConditionallyMutate,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.ConditionallyMutate,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
noAlias: true,
|
||||
|
|
@ -585,11 +585,11 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"findIndex",
|
||||
'findIndex',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.ConditionallyMutate,
|
||||
returnType: { kind: "Primitive" },
|
||||
returnType: {kind: 'Primitive'},
|
||||
calleeEffect: Effect.ConditionallyMutate,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
noAlias: true,
|
||||
|
|
@ -597,7 +597,7 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
|||
}),
|
||||
],
|
||||
[
|
||||
"join",
|
||||
'join',
|
||||
addFunction(BUILTIN_SHAPES, [], {
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
|
|
@ -606,7 +606,7 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
|||
returnValueKind: ValueKind.Primitive,
|
||||
}),
|
||||
],
|
||||
["*", { kind: "Object", shapeId: BuiltInMixedReadonlyId }],
|
||||
['*', {kind: 'Object', shapeId: BuiltInMixedReadonlyId}],
|
||||
]);
|
||||
|
||||
addObject(BUILTIN_SHAPES, BuiltInJsxId, []);
|
||||
|
|
@ -617,12 +617,12 @@ export const DefaultMutatingHook = addHook(
|
|||
{
|
||||
positionalParams: [],
|
||||
restParam: Effect.ConditionallyMutate,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "Custom",
|
||||
hookKind: 'Custom',
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
},
|
||||
"DefaultMutatingHook"
|
||||
'DefaultMutatingHook',
|
||||
);
|
||||
|
||||
export const DefaultNonmutatingHook = addHook(
|
||||
|
|
@ -630,10 +630,10 @@ export const DefaultNonmutatingHook = addHook(
|
|||
{
|
||||
positionalParams: [],
|
||||
restParam: Effect.Freeze,
|
||||
returnType: { kind: "Poly" },
|
||||
returnType: {kind: 'Poly'},
|
||||
calleeEffect: Effect.Read,
|
||||
hookKind: "Custom",
|
||||
hookKind: 'Custom',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
},
|
||||
"DefaultNonmutatingHook"
|
||||
'DefaultNonmutatingHook',
|
||||
);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,5 +1,5 @@
|
|||
import { CompilerError } from "..";
|
||||
import { BlockId, GotoVariant, HIRFunction } from "./HIR";
|
||||
import {CompilerError} from '..';
|
||||
import {BlockId, GotoVariant, HIRFunction} from './HIR';
|
||||
|
||||
export function pruneUnusedLabelsHIR(fn: HIRFunction): void {
|
||||
const merged: Array<{
|
||||
|
|
@ -10,16 +10,16 @@ export function pruneUnusedLabelsHIR(fn: HIRFunction): void {
|
|||
const rewrites: Map<BlockId, BlockId> = new Map();
|
||||
for (const [blockId, block] of fn.body.blocks) {
|
||||
const terminal = block.terminal;
|
||||
if (terminal.kind === "label") {
|
||||
const { block: nextId, fallthrough: fallthroughId } = terminal;
|
||||
if (terminal.kind === 'label') {
|
||||
const {block: nextId, fallthrough: fallthroughId} = terminal;
|
||||
const next = fn.body.blocks.get(nextId)!;
|
||||
const fallthrough = fn.body.blocks.get(fallthroughId)!;
|
||||
if (
|
||||
next.terminal.kind === "goto" &&
|
||||
next.terminal.kind === 'goto' &&
|
||||
next.terminal.variant === GotoVariant.Break &&
|
||||
next.terminal.block === fallthroughId
|
||||
) {
|
||||
if (next.kind === "block" && fallthrough.kind === "block") {
|
||||
if (next.kind === 'block' && fallthrough.kind === 'block') {
|
||||
// Only merge normal block types
|
||||
merged.push({
|
||||
label: blockId,
|
||||
|
|
@ -45,9 +45,9 @@ export function pruneUnusedLabelsHIR(fn: HIRFunction): void {
|
|||
CompilerError.invariant(
|
||||
next.phis.size === 0 && fallthrough.phis.size === 0,
|
||||
{
|
||||
reason: "Unexpected phis when merging label blocks",
|
||||
reason: 'Unexpected phis when merging label blocks',
|
||||
loc: label.terminal.loc,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
CompilerError.invariant(
|
||||
|
|
@ -56,9 +56,9 @@ export function pruneUnusedLabelsHIR(fn: HIRFunction): void {
|
|||
next.preds.has(originalLabelId) &&
|
||||
fallthrough.preds.has(nextId),
|
||||
{
|
||||
reason: "Unexpected block predecessors when merging label blocks",
|
||||
reason: 'Unexpected block predecessors when merging label blocks',
|
||||
loc: label.terminal.loc,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
label.instructions.push(...next.instructions, ...fallthrough.instructions);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import {CompilerError} from '../CompilerError';
|
||||
|
||||
export type BuiltInType = PrimitiveType | FunctionType | ObjectType;
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ export type Type =
|
|||
| PolyType
|
||||
| PropType
|
||||
| ObjectMethod;
|
||||
export type PrimitiveType = { kind: "Primitive" };
|
||||
export type PrimitiveType = {kind: 'Primitive'};
|
||||
|
||||
/*
|
||||
* An {@link FunctionType} or {@link ObjectType} (also a JS object) may be associated with an
|
||||
|
|
@ -34,36 +34,36 @@ export type PrimitiveType = { kind: "Primitive" };
|
|||
*/
|
||||
|
||||
export type FunctionType = {
|
||||
kind: "Function";
|
||||
kind: 'Function';
|
||||
shapeId: string | null;
|
||||
return: Type;
|
||||
};
|
||||
|
||||
export type ObjectType = {
|
||||
kind: "Object";
|
||||
kind: 'Object';
|
||||
shapeId: string | null;
|
||||
};
|
||||
|
||||
export type TypeVar = {
|
||||
kind: "Type";
|
||||
kind: 'Type';
|
||||
id: TypeId;
|
||||
};
|
||||
export type PolyType = {
|
||||
kind: "Poly";
|
||||
kind: 'Poly';
|
||||
};
|
||||
export type PhiType = {
|
||||
kind: "Phi";
|
||||
kind: 'Phi';
|
||||
operands: Array<Type>;
|
||||
};
|
||||
export type PropType = {
|
||||
kind: "Property";
|
||||
kind: 'Property';
|
||||
objectType: Type;
|
||||
objectName: string;
|
||||
propertyName: string;
|
||||
};
|
||||
|
||||
export type ObjectMethod = {
|
||||
kind: "ObjectMethod";
|
||||
kind: 'ObjectMethod';
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -71,11 +71,11 @@ export type ObjectMethod = {
|
|||
* accidentally.
|
||||
*/
|
||||
const opaqueTypeId = Symbol();
|
||||
export type TypeId = number & { [opaqueTypeId]: "IdentifierId" };
|
||||
export type TypeId = number & {[opaqueTypeId]: 'IdentifierId'};
|
||||
|
||||
export function makeTypeId(id: number): TypeId {
|
||||
CompilerError.invariant(id >= 0 && Number.isInteger(id), {
|
||||
reason: "Expected instruction id to be a non-negative integer",
|
||||
reason: 'Expected instruction id to be a non-negative integer',
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
|
|
@ -86,7 +86,7 @@ export function makeTypeId(id: number): TypeId {
|
|||
let typeCounter = 0;
|
||||
export function makeType(): TypeVar {
|
||||
return {
|
||||
kind: "Type",
|
||||
kind: 'Type',
|
||||
id: makeTypeId(typeCounter++),
|
||||
};
|
||||
}
|
||||
|
|
@ -97,40 +97,40 @@ export function makeType(): TypeVar {
|
|||
*/
|
||||
export function duplicateType(type: Type): Type {
|
||||
switch (type.kind) {
|
||||
case "Function": {
|
||||
case 'Function': {
|
||||
return {
|
||||
kind: "Function",
|
||||
kind: 'Function',
|
||||
return: duplicateType(type.return),
|
||||
shapeId: type.shapeId,
|
||||
};
|
||||
}
|
||||
case "Object": {
|
||||
return { kind: "Object", shapeId: type.shapeId };
|
||||
case 'Object': {
|
||||
return {kind: 'Object', shapeId: type.shapeId};
|
||||
}
|
||||
case "ObjectMethod": {
|
||||
return { kind: "ObjectMethod" };
|
||||
case 'ObjectMethod': {
|
||||
return {kind: 'ObjectMethod'};
|
||||
}
|
||||
case "Phi": {
|
||||
case 'Phi': {
|
||||
return {
|
||||
kind: "Phi",
|
||||
operands: type.operands.map((operand) => duplicateType(operand)),
|
||||
kind: 'Phi',
|
||||
operands: type.operands.map(operand => duplicateType(operand)),
|
||||
};
|
||||
}
|
||||
case "Poly": {
|
||||
return { kind: "Poly" };
|
||||
case 'Poly': {
|
||||
return {kind: 'Poly'};
|
||||
}
|
||||
case "Primitive": {
|
||||
return { kind: "Primitive" };
|
||||
case 'Primitive': {
|
||||
return {kind: 'Primitive'};
|
||||
}
|
||||
case "Property": {
|
||||
case 'Property': {
|
||||
return {
|
||||
kind: "Property",
|
||||
kind: 'Property',
|
||||
objectType: duplicateType(type.objectType),
|
||||
objectName: type.objectName,
|
||||
propertyName: type.propertyName,
|
||||
};
|
||||
}
|
||||
case "Type": {
|
||||
case 'Type': {
|
||||
return makeType();
|
||||
}
|
||||
}
|
||||
|
|
@ -151,7 +151,7 @@ export function typeEquals(tA: Type, tB: Type): boolean {
|
|||
}
|
||||
|
||||
function typeVarEquals(tA: Type, tB: Type): boolean {
|
||||
if (tA.kind === "Type" && tB.kind === "Type") {
|
||||
if (tA.kind === 'Type' && tB.kind === 'Type') {
|
||||
return tA.id === tB.id;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -162,11 +162,11 @@ function typeKindCheck(tA: Type, tb: Type, type: string): boolean {
|
|||
}
|
||||
|
||||
function objectMethodTypeEquals(tA: Type, tB: Type): boolean {
|
||||
return typeKindCheck(tA, tB, "ObjectMethod");
|
||||
return typeKindCheck(tA, tB, 'ObjectMethod');
|
||||
}
|
||||
|
||||
function propTypeEquals(tA: Type, tB: Type): boolean {
|
||||
if (tA.kind === "Property" && tB.kind === "Property") {
|
||||
if (tA.kind === 'Property' && tB.kind === 'Property') {
|
||||
if (!typeEquals(tA.objectType, tB.objectType)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -180,15 +180,15 @@ function propTypeEquals(tA: Type, tB: Type): boolean {
|
|||
}
|
||||
|
||||
function primitiveTypeEquals(tA: Type, tB: Type): boolean {
|
||||
return typeKindCheck(tA, tB, "Primitive");
|
||||
return typeKindCheck(tA, tB, 'Primitive');
|
||||
}
|
||||
|
||||
function polyTypeEquals(tA: Type, tB: Type): boolean {
|
||||
return typeKindCheck(tA, tB, "Poly");
|
||||
return typeKindCheck(tA, tB, 'Poly');
|
||||
}
|
||||
|
||||
function objectTypeEquals(tA: Type, tB: Type): boolean {
|
||||
if (tA.kind === "Object" && tB.kind == "Object") {
|
||||
if (tA.kind === 'Object' && tB.kind == 'Object') {
|
||||
return tA.shapeId === tB.shapeId;
|
||||
}
|
||||
|
||||
|
|
@ -196,14 +196,14 @@ function objectTypeEquals(tA: Type, tB: Type): boolean {
|
|||
}
|
||||
|
||||
function funcTypeEquals(tA: Type, tB: Type): boolean {
|
||||
if (tA.kind !== "Function" || tB.kind !== "Function") {
|
||||
if (tA.kind !== 'Function' || tB.kind !== 'Function') {
|
||||
return false;
|
||||
}
|
||||
return typeEquals(tA.return, tB.return);
|
||||
}
|
||||
|
||||
function phiTypeEquals(tA: Type, tB: Type): boolean {
|
||||
if (tA.kind === "Phi" && tB.kind === "Phi") {
|
||||
if (tA.kind === 'Phi' && tB.kind === 'Phi') {
|
||||
if (tA.operands.length !== tB.operands.length) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,16 +5,16 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
export { assertConsistentIdentifiers } from "./AssertConsistentIdentifiers";
|
||||
export {assertConsistentIdentifiers} from './AssertConsistentIdentifiers';
|
||||
export {
|
||||
assertTerminalSuccessorsExist,
|
||||
assertTerminalPredsExist,
|
||||
} from "./AssertTerminalBlocksExist";
|
||||
export { assertValidBlockNesting } from "./AssertValidBlockNesting";
|
||||
export { assertValidMutableRanges } from "./AssertValidMutableRanges";
|
||||
export { lower } from "./BuildHIR";
|
||||
export { buildReactiveScopeTerminalsHIR } from "./BuildReactiveScopeTerminalsHIR";
|
||||
export { computeDominatorTree, computePostDominatorTree } from "./Dominator";
|
||||
} from './AssertTerminalBlocksExist';
|
||||
export {assertValidBlockNesting} from './AssertValidBlockNesting';
|
||||
export {assertValidMutableRanges} from './AssertValidMutableRanges';
|
||||
export {lower} from './BuildHIR';
|
||||
export {buildReactiveScopeTerminalsHIR} from './BuildReactiveScopeTerminalsHIR';
|
||||
export {computeDominatorTree, computePostDominatorTree} from './Dominator';
|
||||
export {
|
||||
Environment,
|
||||
parseConfigPragma,
|
||||
|
|
@ -22,15 +22,15 @@ export {
|
|||
type EnvironmentConfig,
|
||||
type ExternalFunction,
|
||||
type Hook,
|
||||
} from "./Environment";
|
||||
export * from "./HIR";
|
||||
} from './Environment';
|
||||
export * from './HIR';
|
||||
export {
|
||||
markInstructionIds,
|
||||
markPredecessors,
|
||||
removeUnnecessaryTryCatch,
|
||||
reversePostorderBlocks,
|
||||
} from "./HIRBuilder";
|
||||
export { mergeConsecutiveBlocks } from "./MergeConsecutiveBlocks";
|
||||
export { mergeOverlappingReactiveScopesHIR } from "./MergeOverlappingReactiveScopesHIR";
|
||||
export { printFunction, printHIR } from "./PrintHIR";
|
||||
export { pruneUnusedLabelsHIR } from "./PruneUnusedLabelsHIR";
|
||||
} from './HIRBuilder';
|
||||
export {mergeConsecutiveBlocks} from './MergeConsecutiveBlocks';
|
||||
export {mergeOverlappingReactiveScopesHIR} from './MergeOverlappingReactiveScopesHIR';
|
||||
export {printFunction, printHIR} from './PrintHIR';
|
||||
export {pruneUnusedLabelsHIR} from './PruneUnusedLabelsHIR';
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {
|
||||
Effect,
|
||||
HIRFunction,
|
||||
|
|
@ -17,14 +17,14 @@ import {
|
|||
isRefValueType,
|
||||
isUseRefType,
|
||||
makeInstructionId,
|
||||
} from "../HIR";
|
||||
import { deadCodeElimination } from "../Optimization";
|
||||
import { inferReactiveScopeVariables } from "../ReactiveScopes";
|
||||
import { leaveSSA } from "../SSA";
|
||||
import { logHIRFunction } from "../Utils/logger";
|
||||
import { inferMutableContextVariables } from "./InferMutableContextVariables";
|
||||
import { inferMutableRanges } from "./InferMutableRanges";
|
||||
import inferReferenceEffects from "./InferReferenceEffects";
|
||||
} from '../HIR';
|
||||
import {deadCodeElimination} from '../Optimization';
|
||||
import {inferReactiveScopeVariables} from '../ReactiveScopes';
|
||||
import {leaveSSA} from '../SSA';
|
||||
import {logHIRFunction} from '../Utils/logger';
|
||||
import {inferMutableContextVariables} from './InferMutableContextVariables';
|
||||
import {inferMutableRanges} from './InferMutableRanges';
|
||||
import inferReferenceEffects from './InferReferenceEffects';
|
||||
|
||||
// Helper class to track indirections such as LoadLocal and PropertyLoad.
|
||||
export class IdentifierState {
|
||||
|
|
@ -42,7 +42,7 @@ export class IdentifierState {
|
|||
const objectDependency = this.properties.get(object.identifier);
|
||||
let nextDependency: ReactiveScopeDependency;
|
||||
if (objectDependency === undefined) {
|
||||
nextDependency = { identifier: object.identifier, path: [property] };
|
||||
nextDependency = {identifier: object.identifier, path: [property]};
|
||||
} else {
|
||||
nextDependency = {
|
||||
identifier: objectDependency.identifier,
|
||||
|
|
@ -54,7 +54,7 @@ export class IdentifierState {
|
|||
|
||||
declareTemporary(lvalue: Place, value: Place): void {
|
||||
const resolved: ReactiveScopeDependency = this.properties.get(
|
||||
value.identifier
|
||||
value.identifier,
|
||||
) ?? {
|
||||
identifier: value.identifier,
|
||||
path: [],
|
||||
|
|
@ -69,30 +69,30 @@ export default function analyseFunctions(func: HIRFunction): void {
|
|||
for (const [_, block] of func.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
switch (instr.value.kind) {
|
||||
case "ObjectMethod":
|
||||
case "FunctionExpression": {
|
||||
case 'ObjectMethod':
|
||||
case 'FunctionExpression': {
|
||||
lower(instr.value.loweredFunc.func);
|
||||
infer(instr.value.loweredFunc, state, func.context);
|
||||
break;
|
||||
}
|
||||
case "PropertyLoad": {
|
||||
case 'PropertyLoad': {
|
||||
state.declareProperty(
|
||||
instr.lvalue,
|
||||
instr.value.object,
|
||||
instr.value.property
|
||||
instr.value.property,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "ComputedLoad": {
|
||||
case 'ComputedLoad': {
|
||||
/*
|
||||
* The path is set to an empty string as the path doesn't really
|
||||
* matter for a computed load.
|
||||
*/
|
||||
state.declareProperty(instr.lvalue, instr.value.object, "");
|
||||
state.declareProperty(instr.lvalue, instr.value.object, '');
|
||||
break;
|
||||
}
|
||||
case "LoadLocal":
|
||||
case "LoadContext": {
|
||||
case 'LoadLocal':
|
||||
case 'LoadContext': {
|
||||
if (instr.lvalue.identifier.name === null) {
|
||||
state.declareTemporary(instr.lvalue, instr.value.place);
|
||||
}
|
||||
|
|
@ -105,19 +105,19 @@ export default function analyseFunctions(func: HIRFunction): void {
|
|||
|
||||
function lower(func: HIRFunction): void {
|
||||
analyseFunctions(func);
|
||||
inferReferenceEffects(func, { isFunctionExpression: true });
|
||||
inferReferenceEffects(func, {isFunctionExpression: true});
|
||||
deadCodeElimination(func);
|
||||
inferMutableRanges(func);
|
||||
leaveSSA(func);
|
||||
inferReactiveScopeVariables(func);
|
||||
inferMutableContextVariables(func);
|
||||
logHIRFunction("AnalyseFunction (inner)", func);
|
||||
logHIRFunction('AnalyseFunction (inner)', func);
|
||||
}
|
||||
|
||||
function infer(
|
||||
loweredFunc: LoweredFunction,
|
||||
state: IdentifierState,
|
||||
context: Array<Place>
|
||||
context: Array<Place>,
|
||||
): void {
|
||||
const mutations = new Map<string, Effect>();
|
||||
for (const operand of loweredFunc.func.context) {
|
||||
|
|
@ -166,7 +166,7 @@ function infer(
|
|||
*/
|
||||
for (const place of context) {
|
||||
CompilerError.invariant(place.identifier.name !== null, {
|
||||
reason: "context refs should always have a name",
|
||||
reason: 'context refs should always have a name',
|
||||
description: null,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { CompilerError, SourceLocation } from "..";
|
||||
import {CompilerError, SourceLocation} from '..';
|
||||
import {
|
||||
CallExpression,
|
||||
Effect,
|
||||
|
|
@ -28,11 +28,11 @@ import {
|
|||
TInstruction,
|
||||
getHookKindForType,
|
||||
makeInstructionId,
|
||||
} from "../HIR";
|
||||
import { createTemporaryPlace, markInstructionIds } from "../HIR/HIRBuilder";
|
||||
} from '../HIR';
|
||||
import {createTemporaryPlace, markInstructionIds} from '../HIR/HIRBuilder';
|
||||
|
||||
type ManualMemoCallee = {
|
||||
kind: "useMemo" | "useCallback";
|
||||
kind: 'useMemo' | 'useCallback';
|
||||
loadInstr: TInstruction<LoadGlobal> | TInstruction<PropertyLoad>;
|
||||
};
|
||||
|
||||
|
|
@ -51,19 +51,19 @@ type IdentifierSidemap = {
|
|||
*/
|
||||
export function collectMaybeMemoDependencies(
|
||||
value: InstructionValue,
|
||||
maybeDeps: Map<IdentifierId, ManualMemoDependency>
|
||||
maybeDeps: Map<IdentifierId, ManualMemoDependency>,
|
||||
): ManualMemoDependency | null {
|
||||
switch (value.kind) {
|
||||
case "LoadGlobal": {
|
||||
case 'LoadGlobal': {
|
||||
return {
|
||||
root: {
|
||||
kind: "Global",
|
||||
kind: 'Global',
|
||||
identifierName: value.binding.name,
|
||||
},
|
||||
path: [],
|
||||
};
|
||||
}
|
||||
case "PropertyLoad": {
|
||||
case 'PropertyLoad': {
|
||||
const object = maybeDeps.get(value.object.identifier.id);
|
||||
if (object != null) {
|
||||
return {
|
||||
|
|
@ -74,26 +74,26 @@ export function collectMaybeMemoDependencies(
|
|||
break;
|
||||
}
|
||||
|
||||
case "LoadLocal":
|
||||
case "LoadContext": {
|
||||
case 'LoadLocal':
|
||||
case 'LoadContext': {
|
||||
const source = maybeDeps.get(value.place.identifier.id);
|
||||
if (source != null) {
|
||||
return source;
|
||||
} else if (
|
||||
value.place.identifier.name != null &&
|
||||
value.place.identifier.name.kind === "named"
|
||||
value.place.identifier.name.kind === 'named'
|
||||
) {
|
||||
return {
|
||||
root: {
|
||||
kind: "NamedLocal",
|
||||
value: { ...value.place },
|
||||
kind: 'NamedLocal',
|
||||
value: {...value.place},
|
||||
},
|
||||
path: [],
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "StoreLocal": {
|
||||
case 'StoreLocal': {
|
||||
/*
|
||||
* Value blocks rely on StoreLocal to populate their return value.
|
||||
* We need to track these as optional property chains are valid in
|
||||
|
|
@ -102,7 +102,7 @@ export function collectMaybeMemoDependencies(
|
|||
const lvalue = value.lvalue.place.identifier;
|
||||
const rvalue = value.value.identifier.id;
|
||||
const aliased = maybeDeps.get(rvalue);
|
||||
if (aliased != null && lvalue.name?.kind !== "named") {
|
||||
if (aliased != null && lvalue.name?.kind !== 'named') {
|
||||
maybeDeps.set(lvalue.id, aliased);
|
||||
return aliased;
|
||||
}
|
||||
|
|
@ -115,34 +115,34 @@ export function collectMaybeMemoDependencies(
|
|||
function collectTemporaries(
|
||||
instr: Instruction,
|
||||
env: Environment,
|
||||
sidemap: IdentifierSidemap
|
||||
sidemap: IdentifierSidemap,
|
||||
): void {
|
||||
const { value, lvalue } = instr;
|
||||
const {value, lvalue} = instr;
|
||||
switch (value.kind) {
|
||||
case "FunctionExpression": {
|
||||
case 'FunctionExpression': {
|
||||
sidemap.functions.set(
|
||||
instr.lvalue.identifier.id,
|
||||
instr as TInstruction<FunctionExpression>
|
||||
instr as TInstruction<FunctionExpression>,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "LoadGlobal": {
|
||||
case 'LoadGlobal': {
|
||||
const global = env.getGlobalDeclaration(value.binding);
|
||||
const hookKind = global !== null ? getHookKindForType(env, global) : null;
|
||||
const lvalId = instr.lvalue.identifier.id;
|
||||
if (hookKind === "useMemo" || hookKind === "useCallback") {
|
||||
if (hookKind === 'useMemo' || hookKind === 'useCallback') {
|
||||
sidemap.manualMemos.set(lvalId, {
|
||||
kind: hookKind,
|
||||
loadInstr: instr as TInstruction<LoadGlobal>,
|
||||
});
|
||||
} else if (value.binding.name === "React") {
|
||||
} else if (value.binding.name === 'React') {
|
||||
sidemap.react.add(lvalId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "PropertyLoad": {
|
||||
case 'PropertyLoad': {
|
||||
if (sidemap.react.has(value.object.identifier.id)) {
|
||||
if (value.property === "useMemo" || value.property === "useCallback") {
|
||||
if (value.property === 'useMemo' || value.property === 'useCallback') {
|
||||
sidemap.manualMemos.set(instr.lvalue.identifier.id, {
|
||||
kind: value.property,
|
||||
loadInstr: instr as TInstruction<PropertyLoad>,
|
||||
|
|
@ -151,11 +151,11 @@ function collectTemporaries(
|
|||
}
|
||||
break;
|
||||
}
|
||||
case "ArrayExpression": {
|
||||
if (value.elements.every((e) => e.kind === "Identifier")) {
|
||||
case 'ArrayExpression': {
|
||||
if (value.elements.every(e => e.kind === 'Identifier')) {
|
||||
sidemap.maybeDepsLists.set(
|
||||
instr.lvalue.identifier.id,
|
||||
value.elements as Array<Place>
|
||||
value.elements as Array<Place>,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
|
@ -173,14 +173,14 @@ function makeManualMemoizationMarkers(
|
|||
env: Environment,
|
||||
depsList: Array<ManualMemoDependency> | null,
|
||||
memoDecl: Place,
|
||||
manualMemoId: number
|
||||
manualMemoId: number,
|
||||
): [TInstruction<StartMemoize>, TInstruction<FinishMemoize>] {
|
||||
return [
|
||||
{
|
||||
id: makeInstructionId(0),
|
||||
lvalue: createTemporaryPlace(env, fnExpr.loc),
|
||||
value: {
|
||||
kind: "StartMemoize",
|
||||
kind: 'StartMemoize',
|
||||
manualMemoId,
|
||||
/*
|
||||
* Use deps list from source instead of inferred deps
|
||||
|
|
@ -195,9 +195,9 @@ function makeManualMemoizationMarkers(
|
|||
id: makeInstructionId(0),
|
||||
lvalue: createTemporaryPlace(env, fnExpr.loc),
|
||||
value: {
|
||||
kind: "FinishMemoize",
|
||||
kind: 'FinishMemoize',
|
||||
manualMemoId,
|
||||
decl: { ...memoDecl },
|
||||
decl: {...memoDecl},
|
||||
loc: fnExpr.loc,
|
||||
},
|
||||
loc: fnExpr.loc,
|
||||
|
|
@ -208,9 +208,9 @@ function makeManualMemoizationMarkers(
|
|||
function getManualMemoizationReplacement(
|
||||
fn: Place,
|
||||
loc: SourceLocation,
|
||||
kind: "useMemo" | "useCallback"
|
||||
kind: 'useMemo' | 'useCallback',
|
||||
): LoadLocal | CallExpression {
|
||||
if (kind === "useMemo") {
|
||||
if (kind === 'useMemo') {
|
||||
/*
|
||||
* Replace the hook callee with the fn arg.
|
||||
*
|
||||
|
|
@ -230,7 +230,7 @@ function getManualMemoizationReplacement(
|
|||
* inline the useMemo callback along with any other immediately invoked IIFEs.
|
||||
*/
|
||||
return {
|
||||
kind: "CallExpression",
|
||||
kind: 'CallExpression',
|
||||
callee: fn,
|
||||
/*
|
||||
* Drop the args, including the deps array which DCE will remove
|
||||
|
|
@ -256,9 +256,9 @@ function getManualMemoizationReplacement(
|
|||
* $4 = LoadLocal $2 // reference the function
|
||||
*/
|
||||
return {
|
||||
kind: "LoadLocal",
|
||||
kind: 'LoadLocal',
|
||||
place: {
|
||||
kind: "Identifier",
|
||||
kind: 'Identifier',
|
||||
identifier: fn.identifier,
|
||||
effect: Effect.Unknown,
|
||||
reactive: false,
|
||||
|
|
@ -271,8 +271,8 @@ function getManualMemoizationReplacement(
|
|||
|
||||
function extractManualMemoizationArgs(
|
||||
instr: TInstruction<CallExpression> | TInstruction<MethodCall>,
|
||||
kind: "useCallback" | "useMemo",
|
||||
sidemap: IdentifierSidemap
|
||||
kind: 'useCallback' | 'useMemo',
|
||||
sidemap: IdentifierSidemap,
|
||||
): {
|
||||
fnPlace: Place;
|
||||
depsList: Array<ManualMemoDependency> | null;
|
||||
|
|
@ -287,7 +287,7 @@ function extractManualMemoizationArgs(
|
|||
suggestions: null,
|
||||
});
|
||||
}
|
||||
if (fnPlace.kind === "Spread" || depsListPlace?.kind === "Spread") {
|
||||
if (fnPlace.kind === 'Spread' || depsListPlace?.kind === 'Spread') {
|
||||
CompilerError.throwInvalidReact({
|
||||
reason: `Unexpected spread argument to ${kind}`,
|
||||
loc: instr.value.loc,
|
||||
|
|
@ -297,7 +297,7 @@ function extractManualMemoizationArgs(
|
|||
let depsList: Array<ManualMemoDependency> | null = null;
|
||||
if (depsListPlace != null) {
|
||||
const maybeDepsList = sidemap.maybeDepsLists.get(
|
||||
depsListPlace.identifier.id
|
||||
depsListPlace.identifier.id,
|
||||
);
|
||||
if (maybeDepsList == null) {
|
||||
CompilerError.throwInvalidReact({
|
||||
|
|
@ -306,7 +306,7 @@ function extractManualMemoizationArgs(
|
|||
loc: depsListPlace.loc,
|
||||
});
|
||||
}
|
||||
depsList = maybeDepsList.map((dep) => {
|
||||
depsList = maybeDepsList.map(dep => {
|
||||
const maybeDep = sidemap.maybeDeps.get(dep.identifier.id);
|
||||
if (maybeDep == null) {
|
||||
CompilerError.throwInvalidReact({
|
||||
|
|
@ -362,25 +362,25 @@ export function dropManualMemoization(func: HIRFunction): void {
|
|||
for (let i = 0; i < block.instructions.length; i++) {
|
||||
const instr = block.instructions[i]!;
|
||||
if (
|
||||
instr.value.kind === "CallExpression" ||
|
||||
instr.value.kind === "MethodCall"
|
||||
instr.value.kind === 'CallExpression' ||
|
||||
instr.value.kind === 'MethodCall'
|
||||
) {
|
||||
const id =
|
||||
instr.value.kind === "CallExpression"
|
||||
instr.value.kind === 'CallExpression'
|
||||
? instr.value.callee.identifier.id
|
||||
: instr.value.property.identifier.id;
|
||||
|
||||
const manualMemo = sidemap.manualMemos.get(id);
|
||||
if (manualMemo != null) {
|
||||
const { fnPlace, depsList } = extractManualMemoizationArgs(
|
||||
const {fnPlace, depsList} = extractManualMemoizationArgs(
|
||||
instr as TInstruction<CallExpression> | TInstruction<MethodCall>,
|
||||
manualMemo.kind,
|
||||
sidemap
|
||||
sidemap,
|
||||
);
|
||||
instr.value = getManualMemoizationReplacement(
|
||||
fnPlace,
|
||||
instr.value.loc,
|
||||
manualMemo.kind
|
||||
manualMemo.kind,
|
||||
);
|
||||
if (isValidationEnabled) {
|
||||
/**
|
||||
|
|
@ -404,10 +404,10 @@ export function dropManualMemoization(func: HIRFunction): void {
|
|||
});
|
||||
}
|
||||
const memoDecl: Place =
|
||||
manualMemo.kind === "useMemo"
|
||||
manualMemo.kind === 'useMemo'
|
||||
? instr.lvalue
|
||||
: {
|
||||
kind: "Identifier",
|
||||
kind: 'Identifier',
|
||||
identifier: fnPlace.identifier,
|
||||
effect: Effect.Unknown,
|
||||
reactive: false,
|
||||
|
|
@ -419,7 +419,7 @@ export function dropManualMemoization(func: HIRFunction): void {
|
|||
func.env,
|
||||
depsList,
|
||||
memoDecl,
|
||||
nextManualMemoId++
|
||||
nextManualMemoId++,
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import {
|
|||
Instruction,
|
||||
isPrimitiveType,
|
||||
Place,
|
||||
} from "../HIR/HIR";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
} from '../HIR/HIR';
|
||||
import DisjointSet from '../Utils/DisjointSet';
|
||||
|
||||
export type AliasSet = Set<Identifier>;
|
||||
|
||||
|
|
@ -29,34 +29,34 @@ export function inferAliases(func: HIRFunction): DisjointSet<Identifier> {
|
|||
|
||||
function inferInstr(
|
||||
instr: Instruction,
|
||||
aliases: DisjointSet<Identifier>
|
||||
aliases: DisjointSet<Identifier>,
|
||||
): void {
|
||||
const { lvalue, value: instrValue } = instr;
|
||||
const {lvalue, value: instrValue} = instr;
|
||||
let alias: Place | null = null;
|
||||
switch (instrValue.kind) {
|
||||
case "LoadLocal":
|
||||
case "LoadContext": {
|
||||
case 'LoadLocal':
|
||||
case 'LoadContext': {
|
||||
if (isPrimitiveType(instrValue.place.identifier)) {
|
||||
return;
|
||||
}
|
||||
alias = instrValue.place;
|
||||
break;
|
||||
}
|
||||
case "StoreLocal":
|
||||
case "StoreContext": {
|
||||
case 'StoreLocal':
|
||||
case 'StoreContext': {
|
||||
alias = instrValue.value;
|
||||
break;
|
||||
}
|
||||
case "Destructure": {
|
||||
case 'Destructure': {
|
||||
alias = instrValue.value;
|
||||
break;
|
||||
}
|
||||
case "ComputedLoad":
|
||||
case "PropertyLoad": {
|
||||
case 'ComputedLoad':
|
||||
case 'PropertyLoad': {
|
||||
alias = instrValue.object;
|
||||
break;
|
||||
}
|
||||
case "TypeCastExpression": {
|
||||
case 'TypeCastExpression': {
|
||||
alias = instrValue.value;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { HIRFunction, Identifier } from "../HIR/HIR";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
import {HIRFunction, Identifier} from '../HIR/HIR';
|
||||
import DisjointSet from '../Utils/DisjointSet';
|
||||
|
||||
export function inferAliasForPhis(
|
||||
func: HIRFunction,
|
||||
aliases: DisjointSet<Identifier>
|
||||
aliases: DisjointSet<Identifier>,
|
||||
): void {
|
||||
for (const [_, block] of func.body.blocks) {
|
||||
for (const phi of block.phis) {
|
||||
|
|
|
|||
|
|
@ -11,20 +11,20 @@ import {
|
|||
Identifier,
|
||||
InstructionId,
|
||||
Place,
|
||||
} from "../HIR/HIR";
|
||||
} from '../HIR/HIR';
|
||||
import {
|
||||
eachInstructionLValue,
|
||||
eachInstructionValueOperand,
|
||||
} from "../HIR/visitors";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
} from '../HIR/visitors';
|
||||
import DisjointSet from '../Utils/DisjointSet';
|
||||
|
||||
export function inferAliasForStores(
|
||||
func: HIRFunction,
|
||||
aliases: DisjointSet<Identifier>
|
||||
aliases: DisjointSet<Identifier>,
|
||||
): void {
|
||||
for (const [_, block] of func.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
const { value, lvalue } = instr;
|
||||
const {value, lvalue} = instr;
|
||||
const isStore =
|
||||
lvalue.effect === Effect.Store ||
|
||||
/*
|
||||
|
|
@ -32,7 +32,7 @@ export function inferAliasForStores(
|
|||
* as Effect.Store.
|
||||
*/
|
||||
![...eachInstructionValueOperand(value)].every(
|
||||
(operand) => operand.effect !== Effect.Store
|
||||
operand => operand.effect !== Effect.Store,
|
||||
);
|
||||
|
||||
if (!isStore) {
|
||||
|
|
@ -57,7 +57,7 @@ function maybeAlias(
|
|||
aliases: DisjointSet<Identifier>,
|
||||
lvalue: Place,
|
||||
rvalue: Place,
|
||||
id: InstructionId
|
||||
id: InstructionId,
|
||||
): void {
|
||||
if (
|
||||
lvalue.identifier.mutableRange.end > id + 1 ||
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { Effect, HIRFunction, Identifier, Place } from "../HIR";
|
||||
import {Effect, HIRFunction, Identifier, Place} from '../HIR';
|
||||
import {
|
||||
eachInstructionValueOperand,
|
||||
eachTerminalOperand,
|
||||
} from "../HIR/visitors";
|
||||
import { IdentifierState } from "./AnalyseFunctions";
|
||||
} from '../HIR/visitors';
|
||||
import {IdentifierState} from './AnalyseFunctions';
|
||||
|
||||
/*
|
||||
* This pass infers which of the given function's context (free) variables
|
||||
|
|
@ -61,24 +61,24 @@ export function inferMutableContextVariables(fn: HIRFunction): void {
|
|||
for (const [, block] of fn.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
switch (instr.value.kind) {
|
||||
case "PropertyLoad": {
|
||||
case 'PropertyLoad': {
|
||||
state.declareProperty(
|
||||
instr.lvalue,
|
||||
instr.value.object,
|
||||
instr.value.property
|
||||
instr.value.property,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "ComputedLoad": {
|
||||
case 'ComputedLoad': {
|
||||
/*
|
||||
* The path is set to an empty string as the path doesn't really
|
||||
* matter for a computed load.
|
||||
*/
|
||||
state.declareProperty(instr.lvalue, instr.value.object, "");
|
||||
state.declareProperty(instr.lvalue, instr.value.object, '');
|
||||
break;
|
||||
}
|
||||
case "LoadLocal":
|
||||
case "LoadContext": {
|
||||
case 'LoadLocal':
|
||||
case 'LoadContext': {
|
||||
if (instr.lvalue.identifier.name === null) {
|
||||
state.declareTemporary(instr.lvalue, instr.value.place);
|
||||
}
|
||||
|
|
@ -105,7 +105,7 @@ export function inferMutableContextVariables(fn: HIRFunction): void {
|
|||
function visitOperand(
|
||||
state: IdentifierState,
|
||||
knownMutatedIdentifiers: Set<Identifier>,
|
||||
operand: Place
|
||||
operand: Place,
|
||||
): void {
|
||||
const resolved = state.resolve(operand.identifier);
|
||||
if (operand.effect === Effect.Mutate || operand.effect === Effect.Store) {
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ import {
|
|||
InstructionKind,
|
||||
makeInstructionId,
|
||||
Place,
|
||||
} from "../HIR/HIR";
|
||||
import { printPlace } from "../HIR/PrintHIR";
|
||||
} from '../HIR/HIR';
|
||||
import {printPlace} from '../HIR/PrintHIR';
|
||||
import {
|
||||
eachInstructionLValue,
|
||||
eachInstructionOperand,
|
||||
eachTerminalOperand,
|
||||
} from "../HIR/visitors";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
} from '../HIR/visitors';
|
||||
import {assertExhaustive} from '../Utils/utils';
|
||||
|
||||
/*
|
||||
* For each usage of a value in the given function, determines if the usage
|
||||
|
|
@ -72,7 +72,7 @@ function infer(place: Place, instrId: InstructionId): void {
|
|||
function inferPlace(
|
||||
place: Place,
|
||||
instrId: InstructionId,
|
||||
inferMutableRangeForStores: boolean
|
||||
inferMutableRangeForStores: boolean,
|
||||
): void {
|
||||
switch (place.effect) {
|
||||
case Effect.Unknown: {
|
||||
|
|
@ -99,7 +99,7 @@ function inferPlace(
|
|||
|
||||
export function inferMutableLifetimes(
|
||||
func: HIRFunction,
|
||||
inferMutableRangeForStores: boolean
|
||||
inferMutableRangeForStores: boolean,
|
||||
): void {
|
||||
/*
|
||||
* Context variables only appear to mutate where they are assigned, but we need
|
||||
|
|
@ -125,7 +125,7 @@ export function inferMutableLifetimes(
|
|||
phi.id.mutableRange.start = operand.mutableRange.start;
|
||||
} else {
|
||||
phi.id.mutableRange.start = makeInstructionId(
|
||||
Math.min(phi.id.mutableRange.start, operand.mutableRange.start)
|
||||
Math.min(phi.id.mutableRange.start, operand.mutableRange.start),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -153,23 +153,23 @@ export function inferMutableLifetimes(
|
|||
}
|
||||
|
||||
if (
|
||||
instr.value.kind === "DeclareContext" ||
|
||||
(instr.value.kind === "StoreContext" &&
|
||||
instr.value.kind === 'DeclareContext' ||
|
||||
(instr.value.kind === 'StoreContext' &&
|
||||
instr.value.lvalue.kind !== InstructionKind.Reassign)
|
||||
) {
|
||||
// Save declarations of context variables
|
||||
contextVariableDeclarationInstructions.set(
|
||||
instr.value.lvalue.place.identifier,
|
||||
instr.id
|
||||
instr.id,
|
||||
);
|
||||
} else if (instr.value.kind === "StoreContext") {
|
||||
} else if (instr.value.kind === 'StoreContext') {
|
||||
/*
|
||||
* Else this is a reassignment, extend the range from the declaration (if present).
|
||||
* Note that declarations may not be present for context variables that are reassigned
|
||||
* within a function expression before (or without) a read of the same variable
|
||||
*/
|
||||
const declaration = contextVariableDeclarationInstructions.get(
|
||||
instr.value.lvalue.place.identifier
|
||||
instr.value.lvalue.place.identifier,
|
||||
);
|
||||
if (declaration != null) {
|
||||
const range = instr.value.lvalue.place.identifier.mutableRange;
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { HIRFunction, Identifier } from "../HIR/HIR";
|
||||
import { inferAliases } from "./InferAlias";
|
||||
import { inferAliasForPhis } from "./InferAliasForPhis";
|
||||
import { inferAliasForStores } from "./InferAliasForStores";
|
||||
import { inferMutableLifetimes } from "./InferMutableLifetimes";
|
||||
import { inferMutableRangesForAlias } from "./InferMutableRangesForAlias";
|
||||
import { inferTryCatchAliases } from "./InferTryCatchAliases";
|
||||
import {HIRFunction, Identifier} from '../HIR/HIR';
|
||||
import {inferAliases} from './InferAlias';
|
||||
import {inferAliasForPhis} from './InferAliasForPhis';
|
||||
import {inferAliasForStores} from './InferAliasForStores';
|
||||
import {inferMutableLifetimes} from './InferMutableLifetimes';
|
||||
import {inferMutableRangesForAlias} from './InferMutableRangesForAlias';
|
||||
import {inferTryCatchAliases} from './InferTryCatchAliases';
|
||||
|
||||
export function inferMutableRanges(ir: HIRFunction): void {
|
||||
// Infer mutable ranges for non fields
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { HIRFunction, Identifier, InstructionId } from "../HIR/HIR";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
import {HIRFunction, Identifier, InstructionId} from '../HIR/HIR';
|
||||
import DisjointSet from '../Utils/DisjointSet';
|
||||
|
||||
export function inferMutableRangesForAlias(
|
||||
_fn: HIRFunction,
|
||||
aliases: DisjointSet<Identifier>
|
||||
aliases: DisjointSet<Identifier>,
|
||||
): void {
|
||||
const aliasSets = aliases.buildSets();
|
||||
for (const aliasSet of aliasSets) {
|
||||
|
|
@ -19,7 +19,7 @@ export function inferMutableRangesForAlias(
|
|||
* mutated.
|
||||
*/
|
||||
const mutatingIdentifiers = [...aliasSet].filter(
|
||||
(id) => id.mutableRange.end - id.mutableRange.start > 1
|
||||
id => id.mutableRange.end - id.mutableRange.start > 1,
|
||||
);
|
||||
|
||||
if (mutatingIdentifiers.length > 0) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { CompilerError } from "..";
|
||||
import {CompilerError} from '..';
|
||||
import {
|
||||
BlockId,
|
||||
Effect,
|
||||
|
|
@ -17,19 +17,19 @@ import {
|
|||
getHookKind,
|
||||
isStableType,
|
||||
isUseOperator,
|
||||
} from "../HIR";
|
||||
import { PostDominator } from "../HIR/Dominator";
|
||||
} from '../HIR';
|
||||
import {PostDominator} from '../HIR/Dominator';
|
||||
import {
|
||||
eachInstructionLValue,
|
||||
eachInstructionValueOperand,
|
||||
eachTerminalOperand,
|
||||
} from "../HIR/visitors";
|
||||
} from '../HIR/visitors';
|
||||
import {
|
||||
findDisjointMutableValues,
|
||||
isMutable,
|
||||
} from "../ReactiveScopes/InferReactiveScopeVariables";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
} from '../ReactiveScopes/InferReactiveScopeVariables';
|
||||
import DisjointSet from '../Utils/DisjointSet';
|
||||
import {assertExhaustive} from '../Utils/utils';
|
||||
|
||||
/*
|
||||
* Infers which `Place`s are reactive, ie may *semantically* change
|
||||
|
|
@ -112,7 +112,7 @@ import { assertExhaustive } from "../Utils/utils";
|
|||
export function inferReactivePlaces(fn: HIRFunction): void {
|
||||
const reactiveIdentifiers = new ReactivityMap(findDisjointMutableValues(fn));
|
||||
for (const param of fn.params) {
|
||||
const place = param.kind === "Identifier" ? param : param.place;
|
||||
const place = param.kind === 'Identifier' ? param : param.place;
|
||||
reactiveIdentifiers.markReactive(place);
|
||||
}
|
||||
|
||||
|
|
@ -130,14 +130,14 @@ export function inferReactivePlaces(fn: HIRFunction): void {
|
|||
for (const blockId of controlBlocks) {
|
||||
const controlBlock = fn.body.blocks.get(blockId)!;
|
||||
switch (controlBlock.terminal.kind) {
|
||||
case "if":
|
||||
case "branch": {
|
||||
case 'if':
|
||||
case 'branch': {
|
||||
if (reactiveIdentifiers.isReactive(controlBlock.terminal.test)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "switch": {
|
||||
case 'switch': {
|
||||
if (reactiveIdentifiers.isReactive(controlBlock.terminal.test)) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -185,7 +185,7 @@ export function inferReactivePlaces(fn: HIRFunction): void {
|
|||
}
|
||||
}
|
||||
for (const instruction of block.instructions) {
|
||||
const { value } = instruction;
|
||||
const {value} = instruction;
|
||||
let hasReactiveInput = false;
|
||||
/*
|
||||
* NOTE: we want to mark all operands as reactive or not, so we
|
||||
|
|
@ -204,13 +204,13 @@ export function inferReactivePlaces(fn: HIRFunction): void {
|
|||
* but we are conservative and assume that the value could be reactive.
|
||||
*/
|
||||
if (
|
||||
value.kind === "CallExpression" &&
|
||||
value.kind === 'CallExpression' &&
|
||||
(getHookKind(fn.env, value.callee.identifier) != null ||
|
||||
isUseOperator(value.callee.identifier))
|
||||
) {
|
||||
hasReactiveInput = true;
|
||||
} else if (
|
||||
value.kind === "MethodCall" &&
|
||||
value.kind === 'MethodCall' &&
|
||||
(getHookKind(fn.env, value.property.identifier) != null ||
|
||||
isUseOperator(value.property.identifier))
|
||||
) {
|
||||
|
|
@ -248,7 +248,7 @@ export function inferReactivePlaces(fn: HIRFunction): void {
|
|||
}
|
||||
case Effect.Unknown: {
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Unexpected unknown effect",
|
||||
reason: 'Unexpected unknown effect',
|
||||
description: null,
|
||||
loc: operand.loc,
|
||||
suggestions: null,
|
||||
|
|
@ -257,7 +257,7 @@ export function inferReactivePlaces(fn: HIRFunction): void {
|
|||
default: {
|
||||
assertExhaustive(
|
||||
operand.effect,
|
||||
`Unexpected effect kind \`${operand.effect}\``
|
||||
`Unexpected effect kind \`${operand.effect}\``,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -265,25 +265,25 @@ export function inferReactivePlaces(fn: HIRFunction): void {
|
|||
}
|
||||
|
||||
switch (value.kind) {
|
||||
case "LoadLocal": {
|
||||
case 'LoadLocal': {
|
||||
identifierMapping.set(
|
||||
instruction.lvalue.identifier,
|
||||
value.place.identifier
|
||||
value.place.identifier,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "PropertyLoad":
|
||||
case "ComputedLoad": {
|
||||
case 'PropertyLoad':
|
||||
case 'ComputedLoad': {
|
||||
const resolvedId =
|
||||
identifierMapping.get(value.object.identifier) ??
|
||||
value.object.identifier;
|
||||
identifierMapping.set(instruction.lvalue.identifier, resolvedId);
|
||||
break;
|
||||
}
|
||||
case "LoadContext": {
|
||||
case 'LoadContext': {
|
||||
identifierMapping.set(
|
||||
instruction.lvalue.identifier,
|
||||
value.place.identifier
|
||||
value.place.identifier,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
|
@ -304,7 +304,7 @@ export function inferReactivePlaces(fn: HIRFunction): void {
|
|||
function postDominatorFrontier(
|
||||
fn: HIRFunction,
|
||||
postDominators: PostDominator<BlockId>,
|
||||
targetId: BlockId
|
||||
targetId: BlockId,
|
||||
): Set<BlockId> {
|
||||
const visited = new Set<BlockId>();
|
||||
const frontier = new Set<BlockId>();
|
||||
|
|
@ -328,7 +328,7 @@ function postDominatorFrontier(
|
|||
function postDominatorsOf(
|
||||
fn: HIRFunction,
|
||||
postDominators: PostDominator<BlockId>,
|
||||
targetId: BlockId
|
||||
targetId: BlockId,
|
||||
): Set<BlockId> {
|
||||
const result = new Set<BlockId>();
|
||||
const visited = new Set<BlockId>();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -5,8 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { BlockId, HIRFunction, Identifier } from "../HIR";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
import {BlockId, HIRFunction, Identifier} from '../HIR';
|
||||
import DisjointSet from '../Utils/DisjointSet';
|
||||
|
||||
/*
|
||||
* Any values created within a try/catch block could be aliased to the try handler.
|
||||
|
|
@ -16,19 +16,19 @@ import DisjointSet from "../Utils/DisjointSet";
|
|||
*/
|
||||
export function inferTryCatchAliases(
|
||||
fn: HIRFunction,
|
||||
aliases: DisjointSet<Identifier>
|
||||
aliases: DisjointSet<Identifier>,
|
||||
): void {
|
||||
const handlerParams: Map<BlockId, Identifier> = new Map();
|
||||
for (const [_, block] of fn.body.blocks) {
|
||||
if (
|
||||
block.terminal.kind === "try" &&
|
||||
block.terminal.kind === 'try' &&
|
||||
block.terminal.handlerBinding !== null
|
||||
) {
|
||||
handlerParams.set(
|
||||
block.terminal.handler,
|
||||
block.terminal.handlerBinding.identifier
|
||||
block.terminal.handlerBinding.identifier,
|
||||
);
|
||||
} else if (block.terminal.kind === "maybe-throw") {
|
||||
} else if (block.terminal.kind === 'maybe-throw') {
|
||||
const handlerParam = handlerParams.get(block.terminal.handler);
|
||||
if (handlerParam === undefined) {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ import {
|
|||
makeType,
|
||||
promoteTemporary,
|
||||
reversePostorderBlocks,
|
||||
} from "../HIR";
|
||||
import { markInstructionIds, markPredecessors } from "../HIR/HIRBuilder";
|
||||
import { eachInstructionValueOperand } from "../HIR/visitors";
|
||||
import { retainWhere } from "../Utils/utils";
|
||||
} from '../HIR';
|
||||
import {markInstructionIds, markPredecessors} from '../HIR/HIRBuilder';
|
||||
import {eachInstructionValueOperand} from '../HIR/visitors';
|
||||
import {retainWhere} from '../Utils/utils';
|
||||
|
||||
/*
|
||||
* Inlines immediately invoked function expressions (IIFEs) to allow more fine-grained memoization
|
||||
|
|
@ -72,7 +72,7 @@ import { retainWhere } from "../Utils/utils";
|
|||
* block (code following the CallExpression).
|
||||
*/
|
||||
export function inlineImmediatelyInvokedFunctionExpressions(
|
||||
fn: HIRFunction
|
||||
fn: HIRFunction,
|
||||
): void {
|
||||
// Track all function expressions that are assigned to a temporary
|
||||
const functions = new Map<IdentifierId, FunctionExpression>();
|
||||
|
|
@ -91,13 +91,13 @@ export function inlineImmediatelyInvokedFunctionExpressions(
|
|||
for (let ii = 0; ii < block.instructions.length; ii++) {
|
||||
const instr = block.instructions[ii]!;
|
||||
switch (instr.value.kind) {
|
||||
case "FunctionExpression": {
|
||||
case 'FunctionExpression': {
|
||||
if (instr.lvalue.identifier.name === null) {
|
||||
functions.set(instr.lvalue.identifier.id, instr.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "CallExpression": {
|
||||
case 'CallExpression': {
|
||||
if (instr.value.args.length !== 0) {
|
||||
// We don't support inlining when there are arguments
|
||||
continue;
|
||||
|
|
@ -146,7 +146,7 @@ export function inlineImmediatelyInvokedFunctionExpressions(
|
|||
const newTerminal: LabelTerminal = {
|
||||
block: body.loweredFunc.func.body.entry,
|
||||
id: makeInstructionId(0),
|
||||
kind: "label",
|
||||
kind: 'label',
|
||||
fallthrough: continuationBlockId,
|
||||
loc: block.terminal.loc,
|
||||
};
|
||||
|
|
@ -193,7 +193,7 @@ export function inlineImmediatelyInvokedFunctionExpressions(
|
|||
for (const [, block] of fn.body.blocks) {
|
||||
retainWhere(
|
||||
block.instructions,
|
||||
(instr) => !inlinedFunctions.has(instr.lvalue.identifier.id)
|
||||
instr => !inlinedFunctions.has(instr.lvalue.identifier.id),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -216,10 +216,10 @@ function rewriteBlock(
|
|||
env: Environment,
|
||||
block: BasicBlock,
|
||||
returnTarget: BlockId,
|
||||
returnValue: Place
|
||||
returnValue: Place,
|
||||
): void {
|
||||
const { terminal } = block;
|
||||
if (terminal.kind !== "return") {
|
||||
const {terminal} = block;
|
||||
if (terminal.kind !== 'return') {
|
||||
return;
|
||||
}
|
||||
block.instructions.push({
|
||||
|
|
@ -238,20 +238,20 @@ function rewriteBlock(
|
|||
type: makeType(),
|
||||
loc: terminal.loc,
|
||||
},
|
||||
kind: "Identifier",
|
||||
kind: 'Identifier',
|
||||
reactive: false,
|
||||
loc: terminal.loc,
|
||||
},
|
||||
value: {
|
||||
kind: "StoreLocal",
|
||||
lvalue: { kind: InstructionKind.Reassign, place: { ...returnValue } },
|
||||
kind: 'StoreLocal',
|
||||
lvalue: {kind: InstructionKind.Reassign, place: {...returnValue}},
|
||||
value: terminal.value,
|
||||
type: null,
|
||||
loc: terminal.loc,
|
||||
},
|
||||
});
|
||||
block.terminal = {
|
||||
kind: "goto",
|
||||
kind: 'goto',
|
||||
block: returnTarget,
|
||||
id: makeInstructionId(0),
|
||||
variant: GotoVariant.Break,
|
||||
|
|
@ -262,7 +262,7 @@ function rewriteBlock(
|
|||
function declareTemporary(
|
||||
env: Environment,
|
||||
block: BasicBlock,
|
||||
result: Place
|
||||
result: Place,
|
||||
): void {
|
||||
block.instructions.push({
|
||||
id: makeInstructionId(0),
|
||||
|
|
@ -280,12 +280,12 @@ function declareTemporary(
|
|||
type: makeType(),
|
||||
loc: result.loc,
|
||||
},
|
||||
kind: "Identifier",
|
||||
kind: 'Identifier',
|
||||
reactive: false,
|
||||
loc: GeneratedSource,
|
||||
},
|
||||
value: {
|
||||
kind: "DeclareLocal",
|
||||
kind: 'DeclareLocal',
|
||||
lvalue: {
|
||||
place: result,
|
||||
kind: InstructionKind.Let,
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
export { default as analyseFunctions } from "./AnalyseFunctions";
|
||||
export { dropManualMemoization } from "./DropManualMemoization";
|
||||
export { inferMutableRanges } from "./InferMutableRanges";
|
||||
export { inferReactivePlaces } from "./InferReactivePlaces";
|
||||
export { default as inferReferenceEffects } from "./InferReferenceEffects";
|
||||
export { inlineImmediatelyInvokedFunctionExpressions } from "./InlineImmediatelyInvokedFunctionExpressions";
|
||||
export {default as analyseFunctions} from './AnalyseFunctions';
|
||||
export {dropManualMemoization} from './DropManualMemoization';
|
||||
export {inferMutableRanges} from './InferMutableRanges';
|
||||
export {inferReactivePlaces} from './InferReactivePlaces';
|
||||
export {default as inferReferenceEffects} from './InferReferenceEffects';
|
||||
export {inlineImmediatelyInvokedFunctionExpressions} from './InlineImmediatelyInvokedFunctionExpressions';
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { isValidIdentifier } from "@babel/types";
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import {isValidIdentifier} from '@babel/types';
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {
|
||||
Environment,
|
||||
GotoVariant,
|
||||
|
|
@ -24,13 +24,13 @@ import {
|
|||
markPredecessors,
|
||||
mergeConsecutiveBlocks,
|
||||
reversePostorderBlocks,
|
||||
} from "../HIR";
|
||||
} from '../HIR';
|
||||
import {
|
||||
removeDeadDoWhileStatements,
|
||||
removeUnnecessaryTryCatch,
|
||||
removeUnreachableForUpdates,
|
||||
} from "../HIR/HIRBuilder";
|
||||
import { eliminateRedundantPhi } from "../SSA";
|
||||
} from '../HIR/HIRBuilder';
|
||||
import {eliminateRedundantPhi} from '../SSA';
|
||||
|
||||
/*
|
||||
* Applies constant propagation/folding to the given function. The approach is
|
||||
|
|
@ -105,7 +105,7 @@ function constantPropagationImpl(fn: HIRFunction, constants: Constants): void {
|
|||
|
||||
function applyConstantPropagation(
|
||||
fn: HIRFunction,
|
||||
constants: Constants
|
||||
constants: Constants,
|
||||
): boolean {
|
||||
let hasChanges = false;
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
|
|
@ -122,7 +122,7 @@ function applyConstantPropagation(
|
|||
}
|
||||
|
||||
for (let i = 0; i < block.instructions.length; i++) {
|
||||
if (block.kind === "sequence" && i === block.instructions.length - 1) {
|
||||
if (block.kind === 'sequence' && i === block.instructions.length - 1) {
|
||||
/*
|
||||
* evaluating the last value of a value block can break order of evaluation,
|
||||
* skip these instructions
|
||||
|
|
@ -138,15 +138,15 @@ function applyConstantPropagation(
|
|||
|
||||
const terminal = block.terminal;
|
||||
switch (terminal.kind) {
|
||||
case "if": {
|
||||
case 'if': {
|
||||
const testValue = read(constants, terminal.test);
|
||||
if (testValue !== null && testValue.kind === "Primitive") {
|
||||
if (testValue !== null && testValue.kind === 'Primitive') {
|
||||
hasChanges = true;
|
||||
const targetBlockId = testValue.value
|
||||
? terminal.consequent
|
||||
: terminal.alternate;
|
||||
block.terminal = {
|
||||
kind: "goto",
|
||||
kind: 'goto',
|
||||
variant: GotoVariant.Break,
|
||||
block: targetBlockId,
|
||||
id: terminal.id,
|
||||
|
|
@ -188,9 +188,9 @@ function evaluatePhi(phi: Phi, constants: Constants): Constant | null {
|
|||
}
|
||||
|
||||
switch (operandValue.kind) {
|
||||
case "Primitive": {
|
||||
CompilerError.invariant(value.kind === "Primitive", {
|
||||
reason: "value kind expected to be Primitive",
|
||||
case 'Primitive': {
|
||||
CompilerError.invariant(value.kind === 'Primitive', {
|
||||
reason: 'value kind expected to be Primitive',
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
|
|
@ -201,9 +201,9 @@ function evaluatePhi(phi: Phi, constants: Constants): Constant | null {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case "LoadGlobal": {
|
||||
CompilerError.invariant(value.kind === "LoadGlobal", {
|
||||
reason: "value kind expected to be LoadGlobal",
|
||||
case 'LoadGlobal': {
|
||||
CompilerError.invariant(value.kind === 'LoadGlobal', {
|
||||
reason: 'value kind expected to be LoadGlobal',
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
|
|
@ -225,26 +225,26 @@ function evaluatePhi(phi: Phi, constants: Constants): Constant | null {
|
|||
function evaluateInstruction(
|
||||
env: Environment,
|
||||
constants: Constants,
|
||||
instr: Instruction
|
||||
instr: Instruction,
|
||||
): Constant | null {
|
||||
const value = instr.value;
|
||||
switch (value.kind) {
|
||||
case "Primitive": {
|
||||
case 'Primitive': {
|
||||
return value;
|
||||
}
|
||||
case "LoadGlobal": {
|
||||
case 'LoadGlobal': {
|
||||
return value;
|
||||
}
|
||||
case "ComputedLoad": {
|
||||
case 'ComputedLoad': {
|
||||
const property = read(constants, value.property);
|
||||
if (
|
||||
property !== null &&
|
||||
property.kind === "Primitive" &&
|
||||
typeof property.value === "string" &&
|
||||
property.kind === 'Primitive' &&
|
||||
typeof property.value === 'string' &&
|
||||
isValidIdentifier(property.value)
|
||||
) {
|
||||
const nextValue: InstructionValue = {
|
||||
kind: "PropertyLoad",
|
||||
kind: 'PropertyLoad',
|
||||
loc: value.loc,
|
||||
property: property.value,
|
||||
object: value.object,
|
||||
|
|
@ -253,16 +253,16 @@ function evaluateInstruction(
|
|||
}
|
||||
return null;
|
||||
}
|
||||
case "ComputedStore": {
|
||||
case 'ComputedStore': {
|
||||
const property = read(constants, value.property);
|
||||
if (
|
||||
property !== null &&
|
||||
property.kind === "Primitive" &&
|
||||
typeof property.value === "string" &&
|
||||
property.kind === 'Primitive' &&
|
||||
typeof property.value === 'string' &&
|
||||
isValidIdentifier(property.value)
|
||||
) {
|
||||
const nextValue: InstructionValue = {
|
||||
kind: "PropertyStore",
|
||||
kind: 'PropertyStore',
|
||||
loc: value.loc,
|
||||
property: property.value,
|
||||
object: value.object,
|
||||
|
|
@ -272,18 +272,18 @@ function evaluateInstruction(
|
|||
}
|
||||
return null;
|
||||
}
|
||||
case "PostfixUpdate": {
|
||||
case 'PostfixUpdate': {
|
||||
const previous = read(constants, value.value);
|
||||
if (
|
||||
previous !== null &&
|
||||
previous.kind === "Primitive" &&
|
||||
typeof previous.value === "number"
|
||||
previous.kind === 'Primitive' &&
|
||||
typeof previous.value === 'number'
|
||||
) {
|
||||
const next =
|
||||
value.operation === "++" ? previous.value + 1 : previous.value - 1;
|
||||
value.operation === '++' ? previous.value + 1 : previous.value - 1;
|
||||
// Store the updated value
|
||||
constants.set(value.lvalue.identifier.id, {
|
||||
kind: "Primitive",
|
||||
kind: 'Primitive',
|
||||
value: next,
|
||||
loc: value.loc,
|
||||
});
|
||||
|
|
@ -292,17 +292,17 @@ function evaluateInstruction(
|
|||
}
|
||||
return null;
|
||||
}
|
||||
case "PrefixUpdate": {
|
||||
case 'PrefixUpdate': {
|
||||
const previous = read(constants, value.value);
|
||||
if (
|
||||
previous !== null &&
|
||||
previous.kind === "Primitive" &&
|
||||
typeof previous.value === "number"
|
||||
previous.kind === 'Primitive' &&
|
||||
typeof previous.value === 'number'
|
||||
) {
|
||||
const next: Primitive = {
|
||||
kind: "Primitive",
|
||||
kind: 'Primitive',
|
||||
value:
|
||||
value.operation === "++" ? previous.value + 1 : previous.value - 1,
|
||||
value.operation === '++' ? previous.value + 1 : previous.value - 1,
|
||||
loc: value.loc,
|
||||
};
|
||||
// Store and return the updated value
|
||||
|
|
@ -311,13 +311,13 @@ function evaluateInstruction(
|
|||
}
|
||||
return null;
|
||||
}
|
||||
case "UnaryExpression": {
|
||||
case 'UnaryExpression': {
|
||||
switch (value.operator) {
|
||||
case "!": {
|
||||
case '!': {
|
||||
const operand = read(constants, value.value);
|
||||
if (operand !== null && operand.kind === "Primitive") {
|
||||
if (operand !== null && operand.kind === 'Primitive') {
|
||||
const result: Primitive = {
|
||||
kind: "Primitive",
|
||||
kind: 'Primitive',
|
||||
value: !operand.value,
|
||||
loc: value.loc,
|
||||
};
|
||||
|
|
@ -330,135 +330,135 @@ function evaluateInstruction(
|
|||
return null;
|
||||
}
|
||||
}
|
||||
case "BinaryExpression": {
|
||||
case 'BinaryExpression': {
|
||||
const lhsValue = read(constants, value.left);
|
||||
const rhsValue = read(constants, value.right);
|
||||
if (
|
||||
lhsValue !== null &&
|
||||
rhsValue !== null &&
|
||||
lhsValue.kind === "Primitive" &&
|
||||
rhsValue.kind === "Primitive"
|
||||
lhsValue.kind === 'Primitive' &&
|
||||
rhsValue.kind === 'Primitive'
|
||||
) {
|
||||
const lhs = lhsValue.value;
|
||||
const rhs = rhsValue.value;
|
||||
let result: Primitive | null = null;
|
||||
switch (value.operator) {
|
||||
case "+": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs + rhs, loc: value.loc };
|
||||
} else if (typeof lhs === "string" && typeof rhs === "string") {
|
||||
result = { kind: "Primitive", value: lhs + rhs, loc: value.loc };
|
||||
case '+': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs + rhs, loc: value.loc};
|
||||
} else if (typeof lhs === 'string' && typeof rhs === 'string') {
|
||||
result = {kind: 'Primitive', value: lhs + rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "-": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs - rhs, loc: value.loc };
|
||||
case '-': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs - rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "*": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs * rhs, loc: value.loc };
|
||||
case '*': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs * rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "/": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs / rhs, loc: value.loc };
|
||||
case '/': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs / rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "|": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs | rhs, loc: value.loc };
|
||||
case '|': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs | rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "&": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs & rhs, loc: value.loc };
|
||||
case '&': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs & rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "^": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs ^ rhs, loc: value.loc };
|
||||
case '^': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs ^ rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "<<": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs << rhs, loc: value.loc };
|
||||
case '<<': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs << rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ">>": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs >> rhs, loc: value.loc };
|
||||
case '>>': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs >> rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ">>>": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
case '>>>': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {
|
||||
kind: "Primitive",
|
||||
kind: 'Primitive',
|
||||
value: lhs >>> rhs,
|
||||
loc: value.loc,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "%": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs % rhs, loc: value.loc };
|
||||
case '%': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs % rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "**": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs ** rhs, loc: value.loc };
|
||||
case '**': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs ** rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "<": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs < rhs, loc: value.loc };
|
||||
case '<': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs < rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "<=": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs <= rhs, loc: value.loc };
|
||||
case '<=': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs <= rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ">": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs > rhs, loc: value.loc };
|
||||
case '>': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs > rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ">=": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs >= rhs, loc: value.loc };
|
||||
case '>=': {
|
||||
if (typeof lhs === 'number' && typeof rhs === 'number') {
|
||||
result = {kind: 'Primitive', value: lhs >= rhs, loc: value.loc};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "==": {
|
||||
result = { kind: "Primitive", value: lhs == rhs, loc: value.loc };
|
||||
case '==': {
|
||||
result = {kind: 'Primitive', value: lhs == rhs, loc: value.loc};
|
||||
break;
|
||||
}
|
||||
case "===": {
|
||||
result = { kind: "Primitive", value: lhs === rhs, loc: value.loc };
|
||||
case '===': {
|
||||
result = {kind: 'Primitive', value: lhs === rhs, loc: value.loc};
|
||||
break;
|
||||
}
|
||||
case "!=": {
|
||||
result = { kind: "Primitive", value: lhs != rhs, loc: value.loc };
|
||||
case '!=': {
|
||||
result = {kind: 'Primitive', value: lhs != rhs, loc: value.loc};
|
||||
break;
|
||||
}
|
||||
case "!==": {
|
||||
result = { kind: "Primitive", value: lhs !== rhs, loc: value.loc };
|
||||
case '!==': {
|
||||
result = {kind: 'Primitive', value: lhs !== rhs, loc: value.loc};
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
|
@ -472,16 +472,16 @@ function evaluateInstruction(
|
|||
}
|
||||
return null;
|
||||
}
|
||||
case "PropertyLoad": {
|
||||
case 'PropertyLoad': {
|
||||
const objectValue = read(constants, value.object);
|
||||
if (objectValue !== null) {
|
||||
if (
|
||||
objectValue.kind === "Primitive" &&
|
||||
typeof objectValue.value === "string" &&
|
||||
value.property === "length"
|
||||
objectValue.kind === 'Primitive' &&
|
||||
typeof objectValue.value === 'string' &&
|
||||
value.property === 'length'
|
||||
) {
|
||||
const result: InstructionValue = {
|
||||
kind: "Primitive",
|
||||
kind: 'Primitive',
|
||||
value: objectValue.value.length,
|
||||
loc: value.loc,
|
||||
};
|
||||
|
|
@ -491,22 +491,22 @@ function evaluateInstruction(
|
|||
}
|
||||
return null;
|
||||
}
|
||||
case "LoadLocal": {
|
||||
case 'LoadLocal': {
|
||||
const placeValue = read(constants, value.place);
|
||||
if (placeValue !== null) {
|
||||
instr.value = placeValue;
|
||||
}
|
||||
return placeValue;
|
||||
}
|
||||
case "StoreLocal": {
|
||||
case 'StoreLocal': {
|
||||
const placeValue = read(constants, value.value);
|
||||
if (placeValue !== null) {
|
||||
constants.set(value.lvalue.place.identifier.id, placeValue);
|
||||
}
|
||||
return placeValue;
|
||||
}
|
||||
case "ObjectMethod":
|
||||
case "FunctionExpression": {
|
||||
case 'ObjectMethod':
|
||||
case 'FunctionExpression': {
|
||||
constantPropagationImpl(value.loweredFunc.func, constants);
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ import {
|
|||
InstructionKind,
|
||||
InstructionValue,
|
||||
ObjectPattern,
|
||||
} from "../HIR";
|
||||
} from '../HIR';
|
||||
import {
|
||||
eachInstructionValueOperand,
|
||||
eachPatternOperand,
|
||||
eachTerminalOperand,
|
||||
} from "../HIR/visitors";
|
||||
import { assertExhaustive, retainWhere } from "../Utils/utils";
|
||||
} from '../HIR/visitors';
|
||||
import {assertExhaustive, retainWhere} from '../Utils/utils';
|
||||
|
||||
/*
|
||||
* Implements dead-code elimination, eliminating instructions whose values are unused.
|
||||
|
|
@ -46,13 +46,13 @@ export function deadCodeElimination(fn: HIRFunction): void {
|
|||
block.phis.delete(phi);
|
||||
}
|
||||
}
|
||||
retainWhere(block.instructions, (instr) =>
|
||||
state.isIdOrNameUsed(instr.lvalue.identifier)
|
||||
retainWhere(block.instructions, instr =>
|
||||
state.isIdOrNameUsed(instr.lvalue.identifier),
|
||||
);
|
||||
// Rewrite retained instructions
|
||||
for (let i = 0; i < block.instructions.length; i++) {
|
||||
const isBlockValue =
|
||||
block.kind !== "block" && i === block.instructions.length - 1;
|
||||
block.kind !== 'block' && i === block.instructions.length - 1;
|
||||
if (!isBlockValue) {
|
||||
rewriteInstruction(block.instructions[i], state);
|
||||
}
|
||||
|
|
@ -122,7 +122,7 @@ function findReferencedIdentifiers(fn: HIRFunction): State {
|
|||
for (let i = block.instructions.length - 1; i >= 0; i--) {
|
||||
const instr = block.instructions[i]!;
|
||||
const isBlockValue =
|
||||
block.kind !== "block" && i === block.instructions.length - 1;
|
||||
block.kind !== 'block' && i === block.instructions.length - 1;
|
||||
|
||||
if (isBlockValue) {
|
||||
/**
|
||||
|
|
@ -140,7 +140,7 @@ function findReferencedIdentifiers(fn: HIRFunction): State {
|
|||
) {
|
||||
state.reference(instr.lvalue.identifier);
|
||||
|
||||
if (instr.value.kind === "StoreLocal") {
|
||||
if (instr.value.kind === 'StoreLocal') {
|
||||
/*
|
||||
* If this is a Let/Const declaration, mark the initializer as referenced
|
||||
* only if the ssa'ed lval is also referenced
|
||||
|
|
@ -171,25 +171,25 @@ function findReferencedIdentifiers(fn: HIRFunction): State {
|
|||
}
|
||||
|
||||
function rewriteInstruction(instr: Instruction, state: State): void {
|
||||
if (instr.value.kind === "Destructure") {
|
||||
if (instr.value.kind === 'Destructure') {
|
||||
// Remove unused lvalues
|
||||
switch (instr.value.lvalue.pattern.kind) {
|
||||
case "ArrayPattern": {
|
||||
case 'ArrayPattern': {
|
||||
/*
|
||||
* For arrays, we can only eliminate unused items from the end of the array,
|
||||
* so we iterate from the end and break once we find a used item. Note that
|
||||
* we already know at least one item is used, from the pruneableValue check.
|
||||
*/
|
||||
let nextItems: ArrayPattern["items"] | null = null;
|
||||
let nextItems: ArrayPattern['items'] | null = null;
|
||||
const originalItems = instr.value.lvalue.pattern.items;
|
||||
for (let i = originalItems.length - 1; i >= 0; i--) {
|
||||
const item = originalItems[i];
|
||||
if (item.kind === "Identifier") {
|
||||
if (item.kind === 'Identifier') {
|
||||
if (state.isIdOrNameUsed(item.identifier)) {
|
||||
nextItems = originalItems.slice(0, i + 1);
|
||||
break;
|
||||
}
|
||||
} else if (item.kind === "Spread") {
|
||||
} else if (item.kind === 'Spread') {
|
||||
if (state.isIdOrNameUsed(item.place.identifier)) {
|
||||
nextItems = originalItems.slice(0, i + 1);
|
||||
break;
|
||||
|
|
@ -201,7 +201,7 @@ function rewriteInstruction(instr: Instruction, state: State): void {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case "ObjectPattern": {
|
||||
case 'ObjectPattern': {
|
||||
/*
|
||||
* For objects we can prune any unused properties so long as there is no used rest element
|
||||
* (`const {x, ...y} = z`). If a rest element exists and is used, then nothing can be pruned
|
||||
|
|
@ -209,9 +209,9 @@ function rewriteInstruction(instr: Instruction, state: State): void {
|
|||
* In the `const {x, ...y} = z` example, removing the `x` property would mean that `y` now
|
||||
* has an `x` property, changing the semantics.
|
||||
*/
|
||||
let nextProperties: ObjectPattern["properties"] | null = null;
|
||||
let nextProperties: ObjectPattern['properties'] | null = null;
|
||||
for (const property of instr.value.lvalue.pattern.properties) {
|
||||
if (property.kind === "ObjectProperty") {
|
||||
if (property.kind === 'ObjectProperty') {
|
||||
if (state.isIdOrNameUsed(property.place.identifier)) {
|
||||
nextProperties ??= [];
|
||||
nextProperties.push(property);
|
||||
|
|
@ -233,11 +233,11 @@ function rewriteInstruction(instr: Instruction, state: State): void {
|
|||
instr.value.lvalue.pattern,
|
||||
`Unexpected pattern kind '${
|
||||
(instr.value.lvalue.pattern as any).kind
|
||||
}'`
|
||||
}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (instr.value.kind === "StoreLocal") {
|
||||
} else if (instr.value.kind === 'StoreLocal') {
|
||||
if (
|
||||
instr.value.lvalue.kind !== InstructionKind.Reassign &&
|
||||
!state.isIdUsed(instr.value.lvalue.place.identifier)
|
||||
|
|
@ -249,7 +249,7 @@ function rewriteInstruction(instr: Instruction, state: State): void {
|
|||
* that the initializer value can be DCE'd
|
||||
*/
|
||||
instr.value = {
|
||||
kind: "DeclareLocal",
|
||||
kind: 'DeclareLocal',
|
||||
lvalue: instr.value.lvalue,
|
||||
type: instr.value.type,
|
||||
loc: instr.value.loc,
|
||||
|
|
@ -264,11 +264,11 @@ function rewriteInstruction(instr: Instruction, state: State): void {
|
|||
*/
|
||||
function pruneableValue(value: InstructionValue, state: State): boolean {
|
||||
switch (value.kind) {
|
||||
case "DeclareLocal": {
|
||||
case 'DeclareLocal': {
|
||||
// Declarations are pruneable only if the named variable is never read later
|
||||
return !state.isIdOrNameUsed(value.lvalue.place.identifier);
|
||||
}
|
||||
case "StoreLocal": {
|
||||
case 'StoreLocal': {
|
||||
if (value.lvalue.kind === InstructionKind.Reassign) {
|
||||
// Reassignments can be pruned if the specific instance being assigned is never read
|
||||
return !state.isIdUsed(value.lvalue.place.identifier);
|
||||
|
|
@ -276,7 +276,7 @@ function pruneableValue(value: InstructionValue, state: State): boolean {
|
|||
// Declarations are pruneable only if the named variable is never read later
|
||||
return !state.isIdOrNameUsed(value.lvalue.place.identifier);
|
||||
}
|
||||
case "Destructure": {
|
||||
case 'Destructure': {
|
||||
let isIdOrNameUsed = false;
|
||||
let isIdUsed = false;
|
||||
for (const place of eachPatternOperand(value.lvalue.pattern)) {
|
||||
|
|
@ -295,23 +295,23 @@ function pruneableValue(value: InstructionValue, state: State): boolean {
|
|||
return !isIdOrNameUsed;
|
||||
}
|
||||
}
|
||||
case "PostfixUpdate":
|
||||
case "PrefixUpdate": {
|
||||
case 'PostfixUpdate':
|
||||
case 'PrefixUpdate': {
|
||||
// Updates are pruneable if the specific instance instance being assigned is never read
|
||||
return !state.isIdUsed(value.lvalue.identifier);
|
||||
}
|
||||
case "Debugger": {
|
||||
case 'Debugger': {
|
||||
// explicitly retain debugger statements to not break debugging workflows
|
||||
return false;
|
||||
}
|
||||
case "Await":
|
||||
case "CallExpression":
|
||||
case "ComputedDelete":
|
||||
case "ComputedStore":
|
||||
case "PropertyDelete":
|
||||
case "MethodCall":
|
||||
case "PropertyStore":
|
||||
case "StoreGlobal": {
|
||||
case 'Await':
|
||||
case 'CallExpression':
|
||||
case 'ComputedDelete':
|
||||
case 'ComputedStore':
|
||||
case 'PropertyDelete':
|
||||
case 'MethodCall':
|
||||
case 'PropertyStore':
|
||||
case 'StoreGlobal': {
|
||||
/*
|
||||
* Mutating instructions are not safe to prune.
|
||||
* TODO: we could be more precise and make this conditional on whether
|
||||
|
|
@ -319,15 +319,15 @@ function pruneableValue(value: InstructionValue, state: State): boolean {
|
|||
*/
|
||||
return false;
|
||||
}
|
||||
case "NewExpression":
|
||||
case "UnsupportedNode":
|
||||
case "TaggedTemplateExpression": {
|
||||
case 'NewExpression':
|
||||
case 'UnsupportedNode':
|
||||
case 'TaggedTemplateExpression': {
|
||||
// Potentially safe to prune, since they should just be creating new values
|
||||
return false;
|
||||
}
|
||||
case "GetIterator":
|
||||
case "NextPropertyOf":
|
||||
case "IteratorNext": {
|
||||
case 'GetIterator':
|
||||
case 'NextPropertyOf':
|
||||
case 'IteratorNext': {
|
||||
/*
|
||||
* Technically a IteratorNext/NextPropertyOf will never be unused because it's
|
||||
* always used later by another StoreLocal or Destructure instruction, but conceptually
|
||||
|
|
@ -335,13 +335,13 @@ function pruneableValue(value: InstructionValue, state: State): boolean {
|
|||
*/
|
||||
return false;
|
||||
}
|
||||
case "LoadContext":
|
||||
case "DeclareContext":
|
||||
case "StoreContext": {
|
||||
case 'LoadContext':
|
||||
case 'DeclareContext':
|
||||
case 'StoreContext': {
|
||||
return false;
|
||||
}
|
||||
case "StartMemoize":
|
||||
case "FinishMemoize": {
|
||||
case 'StartMemoize':
|
||||
case 'FinishMemoize': {
|
||||
/**
|
||||
* This instruction is used by the @enablePreserveExistingMemoizationGuarantees feature
|
||||
* to preserve information about memoization semantics in the original code. We can't
|
||||
|
|
@ -349,31 +349,31 @@ function pruneableValue(value: InstructionValue, state: State): boolean {
|
|||
*/
|
||||
return false;
|
||||
}
|
||||
case "RegExpLiteral":
|
||||
case "MetaProperty":
|
||||
case "LoadGlobal":
|
||||
case "ArrayExpression":
|
||||
case "BinaryExpression":
|
||||
case "ComputedLoad":
|
||||
case "ObjectMethod":
|
||||
case "FunctionExpression":
|
||||
case "LoadLocal":
|
||||
case "JsxExpression":
|
||||
case "JsxFragment":
|
||||
case "JSXText":
|
||||
case "ObjectExpression":
|
||||
case "Primitive":
|
||||
case "PropertyLoad":
|
||||
case "TemplateLiteral":
|
||||
case "TypeCastExpression":
|
||||
case "UnaryExpression": {
|
||||
case 'RegExpLiteral':
|
||||
case 'MetaProperty':
|
||||
case 'LoadGlobal':
|
||||
case 'ArrayExpression':
|
||||
case 'BinaryExpression':
|
||||
case 'ComputedLoad':
|
||||
case 'ObjectMethod':
|
||||
case 'FunctionExpression':
|
||||
case 'LoadLocal':
|
||||
case 'JsxExpression':
|
||||
case 'JsxFragment':
|
||||
case 'JSXText':
|
||||
case 'ObjectExpression':
|
||||
case 'Primitive':
|
||||
case 'PropertyLoad':
|
||||
case 'TemplateLiteral':
|
||||
case 'TypeCastExpression':
|
||||
case 'UnaryExpression': {
|
||||
// Definitely safe to prune since they are read-only
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
value,
|
||||
`Unexepcted value kind \`${(value as any).kind}\``
|
||||
`Unexepcted value kind \`${(value as any).kind}\``,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { CompilerError } from "..";
|
||||
import {CompilerError} from '..';
|
||||
import {
|
||||
BasicBlock,
|
||||
Environment,
|
||||
|
|
@ -18,15 +18,15 @@ import {
|
|||
isExpressionBlockKind,
|
||||
makeInstructionId,
|
||||
markInstructionIds,
|
||||
} from "../HIR";
|
||||
import { printInstruction } from "../HIR/PrintHIR";
|
||||
} from '../HIR';
|
||||
import {printInstruction} from '../HIR/PrintHIR';
|
||||
import {
|
||||
eachInstructionLValue,
|
||||
eachInstructionValueLValue,
|
||||
eachInstructionValueOperand,
|
||||
eachTerminalOperand,
|
||||
} from "../HIR/visitors";
|
||||
import { getOrInsertWith } from "../Utils/utils";
|
||||
} from '../HIR/visitors';
|
||||
import {getOrInsertWith} from '../Utils/utils';
|
||||
|
||||
/**
|
||||
* This pass implements conservative instruction reordering to move instructions closer to
|
||||
|
|
@ -80,8 +80,8 @@ export function instructionReordering(fn: HIRFunction): void {
|
|||
reason: `InstructionReordering: expected all reorderable nodes to have been emitted`,
|
||||
loc:
|
||||
[...shared.values()]
|
||||
.map((node) => node.instruction?.loc)
|
||||
.filter((loc) => loc != null)[0] ?? GeneratedSource,
|
||||
.map(node => node.instruction?.loc)
|
||||
.filter(loc => loc != null)[0] ?? GeneratedSource,
|
||||
});
|
||||
markInstructionIds(fn.body);
|
||||
}
|
||||
|
|
@ -113,11 +113,11 @@ function findReferencedRangeOfTemporaries(fn: HIRFunction): References {
|
|||
function reference(
|
||||
instr: InstructionId,
|
||||
place: Place,
|
||||
kind: ReferenceKind
|
||||
kind: ReferenceKind,
|
||||
): void {
|
||||
if (
|
||||
place.identifier.name !== null &&
|
||||
place.identifier.name.kind === "named"
|
||||
place.identifier.name.kind === 'named'
|
||||
) {
|
||||
if (kind === ReferenceKind.Write) {
|
||||
const name = place.identifier.name.value;
|
||||
|
|
@ -127,7 +127,7 @@ function findReferencedRangeOfTemporaries(fn: HIRFunction): References {
|
|||
} else {
|
||||
lastAssignments.set(
|
||||
name,
|
||||
makeInstructionId(Math.max(previous, instr))
|
||||
makeInstructionId(Math.max(previous, instr)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -154,7 +154,7 @@ function findReferencedRangeOfTemporaries(fn: HIRFunction): References {
|
|||
singleUseIdentifiers: new Set(
|
||||
[...singleUseIdentifiers]
|
||||
.filter(([, count]) => count === 1)
|
||||
.map(([id]) => id)
|
||||
.map(([id]) => id),
|
||||
),
|
||||
lastAssignments,
|
||||
};
|
||||
|
|
@ -164,13 +164,13 @@ function reorderBlock(
|
|||
env: Environment,
|
||||
block: BasicBlock,
|
||||
shared: Nodes,
|
||||
references: References
|
||||
references: References,
|
||||
): void {
|
||||
const locals: Nodes = new Map();
|
||||
const named: Map<string, IdentifierId> = new Map();
|
||||
let previous: IdentifierId | null = null;
|
||||
for (const instr of block.instructions) {
|
||||
const { lvalue, value } = instr;
|
||||
const {lvalue, value} = instr;
|
||||
// Get or create a node for this lvalue
|
||||
const reorderability = getReorderability(instr, references);
|
||||
const node = getOrInsertWith(
|
||||
|
|
@ -182,7 +182,7 @@ function reorderBlock(
|
|||
dependencies: new Set(),
|
||||
reorderability,
|
||||
depth: null,
|
||||
}) as Node
|
||||
}) as Node,
|
||||
);
|
||||
/**
|
||||
* Ensure non-reoderable instructions have their order retained by
|
||||
|
|
@ -198,8 +198,8 @@ function reorderBlock(
|
|||
* Establish dependencies on operands
|
||||
*/
|
||||
for (const operand of eachInstructionValueOperand(value)) {
|
||||
const { name, id } = operand.identifier;
|
||||
if (name !== null && name.kind === "named") {
|
||||
const {name, id} = operand.identifier;
|
||||
if (name !== null && name.kind === 'named') {
|
||||
// Serialize all accesses to named variables
|
||||
const previous = named.get(name.value);
|
||||
if (previous !== undefined) {
|
||||
|
|
@ -225,11 +225,11 @@ function reorderBlock(
|
|||
instruction: null,
|
||||
dependencies: new Set(),
|
||||
depth: null,
|
||||
}) as Node
|
||||
}) as Node,
|
||||
);
|
||||
lvalueNode.dependencies.add(lvalue.identifier.id);
|
||||
const name = lvalueOperand.identifier.name;
|
||||
if (name !== null && name.kind === "named") {
|
||||
if (name !== null && name.kind === 'named') {
|
||||
const previous = named.get(name.value);
|
||||
if (previous !== undefined) {
|
||||
node.dependencies.add(previous);
|
||||
|
|
@ -272,14 +272,14 @@ function reorderBlock(
|
|||
locals,
|
||||
shared,
|
||||
seen,
|
||||
block.instructions.at(-1)!.lvalue.identifier.id
|
||||
block.instructions.at(-1)!.lvalue.identifier.id,
|
||||
);
|
||||
emit(
|
||||
env,
|
||||
locals,
|
||||
shared,
|
||||
nextInstructions,
|
||||
block.instructions.at(-1)!.lvalue.identifier.id
|
||||
block.instructions.at(-1)!.lvalue.identifier.id,
|
||||
);
|
||||
}
|
||||
/*
|
||||
|
|
@ -307,7 +307,7 @@ function reorderBlock(
|
|||
node.instruction != null
|
||||
? `Instruction [${node.instruction.id}] was not emitted yet but is not reorderable`
|
||||
: `Lvalue $${id} was not emitted yet but is not reorderable`,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
DEBUG && console.log(`save shared: $${id}`);
|
||||
|
|
@ -358,7 +358,7 @@ function reorderBlock(
|
|||
DEBUG && console.log(`save shared: $${id}`);
|
||||
shared.set(id, node);
|
||||
} else {
|
||||
DEBUG && console.log("leftover");
|
||||
DEBUG && console.log('leftover');
|
||||
DEBUG && print(env, locals, shared, seen, id);
|
||||
emit(env, locals, shared, nextInstructions, id);
|
||||
}
|
||||
|
|
@ -392,10 +392,10 @@ function print(
|
|||
shared: Nodes,
|
||||
seen: Set<IdentifierId>,
|
||||
id: IdentifierId,
|
||||
depth: number = 0
|
||||
depth: number = 0,
|
||||
): void {
|
||||
if (seen.has(id)) {
|
||||
DEBUG && console.log(`${"| ".repeat(depth)}$${id} <skipped>`);
|
||||
DEBUG && console.log(`${'| '.repeat(depth)}$${id} <skipped>`);
|
||||
return;
|
||||
}
|
||||
seen.add(id);
|
||||
|
|
@ -414,20 +414,20 @@ function print(
|
|||
}
|
||||
DEBUG &&
|
||||
console.log(
|
||||
`${"| ".repeat(depth)}$${id} ${printNode(node)} deps=[${deps
|
||||
.map((x) => `$${x}`)
|
||||
.join(", ")}] depth=${node.depth}`
|
||||
`${'| '.repeat(depth)}$${id} ${printNode(node)} deps=[${deps
|
||||
.map(x => `$${x}`)
|
||||
.join(', ')}] depth=${node.depth}`,
|
||||
);
|
||||
}
|
||||
|
||||
function printNode(node: Node): string {
|
||||
const { instruction } = node;
|
||||
const {instruction} = node;
|
||||
if (instruction === null) {
|
||||
return "<lvalue-only>";
|
||||
return '<lvalue-only>';
|
||||
}
|
||||
switch (instruction.value.kind) {
|
||||
case "FunctionExpression":
|
||||
case "ObjectMethod": {
|
||||
case 'FunctionExpression':
|
||||
case 'ObjectMethod': {
|
||||
return `[${instruction.id}] ${instruction.value.kind}`;
|
||||
}
|
||||
default: {
|
||||
|
|
@ -441,7 +441,7 @@ function emit(
|
|||
locals: Nodes,
|
||||
shared: Nodes,
|
||||
instructions: Array<Instruction>,
|
||||
id: IdentifierId
|
||||
id: IdentifierId,
|
||||
): void {
|
||||
const node = locals.get(id) ?? shared.get(id);
|
||||
if (node == null) {
|
||||
|
|
@ -469,22 +469,22 @@ enum Reorderability {
|
|||
}
|
||||
function getReorderability(
|
||||
instr: Instruction,
|
||||
references: References
|
||||
references: References,
|
||||
): Reorderability {
|
||||
switch (instr.value.kind) {
|
||||
case "JsxExpression":
|
||||
case "JsxFragment":
|
||||
case "JSXText":
|
||||
case "LoadGlobal":
|
||||
case "Primitive":
|
||||
case "TemplateLiteral":
|
||||
case "BinaryExpression":
|
||||
case "UnaryExpression": {
|
||||
case 'JsxExpression':
|
||||
case 'JsxFragment':
|
||||
case 'JSXText':
|
||||
case 'LoadGlobal':
|
||||
case 'Primitive':
|
||||
case 'TemplateLiteral':
|
||||
case 'BinaryExpression':
|
||||
case 'UnaryExpression': {
|
||||
return Reorderability.Reorderable;
|
||||
}
|
||||
case "LoadLocal": {
|
||||
case 'LoadLocal': {
|
||||
const name = instr.value.place.identifier.name;
|
||||
if (name !== null && name.kind === "named") {
|
||||
if (name !== null && name.kind === 'named') {
|
||||
const lastAssignment = references.lastAssignments.get(name.value);
|
||||
if (
|
||||
lastAssignment !== undefined &&
|
||||
|
|
|
|||
|
|
@ -5,23 +5,23 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { HIRFunction } from "../HIR";
|
||||
import {HIRFunction} from '../HIR';
|
||||
|
||||
export function outlineFunctions(fn: HIRFunction): void {
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
const { value } = instr;
|
||||
const {value} = instr;
|
||||
|
||||
if (
|
||||
value.kind === "FunctionExpression" ||
|
||||
value.kind === "ObjectMethod"
|
||||
value.kind === 'FunctionExpression' ||
|
||||
value.kind === 'ObjectMethod'
|
||||
) {
|
||||
// Recurse in case there are inner functions which can be outlined
|
||||
outlineFunctions(value.loweredFunc.func);
|
||||
}
|
||||
|
||||
if (
|
||||
value.kind === "FunctionExpression" &&
|
||||
value.kind === 'FunctionExpression' &&
|
||||
value.loweredFunc.dependencies.length === 0 &&
|
||||
value.loweredFunc.func.context.length === 0 &&
|
||||
// TODO: handle outlining named functions
|
||||
|
|
@ -34,9 +34,9 @@ export function outlineFunctions(fn: HIRFunction): void {
|
|||
|
||||
fn.env.outlineFunction(loweredFunc, null);
|
||||
instr.value = {
|
||||
kind: "LoadGlobal",
|
||||
kind: 'LoadGlobal',
|
||||
binding: {
|
||||
kind: "Global",
|
||||
kind: 'Global',
|
||||
name: id.value,
|
||||
},
|
||||
loc: value.loc,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { CompilerError } from "..";
|
||||
import {CompilerError} from '..';
|
||||
import {
|
||||
BlockId,
|
||||
GeneratedSource,
|
||||
|
|
@ -16,14 +16,14 @@ import {
|
|||
assertTerminalSuccessorsExist,
|
||||
mergeConsecutiveBlocks,
|
||||
reversePostorderBlocks,
|
||||
} from "../HIR";
|
||||
} from '../HIR';
|
||||
import {
|
||||
markInstructionIds,
|
||||
removeDeadDoWhileStatements,
|
||||
removeUnnecessaryTryCatch,
|
||||
removeUnreachableForUpdates,
|
||||
} from "../HIR/HIRBuilder";
|
||||
import { printIdentifier } from "../HIR/PrintHIR";
|
||||
} from '../HIR/HIRBuilder';
|
||||
import {printIdentifier} from '../HIR/PrintHIR';
|
||||
|
||||
/*
|
||||
* This pass prunes `maybe-throw` terminals for blocks that can provably *never* throw.
|
||||
|
|
@ -74,17 +74,17 @@ function pruneMaybeThrowsImpl(fn: HIRFunction): Map<BlockId, BlockId> | null {
|
|||
const terminalMapping = new Map<BlockId, BlockId>();
|
||||
for (const [_, block] of fn.body.blocks) {
|
||||
const terminal = block.terminal;
|
||||
if (terminal.kind !== "maybe-throw") {
|
||||
if (terminal.kind !== 'maybe-throw') {
|
||||
continue;
|
||||
}
|
||||
const canThrow = block.instructions.some((instr) =>
|
||||
instructionMayThrow(instr)
|
||||
const canThrow = block.instructions.some(instr =>
|
||||
instructionMayThrow(instr),
|
||||
);
|
||||
if (!canThrow) {
|
||||
const source = terminalMapping.get(block.id) ?? block.id;
|
||||
terminalMapping.set(terminal.continuation, source);
|
||||
block.terminal = {
|
||||
kind: "goto",
|
||||
kind: 'goto',
|
||||
block: terminal.continuation,
|
||||
variant: GotoVariant.Break,
|
||||
id: terminal.id,
|
||||
|
|
@ -97,9 +97,9 @@ function pruneMaybeThrowsImpl(fn: HIRFunction): Map<BlockId, BlockId> | null {
|
|||
|
||||
function instructionMayThrow(instr: Instruction): boolean {
|
||||
switch (instr.value.kind) {
|
||||
case "Primitive":
|
||||
case "ArrayExpression":
|
||||
case "ObjectExpression": {
|
||||
case 'Primitive':
|
||||
case 'ArrayExpression':
|
||||
case 'ObjectExpression': {
|
||||
return false;
|
||||
}
|
||||
default: {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
export { constantPropagation } from "./ConstantPropagation";
|
||||
export { deadCodeElimination } from "./DeadCodeElimination";
|
||||
export { pruneMaybeThrows } from "./PruneMaybeThrows";
|
||||
export {constantPropagation} from './ConstantPropagation';
|
||||
export {deadCodeElimination} from './DeadCodeElimination';
|
||||
export {pruneMaybeThrows} from './PruneMaybeThrows';
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import {
|
|||
IdentifierId,
|
||||
ReactiveScope,
|
||||
makeInstructionId,
|
||||
} from "../HIR";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
} from '../HIR';
|
||||
import DisjointSet from '../Utils/DisjointSet';
|
||||
|
||||
/**
|
||||
* Ensures that method call instructions have scopes such that either:
|
||||
|
|
@ -24,8 +24,8 @@ export function alignMethodCallScopes(fn: HIRFunction): void {
|
|||
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
const { lvalue, value } = instr;
|
||||
if (value.kind === "MethodCall") {
|
||||
const {lvalue, value} = instr;
|
||||
if (value.kind === 'MethodCall') {
|
||||
const lvalueScope = lvalue.identifier.scope;
|
||||
const propertyScope = value.property.identifier.scope;
|
||||
if (lvalueScope !== null) {
|
||||
|
|
@ -44,8 +44,8 @@ export function alignMethodCallScopes(fn: HIRFunction): void {
|
|||
scopeMapping.set(value.property.identifier.id, null);
|
||||
}
|
||||
} else if (
|
||||
value.kind === "FunctionExpression" ||
|
||||
value.kind === "ObjectMethod"
|
||||
value.kind === 'FunctionExpression' ||
|
||||
value.kind === 'ObjectMethod'
|
||||
) {
|
||||
alignMethodCallScopes(value.loweredFunc.func);
|
||||
}
|
||||
|
|
@ -57,10 +57,10 @@ export function alignMethodCallScopes(fn: HIRFunction): void {
|
|||
return;
|
||||
}
|
||||
root.range.start = makeInstructionId(
|
||||
Math.min(scope.range.start, root.range.start)
|
||||
Math.min(scope.range.start, root.range.start),
|
||||
);
|
||||
root.range.end = makeInstructionId(
|
||||
Math.max(scope.range.end, root.range.end)
|
||||
Math.max(scope.range.end, root.range.end),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user