[compiler][patch] Don't wrap non-ascii fbt operands in JSXExpressionContainer

ghstack-source-id: 4b5505aa5e
Pull Request resolved: https://github.com/facebook/react/pull/30389
This commit is contained in:
Mofei Zhang
2024-07-18 16:18:36 -04:00
parent 378ab81522
commit b2ec0445b4
12 changed files with 227 additions and 102 deletions
@@ -262,7 +262,7 @@ function* runWithEnvironment(
value: hir,
});
memoizeFbtOperandsInSameScope(hir);
const fbtOperands = memoizeFbtOperandsInSameScope(hir);
yield log({
kind: "hir",
name: "MemoizeFbtAndMacroOperandsInSameScope",
@@ -484,7 +484,10 @@ function* runWithEnvironment(
validatePreservedManualMemoization(reactiveFunction);
}
const ast = codegenFunction(reactiveFunction, uniqueIdentifiers).unwrap();
const ast = codegenFunction(reactiveFunction, {
uniqueIdentifiers,
fbtOperands,
}).unwrap();
yield log({ kind: "ast", name: "Codegen", value: ast });
for (const outlined of ast.outlined) {
yield log({ kind: "ast", name: "Codegen (outlined)", value: outlined.fn });
@@ -100,12 +100,19 @@ export type CodegenFunction = {
export function codegenFunction(
fn: ReactiveFunction,
uniqueIdentifiers: Set<string>
{
uniqueIdentifiers,
fbtOperands,
}: {
uniqueIdentifiers: Set<string>;
fbtOperands: Set<IdentifierId>;
}
): Result<CodegenFunction, CompilerError> {
const cx = new Context(
fn.env,
fn.id ?? "[[ anonymous ]]",
uniqueIdentifiers,
fbtOperands,
null
);
@@ -281,7 +288,8 @@ export function codegenFunction(
new Context(
cx.env,
reactiveFunction.id ?? "[[ anonymous ]]",
identifiers
identifiers,
cx.fbtOperands
),
reactiveFunction
);
@@ -391,17 +399,20 @@ class Context {
errors: CompilerError = new CompilerError();
objectMethods: Map<IdentifierId, ObjectMethod> = new Map();
uniqueIdentifiers: Set<string>;
fbtOperands: Set<IdentifierId>;
synthesizedNames: Map<string, ValidIdentifierName> = new Map();
constructor(
env: Environment,
fnName: string,
uniqueIdentifiers: Set<string>,
fbtOperands: Set<IdentifierId>,
temporaries: Temporaries | null = null
) {
this.env = env;
this.fnName = fnName;
this.uniqueIdentifiers = uniqueIdentifiers;
this.fbtOperands = fbtOperands;
this.temp = temporaries !== null ? new Map(temporaries) : new Map();
}
get nextCacheIndex(): number {
@@ -1776,6 +1787,7 @@ function codegenInstructionValue(
cx.env,
reactiveFunction.id ?? "[[ anonymous ]]",
cx.uniqueIdentifiers,
cx.fbtOperands,
cx.temp
),
reactiveFunction
@@ -1979,6 +1991,7 @@ function codegenInstructionValue(
cx.env,
reactiveFunction.id ?? "[[ anonymous ]]",
cx.uniqueIdentifiers,
cx.fbtOperands,
cx.temp
),
reactiveFunction
@@ -2229,7 +2242,10 @@ function codegenJsxAttribute(
switch (innerValue.type) {
case "StringLiteral": {
value = innerValue;
if (STRING_REQUIRES_EXPR_CONTAINER_PATTERN.test(value.value)) {
if (
STRING_REQUIRES_EXPR_CONTAINER_PATTERN.test(value.value) &&
!cx.fbtOperands.has(attribute.place.identifier.id)
) {
value = createJsxExpressionContainer(value.loc, value);
}
break;
@@ -39,7 +39,9 @@ import { eachReactiveValueOperand } from "./visitors";
* Users can also specify their own functions to be treated similarly to fbt via the
* `customMacros` environment configuration.
*/
export function memoizeFbtAndMacroOperandsInSameScope(fn: HIRFunction): void {
export function memoizeFbtAndMacroOperandsInSameScope(
fn: HIRFunction
): Set<IdentifierId> {
const fbtMacroTags = new Set([
...FBT_TAGS,
...(fn.env.config.customMacros ?? []),
@@ -52,6 +54,7 @@ export function memoizeFbtAndMacroOperandsInSameScope(fn: HIRFunction): void {
break;
}
}
return fbtValues;
}
export const FBT_TAGS: Set<string> = new Set([
@@ -1,36 +0,0 @@
## Input
```javascript
import fbt from "fbt";
function Component(props) {
const element = (
<fbt desc={"Dialog to show to user"}>
Hello{" "}
<fbt:param
name="a really long description
that got split into multiple lines"
>
{props.name}
</fbt:param>
</fbt>
);
return element.toString();
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ name: "Jason" }],
};
```
## Error
```
Cannot read properties of undefined (reading 'replace')
```
@@ -1,30 +0,0 @@
## Input
```javascript
import fbt from "fbt";
function Component(props) {
const element = (
<fbt desc={"Dialog to show to user"}>
Hello <fbt:param name='"user" name'>{props.name}</fbt:param>
</fbt>
);
return element.toString();
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ name: "Jason" }],
};
```
## Error
```
Property arguments[0] of CallExpression expected node to be of a type ["Expression","SpreadElement","JSXNamespacedName","ArgumentPlaceholder"] but instead got "JSXExpressionContainer"
```
@@ -1,30 +0,0 @@
## Input
```javascript
import fbt from "fbt";
function Component(props) {
const element = (
<fbt desc={"Dialog to show to user"}>
Hello <fbt:param name="user name ☺">{props.name}</fbt:param>
</fbt>
);
return element.toString();
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ name: "Jason" }],
};
```
## Error
```
Property arguments[0] of CallExpression expected node to be of a type ["Expression","SpreadElement","JSXNamespacedName","ArgumentPlaceholder"] but instead got "JSXExpressionContainer"
```
@@ -0,0 +1,75 @@
## Input
```javascript
import fbt from "fbt";
function Component(props) {
const element = (
<fbt desc={"Dialog to show to user"}>
Hello{" "}
<fbt:param
name="a really long description
that got split into multiple lines"
>
{props.name}
</fbt:param>
</fbt>
);
return element.toString();
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ name: "Jason" }],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import fbt from "fbt";
function Component(props) {
const $ = _c(4);
let t0;
if ($[0] !== props.name) {
t0 = fbt._(
"Hello {a really long description that got split into multiple lines}",
[
fbt._param(
"a really long description that got split into multiple lines",
props.name,
),
],
{ hk: "1euPUp" },
);
$[0] = props.name;
$[1] = t0;
} else {
t0 = $[1];
}
const element = t0;
let t1;
if ($[2] !== element) {
t1 = element.toString();
$[2] = element;
$[3] = t1;
} else {
t1 = $[3];
}
return t1;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ name: "Jason" }],
};
```
### Eval output
(kind: ok) "Hello Jason"
@@ -0,0 +1,61 @@
## Input
```javascript
import fbt from "fbt";
function Component(props) {
const element = (
<fbt desc={"Dialog to show to user"}>
Hello <fbt:param name='"user" name'>{props.name}</fbt:param>
</fbt>
);
return element.toString();
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ name: "Jason" }],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import fbt from "fbt";
function Component(props) {
const $ = _c(4);
let t0;
if ($[0] !== props.name) {
t0 = fbt._('Hello {"user" name}', [fbt._param('"user" name', props.name)], {
hk: "S0vMe",
});
$[0] = props.name;
$[1] = t0;
} else {
t0 = $[1];
}
const element = t0;
let t1;
if ($[2] !== element) {
t1 = element.toString();
$[2] = element;
$[3] = t1;
} else {
t1 = $[3];
}
return t1;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ name: "Jason" }],
};
```
### Eval output
(kind: ok) "Hello Jason"
@@ -0,0 +1,63 @@
## Input
```javascript
import fbt from "fbt";
function Component(props) {
const element = (
<fbt desc={"Dialog to show to user"}>
Hello <fbt:param name="user name ☺">{props.name}</fbt:param>
</fbt>
);
return element.toString();
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ name: "Jason" }],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import fbt from "fbt";
function Component(props) {
const $ = _c(4);
let t0;
if ($[0] !== props.name) {
t0 = fbt._(
"Hello {user name ☺}",
[fbt._param("user name \u263A", props.name)],
{ hk: "1En1lp" },
);
$[0] = props.name;
$[1] = t0;
} else {
t0 = $[1];
}
const element = t0;
let t1;
if ($[2] !== element) {
t1 = element.toString();
$[2] = element;
$[3] = t1;
} else {
t1 = $[3];
}
return t1;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ name: "Jason" }],
};
```
### Eval output
(kind: ok) "Hello Jason"