[playground] Display only new HIR

Stack created with [Sapling](https://sapling-scm.com). Best reviewed with 
[ReviewStack](https://reviewstack.dev/facebook/react-forget/pull/824). * #825 * 
__->__ #824 

[playground] Display only new HIR
This commit is contained in:
Sathya Gunasekaran
2022-11-24 15:24:40 +00:00
parent da6d84049e
commit e2cfbeb3d3
4 changed files with 141 additions and 316 deletions
@@ -1,46 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import clsx from "clsx";
import type { ForgetCompilerFlags } from "../../lib/compilerDriver";
import { useStore, useStoreDispatch } from "../StoreContext";
// TODO: Allow collapsing the compiler options editor
export default function CompilerFlagsEditor() {
const store = useStore();
const dispatch = useStoreDispatch();
return (
<div className="flex flex-col gap-2 px-5 py-4 border-t border-gray-200">
<div>
<h3 className="font-medium">Compiler Options</h3>
</div>
<div className="flex flex-col">
{Object.keys(store.compilerFlags).map((f) => {
const flag = f as keyof ForgetCompilerFlags;
const isChecked = store.compilerFlags[flag];
return (
<label key={flag} className="flex gap-1.5 text-base">
<input
type="checkbox"
checked={isChecked}
onChange={(e) =>
dispatch({
type: "setCompilerFlag",
payload: { flag, value: e.target.checked },
})
}
/>
<span
className={clsx("text-gray-500", { "text-black": isChecked })}
>
{flag}
</span>
</label>
);
})}
</div>
</div>
);
}
@@ -1,207 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import clsx from "clsx";
import { HIR } from "babel-plugin-react-forget";
import { useState } from "react";
import generate from "@babel/generator";
import prettier from "prettier";
import prettierParserBabel from "prettier/parser-babel";
const {
parseFunctions,
Environment,
enterSSA,
eliminateRedundantPhi,
inferReferenceEffects,
inferMutableRanges,
leaveSSA,
lower,
printHIR,
codegen,
} = HIR;
export default function HIRTabContent({ source }: { source: string }) {
const astFunctions = parseFunctions(source);
const [flags, setFlags] = useState({
codegen: true,
eliminateRedundantPhi: true,
inferReferenceEffects: true,
inferMutableRanges: true,
leaveSSA: true,
});
return (
<div className="flex flex-col h-full">
<div className="flex-1">
{astFunctions.map((func, index): React.ReactNode => {
let body;
try {
const env = new Environment();
const ir = lower(func, env);
enterSSA(ir, env);
if (flags.eliminateRedundantPhi) {
eliminateRedundantPhi(ir);
}
if (flags.inferReferenceEffects) {
inferReferenceEffects(ir);
}
if (flags.inferMutableRanges) {
inferMutableRanges(ir);
}
if (flags.leaveSSA) {
leaveSSA(ir);
}
if (flags.codegen) {
const ast = codegen(ir);
const generated = generate(
ast,
{
sourceMaps: true,
sourceFileName: "input.js",
},
source
);
const formatted = prettier.format(generated.code, {
semi: true,
parser: "babel",
plugins: [prettierParserBabel],
});
const sourceMapUrl = getSourceMapUrl(
generated.code,
JSON.stringify(generated.map)
);
body = (
<>
<pre>{formatted}</pre>
{sourceMapUrl && (
<iframe
src={sourceMapUrl}
className="w-full h-96"
title="Generated Code"
/>
)}
</>
);
} else {
body = <pre>{printHIR(ir.body)}</pre>;
}
} catch (e: any) {
body = <div>error: ${e.toString()}</div>;
}
const name = func.node.id?.name ?? "anonymous";
return (
<div key={index} className="px-2 py-4 border-b">
<h3 className="mb-2 font-medium">Function: {name}</h3>
{body}
</div>
);
})}
</div>
<CompilerFlagsEditor flags={flags} setFlags={setFlags} />
</div>
);
}
function utf16ToUTF8(s: string): string {
return unescape(encodeURIComponent(s));
}
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}`
)}`;
}
type Flags = {
eliminateRedundantPhi: boolean;
inferReferenceEffects: boolean;
inferMutableRanges: boolean;
leaveSSA: boolean;
codegen: boolean;
};
function CompilerFlagsEditor({
flags,
setFlags,
}: {
flags: Flags;
setFlags: (flags: Flags) => void;
}) {
return (
<div className="flex flex-col gap-2 px-5 py-4 border-t border-gray-200">
<h3 className="font-medium">Compiler Options</h3>
<div className="flex flex-col">
<LabeledCheckbox
label="Eliminate Redundant Phi"
value={flags.eliminateRedundantPhi}
onChange={(eliminateRedundantPhi) => {
setFlags({ ...flags, eliminateRedundantPhi });
}}
/>
<LabeledCheckbox
label="Infer Reference Effects"
value={flags.inferReferenceEffects}
onChange={(inferReferenceEffects) => {
setFlags({ ...flags, inferReferenceEffects });
}}
/>
<LabeledCheckbox
label="Infer Mutable Ranges"
value={flags.inferMutableRanges}
onChange={(inferMutableRanges) => {
setFlags({ ...flags, inferMutableRanges });
}}
/>
<LabeledCheckbox
label="Leave SSA"
value={flags.leaveSSA}
onChange={(leaveSSA) => {
setFlags({ ...flags, leaveSSA });
}}
/>
<LabeledCheckbox
label="Codegen"
value={flags.codegen}
onChange={(codegen) => {
setFlags({ ...flags, codegen });
}}
/>
</div>
</div>
);
}
function LabeledCheckbox({
label,
value,
onChange,
}: {
label: string;
value: boolean;
onChange: (value: boolean) => void;
}) {
return (
<label className="flex gap-1.5 text-base">
<input
type="checkbox"
checked={value}
onChange={(e) => {
onChange(e.target.checked);
}}
/>
<span className={clsx({ "text-gray-500": !value, "text-black": value })}>
{label}
</span>
</label>
);
}
@@ -5,21 +5,28 @@
* LICENSE file in the root directory of this source tree.
*/
import TabbedWindow from "../TabbedWindow";
import generate from "@babel/generator";
import MonacoEditor from "@monaco-editor/react";
import {
CompilerOutputs,
OutputKind,
stringifyCompilerOutputs,
type Diagnostic,
} from "babel-plugin-react-forget";
import { memo, useEffect, useMemo } from "react";
import compile from "../../lib/compilerDriver";
import { Diagnostic, HIR, OutputKind } from "babel-plugin-react-forget";
import prettier from "prettier";
import prettierParserBabel from "prettier/parser-babel";
import { memo } from "react";
import type { Store } from "../../lib/stores";
import GraphView from "../GraphView";
import TabbedWindow from "../TabbedWindow";
import { monacoOptions } from "./monacoOptions";
import HIRTabContent from "./HIRTabContent";
const {
parseFunctions,
Environment,
enterSSA,
eliminateRedundantPhi,
inferReferenceEffects,
inferMutableRanges,
leaveSSA,
lower,
printHIR,
codegen,
} = HIR;
const MemoizedOutput = memo(Output);
export default MemoizedOutput;
@@ -29,55 +36,141 @@ type Props = {
updateDiagnostics: (newDiags: Diagnostic[]) => void;
};
function Output({ store, updateDiagnostics }: Props) {
const { outputs, diagnostics } = useMemo(
() => compile(store.source, store.compilerFlags),
[store]
);
useEffect(() => {
updateDiagnostics(diagnostics);
}, [diagnostics, updateDiagnostics]);
return (
<TabbedWindow
defaultTab="HIR"
tabs={{
IR: <TextTabContent outputs={outputs} kind={OutputKind.IR} />,
HIR: <HIRTabContent source={store.source} />,
CFG: <TextTabContent outputs={outputs} kind={OutputKind.CFG} />,
ValGraph: (
<GraphTabContent outputs={outputs} kind={OutputKind.ValGraph} />
),
SCCGraph: (
<GraphTabContent outputs={outputs} kind={OutputKind.SCCGraph} />
),
RedGraph: (
<GraphTabContent outputs={outputs} kind={OutputKind.RedGraph} />
),
LIR: <TextTabContent outputs={outputs} kind={OutputKind.LIR} />,
JS: <TextTabContent outputs={outputs} kind={OutputKind.JS} />,
}}
/>
);
// TODO(gsn: Update diagnostics ƒrom HIR output
function Output({ store }: Props) {
const astFunctions = parseFunctions(store.source);
if (astFunctions.length === 0) {
return <div></div>;
}
try {
// TODO: Handle multiple functions
const func = astFunctions[0];
const env = new Environment();
const ir = lower(func, env);
const hirOutput = printHIR(ir.body);
enterSSA(ir, env);
const ssaOutput = printHIR(ir.body);
eliminateRedundantPhi(ir);
const eliminateRedundantPhiOutput = printHIR(ir.body);
inferReferenceEffects(ir);
const inferReferenceEffectsOutput = printHIR(ir.body);
inferMutableRanges(ir);
const inferMutableRangesOutput = printHIR(ir.body);
leaveSSA(ir);
const leaveSSAOutput = printHIR(ir.body);
codegen(ir);
const ast = codegen(ir);
const generated = generate(
ast,
{
sourceMaps: true,
sourceFileName: "input.js",
},
store.source
);
const sourceMapUrl = getSourceMapUrl(
generated.code,
JSON.stringify(generated.map)
);
const codegenOutput = prettier.format(generated.code, {
semi: true,
parser: "babel",
plugins: [prettierParserBabel],
});
return (
<TabbedWindow
defaultTab="HIR"
tabs={{
HIR: (
<TextTabContent
output={hirOutput}
kind={OutputKind.IR}
></TextTabContent>
),
SSA: (
<TextTabContent
output={ssaOutput}
kind={OutputKind.IR}
></TextTabContent>
),
EliminateRedundantPhi: (
<TextTabContent
output={eliminateRedundantPhiOutput}
kind={OutputKind.IR}
></TextTabContent>
),
InferReferenceEffects: (
<TextTabContent
output={inferReferenceEffectsOutput}
kind={OutputKind.IR}
></TextTabContent>
),
InferMutableRanges: (
<TextTabContent
output={inferMutableRangesOutput}
kind={OutputKind.IR}
></TextTabContent>
),
LeaveSSA: (
<TextTabContent
output={leaveSSAOutput}
kind={OutputKind.IR}
></TextTabContent>
),
JS: <TextTabContent output={codegenOutput} kind={OutputKind.JS} />,
SourceMap: (
<>
{" "}
{sourceMapUrl && (
<iframe
src={sourceMapUrl}
className="w-full h-96"
title="Generated Code"
/>
)}
</>
),
}}
/>
);
} catch (e: any) {
return <div>error: ${e.toString()}</div>;
}
}
function utf16ToUTF8(s: string): string {
return unescape(encodeURIComponent(s));
}
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}`
)}`;
}
function TextTabContent({
outputs,
output,
kind,
}: {
outputs: CompilerOutputs;
output: string;
kind: OutputKind;
}) {
const prettyOutputs = useMemo(
() => stringifyCompilerOutputs(outputs),
[outputs]
);
return (
<div className="w-full h-full">
<MonacoEditor
path={kind}
defaultLanguage="javascript"
value={prettyOutputs[kind] ?? "(Empty)"}
value={output}
options={{
...monacoOptions,
readOnly: true,
@@ -86,17 +179,3 @@ function TextTabContent({
</div>
);
}
function GraphTabContent({
outputs,
kind,
}: {
outputs: CompilerOutputs;
kind: OutputKind.ValGraph | OutputKind.SCCGraph | OutputKind.RedGraph;
}) {
return (
<div className="w-full h-full overflow-auto">
<GraphView graphs={outputs[kind]} />
</div>
);
}
@@ -21,7 +21,6 @@ import {
type Store,
} from "../../lib/stores";
import { useStore, useStoreDispatch } from "../StoreContext";
import CompilerFlagsEditor from "./CompilerFlagsEditor";
import Input from "./Input";
import Output from "./Output";