Fix evaluation order for JSX element tags

When lowering a JSX element we were correctly lowering to a temporary in all but 
one case: the common case of an identifier. That is fine in practice but breaks 
in the presence of the tag identifier being reassigned in the props/children. 
This PR fixes to always lower the tag to a temporary.
This commit is contained in:
Joe Savona
2023-04-20 16:48:34 -07:00
parent db378d39f3
commit e9abc41ea3
8 changed files with 95 additions and 74 deletions
+6 -2
View File
@@ -27,7 +27,6 @@ import {
InstructionKind,
InstructionValue,
JsxAttribute,
makeInstructionId,
ObjectPattern,
ObjectProperty,
Place,
@@ -35,6 +34,7 @@ import {
SourceLocation,
SpreadPattern,
ThrowTerminal,
makeInstructionId,
} from "./HIR";
import HIRBuilder, { Bindings } from "./HIRBuilder";
@@ -2041,7 +2041,11 @@ function lowerJsxElementName(
if (exprPath.isJSXIdentifier()) {
const tag: string = exprPath.node.name;
if (tag.match(/^[A-Z]/)) {
return lowerIdentifier(builder, exprPath);
return lowerValueToTemporary(builder, {
kind: "LoadLocal",
place: lowerIdentifier(builder, exprPath),
loc: exprLoc,
});
} else {
if (tag.indexOf(":") !== -1) {
builder.errors.push({
@@ -1,68 +0,0 @@
## Input
```javascript
function Component(props) {
const maybeMutable = new MaybeMutable();
let Tag = View;
// NOTE: the order of evaluation in the lowering is incorrect:
// the jsx element's tag observes `Tag` after reassignment, but should observe
// it before the reassignment.
return (
<Tag>
{((Tag = HScroll), maybeMutate(maybeMutable))}
<Tag />
</Tag>
);
}
```
## Code
```javascript
import * as React from "react";
function Component(props) {
const $ = React.unstable_useMemoCache(5);
let Tag;
let t0;
let t1;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const maybeMutable = new MaybeMutable();
t0 = "\n ";
Tag = HScroll;
t1 = maybeMutate(maybeMutable);
$[0] = Tag;
$[1] = t0;
$[2] = t1;
} else {
Tag = $[0];
t0 = $[1];
t1 = $[2];
}
let t2;
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
t2 = <Tag />;
$[3] = t2;
} else {
t2 = $[3];
}
let t3;
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
t3 = (
<Tag>
{t0}
{t1}
{t2}
</Tag>
);
$[4] = t3;
} else {
t3 = $[4];
}
return t3;
}
```
@@ -0,0 +1,28 @@
## Input
```javascript
function Component(props) {
const maybeMutable = new MaybeMutable();
let Tag = props.component;
// NOTE: the order of evaluation in the lowering is incorrect:
// the jsx element's tag observes `Tag` after reassignment, but should observe
// it before the reassignment.
return (
<Tag>
{((Tag = props.alternateComponent), maybeMutate(maybeMutable))}
<Tag />
</Tag>
);
}
```
## Error
```
[ReactForget] Invariant: [Codegen] No value found for temporary. Value for 'read $33' was not set in the codegen context (8:8)
```
@@ -1,12 +1,12 @@
function Component(props) {
const maybeMutable = new MaybeMutable();
let Tag = View;
let Tag = props.component;
// NOTE: the order of evaluation in the lowering is incorrect:
// the jsx element's tag observes `Tag` after reassignment, but should observe
// it before the reassignment.
return (
<Tag>
{((Tag = HScroll), maybeMutate(maybeMutable))}
{((Tag = props.alternateComponent), maybeMutate(maybeMutable))}
<Tag />
</Tag>
);
@@ -25,7 +25,7 @@ function Component(props) {
## Error
```
[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $43:TObject<BuiltInArray> (frozen) (13:13)
[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $46:TObject<BuiltInArray> (frozen) (13:13)
```
@@ -19,7 +19,7 @@ function Component(props) {
## Error
```
[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $26:TObject<BuiltInArray> (frozen) (7:7)
[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $28:TObject<BuiltInArray> (frozen) (7:7)
```
@@ -0,0 +1,48 @@
## Input
```javascript
function Component(props) {
let Tag = View;
return (
<Tag>
{((Tag = HScroll), props.value)}
<Tag />
</Tag>
);
}
```
## Code
```javascript
import * as React from "react";
function Component(props) {
const $ = React.unstable_useMemoCache(3);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = <HScroll />;
$[0] = t0;
} else {
t0 = $[0];
}
const c_1 = $[1] !== props.value;
let t1;
if (c_1) {
t1 = (
<View>
{props.value}
{t0}
</View>
);
$[1] = props.value;
$[2] = t1;
} else {
t1 = $[2];
}
return t1;
}
```
@@ -0,0 +1,9 @@
function Component(props) {
let Tag = View;
return (
<Tag>
{((Tag = HScroll), props.value)}
<Tag />
</Tag>
);
}