[compiler][patch] Don't wrap non-ascii fbt operands in JSXExpressionContainer
ghstack-source-id: 4b5505aa5e58d1bcc0813452762e9921c74417d7 Pull Request resolved: https://github.com/facebook/react/pull/30389
Mofei Zhang committed
Jul 18, 2024 at 15:33 UTC
b2ec0445b4801e067f624adb2a29afcd06d1de55
12 files changed
+227
-102
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+5
-2
@@ -262,7 +262,7 @@ function* runWithEnvironment(
262
value: hir,
263
});
264
265
- memoizeFbtOperandsInSameScope(hir);
265
+ const fbtOperands = memoizeFbtOperandsInSameScope(hir);
266
yield log({
267
kind: "hir",
268
name: "MemoizeFbtAndMacroOperandsInSameScope",
@@ -484,7 +484,10 @@ function* runWithEnvironment(
484
validatePreservedManualMemoization(reactiveFunction);
485
}
486
487
- const ast = codegenFunction(reactiveFunction, uniqueIdentifiers).unwrap();
487
+ const ast = codegenFunction(reactiveFunction, {
488
+ uniqueIdentifiers,
489
+ fbtOperands,
490
+ }).unwrap();
491
yield log({ kind: "ast", name: "Codegen", value: ast });
492
for (const outlined of ast.outlined) {
493
yield log({ kind: "ast", name: "Codegen (outlined)", value: outlined.fn });
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
+19
-3
@@ -100,12 +100,19 @@ export type CodegenFunction = {
100
101
export function codegenFunction(
102
fn: ReactiveFunction,
103
- uniqueIdentifiers: Set<string>
103
+ {
104
+ uniqueIdentifiers,
105
+ fbtOperands,
106
+ }: {
107
+ uniqueIdentifiers: Set<string>;
108
+ fbtOperands: Set<IdentifierId>;
109
+ }
110
): Result<CodegenFunction, CompilerError> {
111
const cx = new Context(
112
fn.env,
113
fn.id ?? "[[ anonymous ]]",
114
uniqueIdentifiers,
115
+ fbtOperands,
116
null
117
);
118
@@ -281,7 +288,8 @@ export function codegenFunction(
288
new Context(
289
cx.env,
290
reactiveFunction.id ?? "[[ anonymous ]]",
284
- identifiers
291
+ identifiers,
292
+ cx.fbtOperands
293
),
294
reactiveFunction
295
);
@@ -391,17 +399,20 @@ class Context {
399
errors: CompilerError = new CompilerError();
400
objectMethods: Map<IdentifierId, ObjectMethod> = new Map();
401
uniqueIdentifiers: Set<string>;
402
+ fbtOperands: Set<IdentifierId>;
403
synthesizedNames: Map<string, ValidIdentifierName> = new Map();
404
405
constructor(
406
env: Environment,
407
fnName: string,
408
uniqueIdentifiers: Set<string>,
409
+ fbtOperands: Set<IdentifierId>,
410
temporaries: Temporaries | null = null
411
) {
412
this.env = env;
413
this.fnName = fnName;
414
this.uniqueIdentifiers = uniqueIdentifiers;
415
+ this.fbtOperands = fbtOperands;
416
this.temp = temporaries !== null ? new Map(temporaries) : new Map();
417
}
418
get nextCacheIndex(): number {
@@ -1776,6 +1787,7 @@ function codegenInstructionValue(
1787
cx.env,
1788
reactiveFunction.id ?? "[[ anonymous ]]",
1789
cx.uniqueIdentifiers,
1790
+ cx.fbtOperands,
1791
cx.temp
1792
),
1793
reactiveFunction
@@ -1979,6 +1991,7 @@ function codegenInstructionValue(
1991
cx.env,
1992
reactiveFunction.id ?? "[[ anonymous ]]",
1993
cx.uniqueIdentifiers,
1994
+ cx.fbtOperands,
1995
cx.temp
1996
),
1997
reactiveFunction
@@ -2229,7 +2242,10 @@ function codegenJsxAttribute(
2242
switch (innerValue.type) {
2243
case "StringLiteral": {
2244
value = innerValue;
2232
- if (STRING_REQUIRES_EXPR_CONTAINER_PATTERN.test(value.value)) {
2245
+ if (
2246
+ STRING_REQUIRES_EXPR_CONTAINER_PATTERN.test(value.value) &&
2247
+ !cx.fbtOperands.has(attribute.place.identifier.id)
2248
+ ) {
2249
value = createJsxExpressionContainer(value.loc, value);
2250
}
2251
break;
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MemoizeFbtAndMacroOperandsInSameScope.ts
+4
-1
@@ -39,7 +39,9 @@ import { eachReactiveValueOperand } from "./visitors";
39
* Users can also specify their own functions to be treated similarly to fbt via the
40
* `customMacros` environment configuration.
41
*/
42
-export function memoizeFbtAndMacroOperandsInSameScope(fn: HIRFunction): void {
42
+export function memoizeFbtAndMacroOperandsInSameScope(
43
+ fn: HIRFunction
44
+): Set<IdentifierId> {
45
const fbtMacroTags = new Set([
46
...FBT_TAGS,
47
...(fn.env.config.customMacros ?? []),
@@ -52,6 +54,7 @@ export function memoizeFbtAndMacroOperandsInSameScope(fn: HIRFunction): void {
54
break;
55
}
56
}
57
+ return fbtValues;
58
}
59
60
export const FBT_TAGS: Set<string> = new Set([
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-fbt-param-with-newline.expect.md
deleted
-36
@@ -1,36 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-import fbt from "fbt";
6
-
7
-function Component(props) {
8
- const element = (
9
- <fbt desc={"Dialog to show to user"}>
10
- Hello{" "}
11
- <fbt:param
12
- name="a really long description
13
- that got split into multiple lines"
14
- >
15
- {props.name}
16
- </fbt:param>
17
- </fbt>
18
- );
19
- return element.toString();
20
-}
21
-
22
-export const FIXTURE_ENTRYPOINT = {
23
- fn: Component,
24
- params: [{ name: "Jason" }],
25
-};
26
-
27
-```
28
-
29
-
30
-## Error
31
-
32
-```
33
-Cannot read properties of undefined (reading 'replace')
34
-```
35
-
36
-
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-fbt-param-with-quotes.expect.md
deleted
-30
@@ -1,30 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-import fbt from "fbt";
6
-
7
-function Component(props) {
8
- const element = (
9
- <fbt desc={"Dialog to show to user"}>
10
- Hello <fbt:param name='"user" name'>{props.name}</fbt:param>
11
- </fbt>
12
- );
13
- return element.toString();
14
-}
15
-
16
-export const FIXTURE_ENTRYPOINT = {
17
- fn: Component,
18
- params: [{ name: "Jason" }],
19
-};
20
-
21
-```
22
-
23
-
24
-## Error
25
-
26
-```
27
-Property arguments[0] of CallExpression expected node to be of a type ["Expression","SpreadElement","JSXNamespacedName","ArgumentPlaceholder"] but instead got "JSXExpressionContainer"
28
-```
29
-
30
-
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-fbt-param-with-unicode.expect.md
deleted
-30
@@ -1,30 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-import fbt from "fbt";
6
-
7
-function Component(props) {
8
- const element = (
9
- <fbt desc={"Dialog to show to user"}>
10
- Hello <fbt:param name="user name ☺">{props.name}</fbt:param>
11
- </fbt>
12
- );
13
- return element.toString();
14
-}
15
-
16
-export const FIXTURE_ENTRYPOINT = {
17
- fn: Component,
18
- params: [{ name: "Jason" }],
19
-};
20
-
21
-```
22
-
23
-
24
-## Error
25
-
26
-```
27
-Property arguments[0] of CallExpression expected node to be of a type ["Expression","SpreadElement","JSXNamespacedName","ArgumentPlaceholder"] but instead got "JSXExpressionContainer"
28
-```
29
-
30
-
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-newline.expect.md
new
+75
@@ -0,0 +1,75 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import fbt from "fbt";
6
+
7
+function Component(props) {
8
+ const element = (
9
+ <fbt desc={"Dialog to show to user"}>
10
+ Hello{" "}
11
+ <fbt:param
12
+ name="a really long description
13
+ that got split into multiple lines"
14
+ >
15
+ {props.name}
16
+ </fbt:param>
17
+ </fbt>
18
+ );
19
+ return element.toString();
20
+}
21
+
22
+export const FIXTURE_ENTRYPOINT = {
23
+ fn: Component,
24
+ params: [{ name: "Jason" }],
25
+};
26
+
27
+```
28
+
29
+## Code
30
+
31
+```javascript
32
+import { c as _c } from "react/compiler-runtime";
33
+import fbt from "fbt";
34
+
35
+function Component(props) {
36
+ const $ = _c(4);
37
+ let t0;
38
+ if ($[0] !== props.name) {
39
+ t0 = fbt._(
40
+ "Hello {a really long description that got split into multiple lines}",
41
+ [
42
+ fbt._param(
43
+ "a really long description that got split into multiple lines",
44
+
45
+ props.name,
46
+ ),
47
+ ],
48
+ { hk: "1euPUp" },
49
+ );
50
+ $[0] = props.name;
51
+ $[1] = t0;
52
+ } else {
53
+ t0 = $[1];
54
+ }
55
+ const element = t0;
56
+ let t1;
57
+ if ($[2] !== element) {
58
+ t1 = element.toString();
59
+ $[2] = element;
60
+ $[3] = t1;
61
+ } else {
62
+ t1 = $[3];
63
+ }
64
+ return t1;
65
+}
66
+
67
+export const FIXTURE_ENTRYPOINT = {
68
+ fn: Component,
69
+ params: [{ name: "Jason" }],
70
+};
71
+
72
+```
73
+
74
+### Eval output
75
+(kind: ok) "Hello Jason"
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-newline.js
renamed
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-quotes.expect.md
new
+61
@@ -0,0 +1,61 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import fbt from "fbt";
6
+
7
+function Component(props) {
8
+ const element = (
9
+ <fbt desc={"Dialog to show to user"}>
10
+ Hello <fbt:param name='"user" name'>{props.name}</fbt:param>
11
+ </fbt>
12
+ );
13
+ return element.toString();
14
+}
15
+
16
+export const FIXTURE_ENTRYPOINT = {
17
+ fn: Component,
18
+ params: [{ name: "Jason" }],
19
+};
20
+
21
+```
22
+
23
+## Code
24
+
25
+```javascript
26
+import { c as _c } from "react/compiler-runtime";
27
+import fbt from "fbt";
28
+
29
+function Component(props) {
30
+ const $ = _c(4);
31
+ let t0;
32
+ if ($[0] !== props.name) {
33
+ t0 = fbt._('Hello {"user" name}', [fbt._param('"user" name', props.name)], {
34
+ hk: "S0vMe",
35
+ });
36
+ $[0] = props.name;
37
+ $[1] = t0;
38
+ } else {
39
+ t0 = $[1];
40
+ }
41
+ const element = t0;
42
+ let t1;
43
+ if ($[2] !== element) {
44
+ t1 = element.toString();
45
+ $[2] = element;
46
+ $[3] = t1;
47
+ } else {
48
+ t1 = $[3];
49
+ }
50
+ return t1;
51
+}
52
+
53
+export const FIXTURE_ENTRYPOINT = {
54
+ fn: Component,
55
+ params: [{ name: "Jason" }],
56
+};
57
+
58
+```
59
+
60
+### Eval output
61
+(kind: ok) "Hello Jason"
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-quotes.js
renamed
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-unicode.expect.md
new
+63
@@ -0,0 +1,63 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import fbt from "fbt";
6
+
7
+function Component(props) {
8
+ const element = (
9
+ <fbt desc={"Dialog to show to user"}>
10
+ Hello <fbt:param name="user name ☺">{props.name}</fbt:param>
11
+ </fbt>
12
+ );
13
+ return element.toString();
14
+}
15
+
16
+export const FIXTURE_ENTRYPOINT = {
17
+ fn: Component,
18
+ params: [{ name: "Jason" }],
19
+};
20
+
21
+```
22
+
23
+## Code
24
+
25
+```javascript
26
+import { c as _c } from "react/compiler-runtime";
27
+import fbt from "fbt";
28
+
29
+function Component(props) {
30
+ const $ = _c(4);
31
+ let t0;
32
+ if ($[0] !== props.name) {
33
+ t0 = fbt._(
34
+ "Hello {user name ☺}",
35
+ [fbt._param("user name \u263A", props.name)],
36
+ { hk: "1En1lp" },
37
+ );
38
+ $[0] = props.name;
39
+ $[1] = t0;
40
+ } else {
41
+ t0 = $[1];
42
+ }
43
+ const element = t0;
44
+ let t1;
45
+ if ($[2] !== element) {
46
+ t1 = element.toString();
47
+ $[2] = element;
48
+ $[3] = t1;
49
+ } else {
50
+ t1 = $[3];
51
+ }
52
+ return t1;
53
+}
54
+
55
+export const FIXTURE_ENTRYPOINT = {
56
+ fn: Component,
57
+ params: [{ name: "Jason" }],
58
+};
59
+
60
+```
61
+
62
+### Eval output
63
+(kind: ok) "Hello Jason"
\ No newline at end of file