[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
Sathya Gunasekaran committed
Nov 24, 2022 at 15:24 UTC
e2cfbeb3d3457aa4f38bff54a34a04c68c8ab277
4 files changed
+141
-316
compiler/forget/packages/playground/components/Editor/CompilerFlagsEditor.tsx
deleted
-46
@@ -1,46 +0,0 @@
1
-/*
2
- * Copyright (c) Facebook, Inc. and its affiliates.
3
- */
4
-import clsx from "clsx";
5
-import type { ForgetCompilerFlags } from "../../lib/compilerDriver";
6
-import { useStore, useStoreDispatch } from "../StoreContext";
7
-
8
-// TODO: Allow collapsing the compiler options editor
9
-export default function CompilerFlagsEditor() {
10
- const store = useStore();
11
- const dispatch = useStoreDispatch();
12
-
13
- return (
14
- <div className="flex flex-col gap-2 px-5 py-4 border-t border-gray-200">
15
- <div>
16
- <h3 className="font-medium">Compiler Options</h3>
17
- </div>
18
- <div className="flex flex-col">
19
- {Object.keys(store.compilerFlags).map((f) => {
20
- const flag = f as keyof ForgetCompilerFlags;
21
- const isChecked = store.compilerFlags[flag];
22
-
23
- return (
24
- <label key={flag} className="flex gap-1.5 text-base">
25
- <input
26
- type="checkbox"
27
- checked={isChecked}
28
- onChange={(e) =>
29
- dispatch({
30
- type: "setCompilerFlag",
31
- payload: { flag, value: e.target.checked },
32
- })
33
- }
34
- />
35
- <span
36
- className={clsx("text-gray-500", { "text-black": isChecked })}
37
- >
38
- {flag}
39
- </span>
40
- </label>
41
- );
42
- })}
43
- </div>
44
- </div>
45
- );
46
-}
compiler/forget/packages/playground/components/Editor/HIRTabContent.tsx
deleted
-207
@@ -1,207 +0,0 @@
1
-/**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- */
7
-
8
-import clsx from "clsx";
9
-
10
-import { HIR } from "babel-plugin-react-forget";
11
-import { useState } from "react";
12
-import generate from "@babel/generator";
13
-import prettier from "prettier";
14
-import prettierParserBabel from "prettier/parser-babel";
15
-
16
-const {
17
- parseFunctions,
18
- Environment,
19
- enterSSA,
20
- eliminateRedundantPhi,
21
- inferReferenceEffects,
22
- inferMutableRanges,
23
- leaveSSA,
24
- lower,
25
- printHIR,
26
- codegen,
27
-} = HIR;
28
-
29
-export default function HIRTabContent({ source }: { source: string }) {
30
- const astFunctions = parseFunctions(source);
31
-
32
- const [flags, setFlags] = useState({
33
- codegen: true,
34
- eliminateRedundantPhi: true,
35
- inferReferenceEffects: true,
36
- inferMutableRanges: true,
37
- leaveSSA: true,
38
- });
39
-
40
- return (
41
- <div className="flex flex-col h-full">
42
- <div className="flex-1">
43
- {astFunctions.map((func, index): React.ReactNode => {
44
- let body;
45
- try {
46
- const env = new Environment();
47
- const ir = lower(func, env);
48
- enterSSA(ir, env);
49
- if (flags.eliminateRedundantPhi) {
50
- eliminateRedundantPhi(ir);
51
- }
52
- if (flags.inferReferenceEffects) {
53
- inferReferenceEffects(ir);
54
- }
55
- if (flags.inferMutableRanges) {
56
- inferMutableRanges(ir);
57
- }
58
- if (flags.leaveSSA) {
59
- leaveSSA(ir);
60
- }
61
-
62
- if (flags.codegen) {
63
- const ast = codegen(ir);
64
- const generated = generate(
65
- ast,
66
- {
67
- sourceMaps: true,
68
- sourceFileName: "input.js",
69
- },
70
- source
71
- );
72
- const formatted = prettier.format(generated.code, {
73
- semi: true,
74
- parser: "babel",
75
- plugins: [prettierParserBabel],
76
- });
77
- const sourceMapUrl = getSourceMapUrl(
78
- generated.code,
79
- JSON.stringify(generated.map)
80
- );
81
- body = (
82
- <>
83
- <pre>{formatted}</pre>
84
- {sourceMapUrl && (
85
- <iframe
86
- src={sourceMapUrl}
87
- className="w-full h-96"
88
- title="Generated Code"
89
- />
90
- )}
91
- </>
92
- );
93
- } else {
94
- body = <pre>{printHIR(ir.body)}</pre>;
95
- }
96
- } catch (e: any) {
97
- body = <div>error: ${e.toString()}</div>;
98
- }
99
- const name = func.node.id?.name ?? "anonymous";
100
- return (
101
- <div key={index} className="px-2 py-4 border-b">
102
- <h3 className="mb-2 font-medium">Function: {name}</h3>
103
- {body}
104
- </div>
105
- );
106
- })}
107
- </div>
108
- <CompilerFlagsEditor flags={flags} setFlags={setFlags} />
109
- </div>
110
- );
111
-}
112
-
113
-function utf16ToUTF8(s: string): string {
114
- return unescape(encodeURIComponent(s));
115
-}
116
-
117
-function getSourceMapUrl(code: string, map: string): string | null {
118
- code = utf16ToUTF8(code);
119
- map = utf16ToUTF8(map);
120
- return `https://evanw.github.io/source-map-visualization/#${btoa(
121
- `${code.length}\0${code}${map.length}\0${map}`
122
- )}`;
123
-}
124
-
125
-type Flags = {
126
- eliminateRedundantPhi: boolean;
127
- inferReferenceEffects: boolean;
128
- inferMutableRanges: boolean;
129
- leaveSSA: boolean;
130
- codegen: boolean;
131
-};
132
-
133
-function CompilerFlagsEditor({
134
- flags,
135
- setFlags,
136
-}: {
137
- flags: Flags;
138
- setFlags: (flags: Flags) => void;
139
-}) {
140
- return (
141
- <div className="flex flex-col gap-2 px-5 py-4 border-t border-gray-200">
142
- <h3 className="font-medium">Compiler Options</h3>
143
- <div className="flex flex-col">
144
- <LabeledCheckbox
145
- label="Eliminate Redundant Phi"
146
- value={flags.eliminateRedundantPhi}
147
- onChange={(eliminateRedundantPhi) => {
148
- setFlags({ ...flags, eliminateRedundantPhi });
149
- }}
150
- />
151
- <LabeledCheckbox
152
- label="Infer Reference Effects"
153
- value={flags.inferReferenceEffects}
154
- onChange={(inferReferenceEffects) => {
155
- setFlags({ ...flags, inferReferenceEffects });
156
- }}
157
- />
158
- <LabeledCheckbox
159
- label="Infer Mutable Ranges"
160
- value={flags.inferMutableRanges}
161
- onChange={(inferMutableRanges) => {
162
- setFlags({ ...flags, inferMutableRanges });
163
- }}
164
- />
165
- <LabeledCheckbox
166
- label="Leave SSA"
167
- value={flags.leaveSSA}
168
- onChange={(leaveSSA) => {
169
- setFlags({ ...flags, leaveSSA });
170
- }}
171
- />
172
- <LabeledCheckbox
173
- label="Codegen"
174
- value={flags.codegen}
175
- onChange={(codegen) => {
176
- setFlags({ ...flags, codegen });
177
- }}
178
- />
179
- </div>
180
- </div>
181
- );
182
-}
183
-
184
-function LabeledCheckbox({
185
- label,
186
- value,
187
- onChange,
188
-}: {
189
- label: string;
190
- value: boolean;
191
- onChange: (value: boolean) => void;
192
-}) {
193
- return (
194
- <label className="flex gap-1.5 text-base">
195
- <input
196
- type="checkbox"
197
- checked={value}
198
- onChange={(e) => {
199
- onChange(e.target.checked);
200
- }}
201
- />
202
- <span className={clsx({ "text-gray-500": !value, "text-black": value })}>
203
- {label}
204
- </span>
205
- </label>
206
- );
207
-}
compiler/forget/packages/playground/components/Editor/Output.tsx
+141
-62
@@ -5,21 +5,28 @@
5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import TabbedWindow from "../TabbedWindow";
8
+import generate from "@babel/generator";
9
import MonacoEditor from "@monaco-editor/react";
10
-import {
11
- CompilerOutputs,
12
- OutputKind,
13
- stringifyCompilerOutputs,
14
- type Diagnostic,
15
-} from "babel-plugin-react-forget";
16
-import { memo, useEffect, useMemo } from "react";
17
-import compile from "../../lib/compilerDriver";
10
+import { Diagnostic, HIR, OutputKind } from "babel-plugin-react-forget";
11
+import prettier from "prettier";
12
+import prettierParserBabel from "prettier/parser-babel";
13
+import { memo } from "react";
14
import type { Store } from "../../lib/stores";
19
-import GraphView from "../GraphView";
15
+import TabbedWindow from "../TabbedWindow";
16
import { monacoOptions } from "./monacoOptions";
21
-import HIRTabContent from "./HIRTabContent";
17
18
+const {
19
+ parseFunctions,
20
+ Environment,
21
+ enterSSA,
22
+ eliminateRedundantPhi,
23
+ inferReferenceEffects,
24
+ inferMutableRanges,
25
+ leaveSSA,
26
+ lower,
27
+ printHIR,
28
+ codegen,
29
+} = HIR;
30
const MemoizedOutput = memo(Output);
31
32
export default MemoizedOutput;
@@ -29,55 +36,141 @@ type Props = {
36
updateDiagnostics: (newDiags: Diagnostic[]) => void;
37
};
38
32
-function Output({ store, updateDiagnostics }: Props) {
33
- const { outputs, diagnostics } = useMemo(
34
- () => compile(store.source, store.compilerFlags),
35
- [store]
36
- );
37
- useEffect(() => {
38
- updateDiagnostics(diagnostics);
39
- }, [diagnostics, updateDiagnostics]);
40
- return (
41
- <TabbedWindow
42
- defaultTab="HIR"
43
- tabs={{
44
- IR: <TextTabContent outputs={outputs} kind={OutputKind.IR} />,
45
- HIR: <HIRTabContent source={store.source} />,
46
- CFG: <TextTabContent outputs={outputs} kind={OutputKind.CFG} />,
47
- ValGraph: (
48
- <GraphTabContent outputs={outputs} kind={OutputKind.ValGraph} />
49
- ),
50
- SCCGraph: (
51
- <GraphTabContent outputs={outputs} kind={OutputKind.SCCGraph} />
52
- ),
53
- RedGraph: (
54
- <GraphTabContent outputs={outputs} kind={OutputKind.RedGraph} />
55
- ),
56
- LIR: <TextTabContent outputs={outputs} kind={OutputKind.LIR} />,
57
- JS: <TextTabContent outputs={outputs} kind={OutputKind.JS} />,
58
- }}
59
- />
60
- );
39
+// TODO(gsn: Update diagnostics ƒrom HIR output
40
+function Output({ store }: Props) {
41
+ const astFunctions = parseFunctions(store.source);
42
+ if (astFunctions.length === 0) {
43
+ return <div></div>;
44
+ }
45
+
46
+ try {
47
+ // TODO: Handle multiple functions
48
+ const func = astFunctions[0];
49
+ const env = new Environment();
50
+ const ir = lower(func, env);
51
+
52
+ const hirOutput = printHIR(ir.body);
53
+
54
+ enterSSA(ir, env);
55
+ const ssaOutput = printHIR(ir.body);
56
+
57
+ eliminateRedundantPhi(ir);
58
+ const eliminateRedundantPhiOutput = printHIR(ir.body);
59
+
60
+ inferReferenceEffects(ir);
61
+ const inferReferenceEffectsOutput = printHIR(ir.body);
62
+
63
+ inferMutableRanges(ir);
64
+ const inferMutableRangesOutput = printHIR(ir.body);
65
+
66
+ leaveSSA(ir);
67
+ const leaveSSAOutput = printHIR(ir.body);
68
+
69
+ codegen(ir);
70
+ const ast = codegen(ir);
71
+ const generated = generate(
72
+ ast,
73
+ {
74
+ sourceMaps: true,
75
+ sourceFileName: "input.js",
76
+ },
77
+ store.source
78
+ );
79
+ const sourceMapUrl = getSourceMapUrl(
80
+ generated.code,
81
+ JSON.stringify(generated.map)
82
+ );
83
+ const codegenOutput = prettier.format(generated.code, {
84
+ semi: true,
85
+ parser: "babel",
86
+ plugins: [prettierParserBabel],
87
+ });
88
+ return (
89
+ <TabbedWindow
90
+ defaultTab="HIR"
91
+ tabs={{
92
+ HIR: (
93
+ <TextTabContent
94
+ output={hirOutput}
95
+ kind={OutputKind.IR}
96
+ ></TextTabContent>
97
+ ),
98
+ SSA: (
99
+ <TextTabContent
100
+ output={ssaOutput}
101
+ kind={OutputKind.IR}
102
+ ></TextTabContent>
103
+ ),
104
+ EliminateRedundantPhi: (
105
+ <TextTabContent
106
+ output={eliminateRedundantPhiOutput}
107
+ kind={OutputKind.IR}
108
+ ></TextTabContent>
109
+ ),
110
+ InferReferenceEffects: (
111
+ <TextTabContent
112
+ output={inferReferenceEffectsOutput}
113
+ kind={OutputKind.IR}
114
+ ></TextTabContent>
115
+ ),
116
+ InferMutableRanges: (
117
+ <TextTabContent
118
+ output={inferMutableRangesOutput}
119
+ kind={OutputKind.IR}
120
+ ></TextTabContent>
121
+ ),
122
+ LeaveSSA: (
123
+ <TextTabContent
124
+ output={leaveSSAOutput}
125
+ kind={OutputKind.IR}
126
+ ></TextTabContent>
127
+ ),
128
+ JS: <TextTabContent output={codegenOutput} kind={OutputKind.JS} />,
129
+ SourceMap: (
130
+ <>
131
+ {" "}
132
+ {sourceMapUrl && (
133
+ <iframe
134
+ src={sourceMapUrl}
135
+ className="w-full h-96"
136
+ title="Generated Code"
137
+ />
138
+ )}
139
+ </>
140
+ ),
141
+ }}
142
+ />
143
+ );
144
+ } catch (e: any) {
145
+ return <div>error: ${e.toString()}</div>;
146
+ }
147
+}
148
+
149
+function utf16ToUTF8(s: string): string {
150
+ return unescape(encodeURIComponent(s));
151
+}
152
+
153
+function getSourceMapUrl(code: string, map: string): string | null {
154
+ code = utf16ToUTF8(code);
155
+ map = utf16ToUTF8(map);
156
+ return `https://evanw.github.io/source-map-visualization/#${btoa(
157
+ `${code.length}\0${code}${map.length}\0${map}`
158
+ )}`;
159
}
160
161
function TextTabContent({
64
- outputs,
162
+ output,
163
kind,
164
}: {
67
- outputs: CompilerOutputs;
165
+ output: string;
166
kind: OutputKind;
167
}) {
70
- const prettyOutputs = useMemo(
71
- () => stringifyCompilerOutputs(outputs),
72
- [outputs]
73
- );
74
-
168
return (
169
<div className="w-full h-full">
170
<MonacoEditor
171
path={kind}
172
defaultLanguage="javascript"
80
- value={prettyOutputs[kind] ?? "(Empty)"}
173
+ value={output}
174
options={{
175
...monacoOptions,
176
readOnly: true,
@@ -86,17 +179,3 @@ function TextTabContent({
179
</div>
180
);
181
}
89
-
90
-function GraphTabContent({
91
- outputs,
92
- kind,
93
-}: {
94
- outputs: CompilerOutputs;
95
- kind: OutputKind.ValGraph | OutputKind.SCCGraph | OutputKind.RedGraph;
96
-}) {
97
- return (
98
- <div className="w-full h-full overflow-auto">
99
- <GraphView graphs={outputs[kind]} />
100
- </div>
101
- );
102
-}
compiler/forget/packages/playground/components/Editor/index.tsx
-1
@@ -21,7 +21,6 @@ import {
21
type Store,
22
} from "../../lib/stores";
23
import { useStore, useStoreDispatch } from "../StoreContext";
24
-import CompilerFlagsEditor from "./CompilerFlagsEditor";
24
import Input from "./Input";
25
import Output from "./Output";
26