@samitouri / QOS-React / commits / 9aea37ff5a

Promote temporaries used in JSX to uppercase names

Ensures that temporaries used in JsxExpression tags are named with a capital letter so that they are treated as custom components rather than builtins.

Joe Savona committed Apr 26, 2023 at 11:27 UTC 9aea37ff5af310dbd28a22b0d56e3d4233e8cb24
8 files changed +135 -55
compiler/forget/src/ReactiveScopes/PromoteUsedTemporaries.ts
+37 -2
@@ -5,15 +5,21 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 +import invariant from "invariant";
9 import {
10 + Identifier,
11 + IdentifierId,
12 + InstructionId,
13 ReactiveFunction,
14 ReactiveInstruction,
15 ReactiveScopeBlock,
16 + ReactiveValue,
17 } from "../HIR/HIR";
18 import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
19
20 type VisitorState = {
21 nextId: number;
22 + tags: JsxExpressionTags;
23 };
24 class Visitor extends ReactiveFunctionVisitor<VisitorState> {
25 override visitScope(block: ReactiveScopeBlock, state: VisitorState): void {
@@ -21,7 +27,7 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
27 for (const dep of block.scope.dependencies) {
28 const { identifier } = dep;
29 if (identifier.name == null) {
24 - identifier.name = `t${state.nextId++}`;
30 + promoteTemporary(identifier, state);
31 }
32 }
33 // This is technically optional. We could prune ReactiveScopes
@@ -31,7 +37,7 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
37 // it is better for now to promote (and memoize) every output.
38 for (const [, declaration] of block.scope.declarations) {
39 if (declaration.identifier.name == null) {
34 - declaration.identifier.name = `t${state.nextId++}`;
40 + promoteTemporary(declaration.identifier, state);
41 }
42 }
43 }
@@ -42,9 +48,38 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
48 this.traverseInstruction(instruction, state);
49 }
50 }
51 +
52 +type JsxExpressionTags = Set<IdentifierId>;
53 +class CollectJsxTagsVisitor extends ReactiveFunctionVisitor<JsxExpressionTags> {
54 + override visitValue(
55 + _id: InstructionId,
56 + value: ReactiveValue,
57 + state: JsxExpressionTags
58 + ): void {
59 + if (value.kind === "JsxExpression") {
60 + state.add(value.tag.identifier.id);
61 + }
62 + }
63 +}
64 +
65 export function promoteUsedTemporaries(fn: ReactiveFunction): void {
66 + const tags: JsxExpressionTags = new Set();
67 + visitReactiveFunction(fn, new CollectJsxTagsVisitor(), tags);
68 const state: VisitorState = {
69 nextId: 0,
70 + tags,
71 };
72 visitReactiveFunction(fn, new Visitor(), state);
73 }
74 +
75 +function promoteTemporary(identifier: Identifier, state: VisitorState): void {
76 + invariant(
77 + identifier.name === null,
78 + "Expected to be called only for temporaries"
79 + );
80 + if (state.tags.has(identifier.id)) {
81 + identifier.name = `T${state.nextId++}`;
82 + } else {
83 + identifier.name = `t${state.nextId++}`;
84 + }
85 +}
compiler/forget/src/__tests__/fixtures/compiler/_bug.builtin-jsx-tag-lowered-between-mutations.expect.md new
+41
@@ -0,0 +1,41 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const maybeMutable = new MaybeMutable();
7 + return <div>{maybeMutate(maybeMutable)}</div>;
8 +}
9 +
10 +```
11 +
12 +## Code
13 +
14 +```javascript
15 +import { unstable_useMemoCache as useMemoCache } from "react";
16 +function Component(props) {
17 + const $ = useMemoCache(3);
18 + let T0;
19 + let t1;
20 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
21 + const maybeMutable = new MaybeMutable();
22 + T0 = "div";
23 + t1 = maybeMutate(maybeMutable);
24 + $[0] = T0;
25 + $[1] = t1;
26 + } else {
27 + T0 = $[0];
28 + t1 = $[1];
29 + }
30 + let t2;
31 + if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
32 + t2 = <T0>{t1}</T0>;
33 + $[2] = t2;
34 + } else {
35 + t2 = $[2];
36 + }
37 + return t2;
38 +}
39 +
40 +```
41 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/_bug.builtin-jsx-tag-lowered-between-mutations.js new
+4
@@ -0,0 +1,4 @@
1 +function Component(props) {
2 + const maybeMutable = new MaybeMutable();
3 + return <div>{maybeMutate(maybeMutable)}</div>;
4 +}
compiler/forget/src/__tests__/fixtures/compiler/global-jsx-tag-lowered-between-mutations.expect.md renamed
+5 -5
@@ -26,22 +26,22 @@ function Component(props) {
26 import { unstable_useMemoCache as useMemoCache } from "react";
27 function Component(props) {
28 const $ = useMemoCache(3);
29 - let t0;
29 + let T0;
30 let t1;
31 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
32 const maybeMutable = new MaybeMutable();
33
34 - t0 = View;
34 + T0 = View;
35 t1 = maybeMutate(maybeMutable);
36 - $[0] = t0;
36 + $[0] = T0;
37 $[1] = t1;
38 } else {
39 - t0 = $[0];
39 + T0 = $[0];
40 t1 = $[1];
41 }
42 let t2;
43 if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
44 - t2 = <t0>{t1}</t0>;
44 + t2 = <T0>{t1}</T0>;
45 $[2] = t2;
46 } else {
47 t2 = $[2];
compiler/forget/src/__tests__/fixtures/compiler/global-jsx-tag-lowered-between-mutations.js renamed
compiler/forget/src/__tests__/fixtures/compiler/inner-memo-value-not-promoted-to-outer-scope-dynamic.expect.md
+23 -23
@@ -28,20 +28,20 @@ function Component(props) {
28 const item = useFragment(FRAGMENT, props.item);
29 useFreeze(item);
30 const c_0 = $[0] !== item;
31 - let t1;
31 + let T1;
32 let t2;
33 - let t3;
33 + let T3;
34 let t4;
35 let t0;
36 let t5;
37 - let t6;
37 + let T6;
38 let t7;
39 if (c_0) {
40 const count = new MaybeMutable(item);
41
42 - t6 = View;
42 + T6 = View;
43 t7 = "\n ";
44 - t3 = View;
44 + T3 = View;
45 t4 = "\n ";
46 if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
47 t0 = <span>Text</span>;
@@ -50,39 +50,39 @@ function Component(props) {
50 t0 = $[9];
51 }
52 t5 = "\n ";
53 - t1 = "span";
53 + T1 = "span";
54 t2 = maybeMutate(count);
55 $[0] = item;
56 - $[1] = t1;
56 + $[1] = T1;
57 $[2] = t2;
58 - $[3] = t3;
58 + $[3] = T3;
59 $[4] = t4;
60 $[5] = t0;
61 $[6] = t5;
62 - $[7] = t6;
62 + $[7] = T6;
63 $[8] = t7;
64 } else {
65 - t1 = $[1];
65 + T1 = $[1];
66 t2 = $[2];
67 - t3 = $[3];
67 + T3 = $[3];
68 t4 = $[4];
69 t0 = $[5];
70 t5 = $[6];
71 - t6 = $[7];
71 + T6 = $[7];
72 t7 = $[8];
73 }
74 - const c_10 = $[10] !== t1;
74 + const c_10 = $[10] !== T1;
75 const c_11 = $[11] !== t2;
76 let t8;
77 if (c_10 || c_11) {
78 - t8 = <t1>{t2}</t1>;
79 - $[10] = t1;
78 + t8 = <T1>{t2}</T1>;
79 + $[10] = T1;
80 $[11] = t2;
81 $[12] = t8;
82 } else {
83 t8 = $[12];
84 }
85 - const c_13 = $[13] !== t3;
85 + const c_13 = $[13] !== T3;
86 const c_14 = $[14] !== t4;
87 const c_15 = $[15] !== t0;
88 const c_16 = $[16] !== t5;
@@ -90,14 +90,14 @@ function Component(props) {
90 let t9;
91 if (c_13 || c_14 || c_15 || c_16 || c_17) {
92 t9 = (
93 - <t3>
93 + <T3>
94 {t4}
95 {t0}
96 {t5}
97 {t8}
98 - </t3>
98 + </T3>
99 );
100 - $[13] = t3;
100 + $[13] = T3;
101 $[14] = t4;
102 $[15] = t0;
103 $[16] = t5;
@@ -106,18 +106,18 @@ function Component(props) {
106 } else {
107 t9 = $[18];
108 }
109 - const c_19 = $[19] !== t6;
109 + const c_19 = $[19] !== T6;
110 const c_20 = $[20] !== t7;
111 const c_21 = $[21] !== t9;
112 let t10;
113 if (c_19 || c_20 || c_21) {
114 t10 = (
115 - <t6>
115 + <T6>
116 {t7}
117 {t9}
118 - </t6>
118 + </T6>
119 );
120 - $[19] = t6;
120 + $[19] = T6;
121 $[20] = t7;
122 $[21] = t9;
123 $[22] = t10;
compiler/forget/src/__tests__/fixtures/compiler/inner-memo-value-not-promoted-to-outer-scope-static.expect.md
+17 -17
@@ -22,20 +22,20 @@ function Component(props) {
22 import { unstable_useMemoCache as useMemoCache } from "react";
23 function Component(props) {
24 const $ = useMemoCache(12);
25 - let t1;
25 + let T1;
26 let t2;
27 - let t3;
27 + let T3;
28 let t4;
29 let t0;
30 let t5;
31 - let t6;
31 + let T6;
32 let t7;
33 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
34 const count = new MaybeMutable();
35
36 - t6 = View;
36 + T6 = View;
37 t7 = "\n ";
38 - t3 = View;
38 + T3 = View;
39 t4 = "\n ";
40 if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
41 t0 = <span>Text</span>;
@@ -44,29 +44,29 @@ function Component(props) {
44 t0 = $[8];
45 }
46 t5 = "\n ";
47 - t1 = "span";
47 + T1 = "span";
48 t2 = maybeMutate(count);
49 - $[0] = t1;
49 + $[0] = T1;
50 $[1] = t2;
51 - $[2] = t3;
51 + $[2] = T3;
52 $[3] = t4;
53 $[4] = t0;
54 $[5] = t5;
55 - $[6] = t6;
55 + $[6] = T6;
56 $[7] = t7;
57 } else {
58 - t1 = $[0];
58 + T1 = $[0];
59 t2 = $[1];
60 - t3 = $[2];
60 + T3 = $[2];
61 t4 = $[3];
62 t0 = $[4];
63 t5 = $[5];
64 - t6 = $[6];
64 + T6 = $[6];
65 t7 = $[7];
66 }
67 let t8;
68 if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
69 - t8 = <t1>{t2}</t1>;
69 + t8 = <T1>{t2}</T1>;
70 $[9] = t8;
71 } else {
72 t8 = $[9];
@@ -74,12 +74,12 @@ function Component(props) {
74 let t9;
75 if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
76 t9 = (
77 - <t3>
77 + <T3>
78 {t4}
79 {t0}
80 {t5}
81 {t8}
82 - </t3>
82 + </T3>
83 );
84 $[10] = t9;
85 } else {
@@ -88,10 +88,10 @@ function Component(props) {
88 let t10;
89 if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
90 t10 = (
91 - <t6>
91 + <T6>
92 {t7}
93 {t9}
94 - </t6>
94 + </T6>
95 );
96 $[11] = t10;
97 } else {
compiler/forget/src/__tests__/fixtures/compiler/jsx-tag-evaluation-order-non-global.expect.md
+8 -8
@@ -27,26 +27,26 @@ function Component(props) {
27 const c_0 = $[0] !== props.component;
28 const c_1 = $[1] !== props.alternateComponent;
29 let Tag;
30 - let t0;
30 + let T0;
31 let t1;
32 let t2;
33 if (c_0 || c_1) {
34 const maybeMutable = new MaybeMutable();
35 Tag = props.component;
36
37 - t0 = Tag;
37 + T0 = Tag;
38 t1 = "\n ";
39 Tag = props.alternateComponent;
40 t2 = maybeMutate(maybeMutable);
41 $[0] = props.component;
42 $[1] = props.alternateComponent;
43 $[2] = Tag;
44 - $[3] = t0;
44 + $[3] = T0;
45 $[4] = t1;
46 $[5] = t2;
47 } else {
48 Tag = $[2];
49 - t0 = $[3];
49 + T0 = $[3];
50 t1 = $[4];
51 t2 = $[5];
52 }
@@ -59,20 +59,20 @@ function Component(props) {
59 } else {
60 t3 = $[7];
61 }
62 - const c_8 = $[8] !== t0;
62 + const c_8 = $[8] !== T0;
63 const c_9 = $[9] !== t1;
64 const c_10 = $[10] !== t2;
65 const c_11 = $[11] !== t3;
66 let t4;
67 if (c_8 || c_9 || c_10 || c_11) {
68 t4 = (
69 - <t0>
69 + <T0>
70 {t1}
71 {t2}
72 {t3}
73 - </t0>
73 + </T0>
74 );
75 - $[8] = t0;
75 + $[8] = T0;
76 $[9] = t1;
77 $[10] = t2;
78 $[11] = t3;