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;