mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[compiler] Lower JSXMemberExpression with LoadLocal
`JSXMemberExpression` is currently the only instruction (that I know of) that directly references identifier lvalues without a corresponding `LoadLocal`. This has some side effects: - deadcode elimination and constant propagation now reach JSXMemberExpressions - we can delete `LoweredFunction.dependencies` without dangling references (previously, the only reference to JSXMemberExpression objects in HIR was in function dependencies) - JSXMemberExpression now is consistent with all other instructions (e.g. has a rvalue-producing LoadLocal) '
This commit is contained in:
@@ -3186,7 +3186,13 @@ function lowerJsxMemberExpression(
|
||||
loc: object.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
objectPlace = lowerIdentifier(builder, object);
|
||||
|
||||
const kind = getLoadKind(builder, object);
|
||||
objectPlace = lowerValueToTemporary(builder, {
|
||||
kind: kind,
|
||||
place: lowerIdentifier(builder, object),
|
||||
loc: exprPath.node.loc ?? GeneratedSource,
|
||||
});
|
||||
}
|
||||
const property = exprPath.get('property').node.name;
|
||||
return lowerValueToTemporary(builder, {
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Throw} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Note: this is disabled in the evaluator due to different devmode errors.
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) <invalidtag val="[object Object]"></invalidtag>
|
||||
* logs: ['Warning: <%s /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.%s','invalidTag']
|
||||
*
|
||||
* Forget:
|
||||
* (kind: ok) <invalidtag val="[object Object]"></invalidtag>
|
||||
* logs: [
|
||||
* 'Warning: <%s /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.%s','invalidTag',
|
||||
* 'Warning: The tag <%s> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.%s','invalidTag',
|
||||
* ]
|
||||
*/
|
||||
function useFoo() {
|
||||
const invalidTag = Throw;
|
||||
/**
|
||||
* Need to be careful to not parse `invalidTag` as a localVar (i.e. render
|
||||
* Throw). Note that the jsx transform turns this into a string tag:
|
||||
* `jsx("invalidTag"...
|
||||
*/
|
||||
return <invalidTag val={{val: 2}} />;
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Throw } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Note: this is disabled in the evaluator due to different devmode errors.
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) <invalidtag val="[object Object]"></invalidtag>
|
||||
* logs: ['Warning: <%s /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.%s','invalidTag']
|
||||
*
|
||||
* Forget:
|
||||
* (kind: ok) <invalidtag val="[object Object]"></invalidtag>
|
||||
* logs: [
|
||||
* 'Warning: <%s /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.%s','invalidTag',
|
||||
* 'Warning: The tag <%s> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.%s','invalidTag',
|
||||
* ]
|
||||
*/
|
||||
function useFoo() {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <invalidTag val={{ val: 2 }} />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import {Throw} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Note: this is disabled in the evaluator due to different devmode errors.
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) <invalidtag val="[object Object]"></invalidtag>
|
||||
* logs: ['Warning: <%s /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.%s','invalidTag']
|
||||
*
|
||||
* Forget:
|
||||
* (kind: ok) <invalidtag val="[object Object]"></invalidtag>
|
||||
* logs: [
|
||||
* 'Warning: <%s /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.%s','invalidTag',
|
||||
* 'Warning: The tag <%s> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.%s','invalidTag',
|
||||
* ]
|
||||
*/
|
||||
function useFoo() {
|
||||
const invalidTag = Throw;
|
||||
/**
|
||||
* Need to be careful to not parse `invalidTag` as a localVar (i.e. render
|
||||
* Throw). Note that the jsx transform turns this into a string tag:
|
||||
* `jsx("invalidTag"...
|
||||
*/
|
||||
return <invalidTag val={{val: 2}} />;
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [],
|
||||
};
|
||||
+1
-2
@@ -27,11 +27,10 @@ import * as SharedRuntime from "shared-runtime";
|
||||
function useFoo(t0) {
|
||||
const $ = _c(1);
|
||||
const { cond } = t0;
|
||||
const MyLocal = SharedRuntime;
|
||||
if (cond) {
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = <MyLocal.Text value={4} />;
|
||||
t1 = <SharedRuntime.Text value={4} />;
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
|
||||
+1
-2
@@ -22,10 +22,9 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import * as SharedRuntime from "shared-runtime";
|
||||
function useFoo() {
|
||||
const $ = _c(1);
|
||||
const MyLocal = SharedRuntime;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <MyLocal.Text value={4} />;
|
||||
t0 = <SharedRuntime.Text value={4} />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import * as SharedRuntime from 'shared-runtime';
|
||||
import {invoke} from 'shared-runtime';
|
||||
function useComponentFactory({name}) {
|
||||
const localVar = SharedRuntime;
|
||||
const cb = () => <localVar.Stringify>hello world {name}</localVar.Stringify>;
|
||||
return invoke(cb);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useComponentFactory,
|
||||
params: [{name: 'sathya'}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import * as SharedRuntime from "shared-runtime";
|
||||
import { invoke } from "shared-runtime";
|
||||
function useComponentFactory(t0) {
|
||||
const $ = _c(4);
|
||||
const { name } = t0;
|
||||
let t1;
|
||||
if ($[0] !== name) {
|
||||
t1 = () => (
|
||||
<SharedRuntime.Stringify>hello world {name}</SharedRuntime.Stringify>
|
||||
);
|
||||
$[0] = name;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb = t1;
|
||||
let t2;
|
||||
if ($[2] !== cb) {
|
||||
t2 = invoke(cb);
|
||||
$[2] = cb;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useComponentFactory,
|
||||
params: [{ name: "sathya" }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"children":["hello world ","sathya"]}</div>
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import * as SharedRuntime from 'shared-runtime';
|
||||
import {invoke} from 'shared-runtime';
|
||||
function useComponentFactory({name}) {
|
||||
const localVar = SharedRuntime;
|
||||
const cb = () => <localVar.Stringify>hello world {name}</localVar.Stringify>;
|
||||
return invoke(cb);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useComponentFactory,
|
||||
params: [{name: 'sathya'}],
|
||||
};
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import * as SharedRuntime from 'shared-runtime';
|
||||
function Component({name}) {
|
||||
const localVar = SharedRuntime;
|
||||
return <localVar.Stringify>hello world {name}</localVar.Stringify>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{name: 'sathya'}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import * as SharedRuntime from "shared-runtime";
|
||||
function Component(t0) {
|
||||
const $ = _c(2);
|
||||
const { name } = t0;
|
||||
let t1;
|
||||
if ($[0] !== name) {
|
||||
t1 = <SharedRuntime.Stringify>hello world {name}</SharedRuntime.Stringify>;
|
||||
$[0] = name;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ name: "sathya" }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"children":["hello world ","sathya"]}</div>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import * as SharedRuntime from 'shared-runtime';
|
||||
function Component({name}) {
|
||||
const localVar = SharedRuntime;
|
||||
return <localVar.Stringify>hello world {name}</localVar.Stringify>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{name: 'sathya'}],
|
||||
};
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import * as SharedRuntime from 'shared-runtime';
|
||||
function Component({name}) {
|
||||
return <SharedRuntime.Stringify>hello world {name}</SharedRuntime.Stringify>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{name: 'sathya'}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import * as SharedRuntime from "shared-runtime";
|
||||
function Component(t0) {
|
||||
const $ = _c(2);
|
||||
const { name } = t0;
|
||||
let t1;
|
||||
if ($[0] !== name) {
|
||||
t1 = <SharedRuntime.Stringify>hello world {name}</SharedRuntime.Stringify>;
|
||||
$[0] = name;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ name: "sathya" }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"children":["hello world ","sathya"]}</div>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import * as SharedRuntime from 'shared-runtime';
|
||||
function Component({name}) {
|
||||
return <SharedRuntime.Stringify>hello world {name}</SharedRuntime.Stringify>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{name: 'sathya'}],
|
||||
};
|
||||
+1
-2
@@ -25,10 +25,9 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import * as SharedRuntime from "shared-runtime";
|
||||
function useFoo() {
|
||||
const $ = _c(1);
|
||||
const MyLocal = SharedRuntime;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const callback = () => <MyLocal.Text value={4} />;
|
||||
const callback = () => <SharedRuntime.Text value={4} />;
|
||||
|
||||
t0 = callback();
|
||||
$[0] = t0;
|
||||
|
||||
@@ -475,6 +475,9 @@ const skipFilter = new Set([
|
||||
'rules-of-hooks/rules-of-hooks-93dc5d5e538a',
|
||||
'rules-of-hooks/rules-of-hooks-69521d94fa03',
|
||||
|
||||
// false positives
|
||||
'invalid-jsx-lowercase-localvar',
|
||||
|
||||
// bugs
|
||||
'fbt/bug-fbt-plural-multiple-function-calls',
|
||||
'fbt/bug-fbt-plural-multiple-mixed-call-tag',
|
||||
|
||||
@@ -252,6 +252,9 @@ export function Stringify(props: any): React.ReactElement {
|
||||
toJSON(props, props?.shouldInvokeFns),
|
||||
);
|
||||
}
|
||||
export function Throw() {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
export function ValidateMemoization({
|
||||
inputs,
|
||||
|
||||
Reference in New Issue
Block a user