Fix using context variable as JSX element tag

Updates the lowering for JSX element tag names to check if the identifier is a 
context or local variable and use the appropriate instruction kind.
This commit is contained in:
Joe Savona
2024-03-21 17:12:08 -07:00
parent 21bb8f9e75
commit 231f29f6f3
6 changed files with 85 additions and 41 deletions
@@ -2916,8 +2916,9 @@ function lowerJsxElementName(
if (exprPath.isJSXIdentifier()) {
const tag: string = exprPath.node.name;
if (tag.match(/^[A-Z]/)) {
const kind = getLoadKind(builder, exprPath);
return lowerValueToTemporary(builder, {
kind: "LoadLocal",
kind: kind,
place: lowerIdentifier(builder, exprPath),
loc: exprLoc,
});
@@ -3245,7 +3246,7 @@ function getStoreKind(
function getLoadKind(
builder: HIRBuilder,
identifier: NodePath<t.Identifier>
identifier: NodePath<t.Identifier | t.JSXIdentifier>
): "LoadLocal" | "LoadContext" {
const isContext = builder.isContextIdentifier(identifier);
return isContext ? "LoadContext" : "LoadLocal";
@@ -267,7 +267,7 @@ export default class HIRBuilder {
return resolvedBinding;
}
isContextIdentifier(path: NodePath<t.Identifier>): boolean {
isContextIdentifier(path: NodePath<t.Identifier | t.JSXIdentifier>): boolean {
const binding = this.#resolveBabelBinding(path);
if (binding) {
return this.#env.isContextIdentifier(binding.identifier);
@@ -0,0 +1,64 @@
## Input
```javascript
import { useMemo } from "react";
import { Stringify } from "shared-runtime";
function Component(props) {
let Component = Stringify;
Component = useMemo(() => {
return Component;
});
return <Component {...props} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ name: "Sathya" }],
};
```
## Code
```javascript
import { useMemo, unstable_useMemoCache as useMemoCache } from "react";
import { Stringify } from "shared-runtime";
function Component(props) {
const $ = useMemoCache(3);
let Component;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
Component = Stringify;
Component;
let t0;
t0 = Component;
Component = t0;
$[0] = Component;
} else {
Component = $[0];
}
let t0;
if ($[1] !== props) {
t0 = <Component {...props} />;
$[1] = props;
$[2] = t0;
} else {
t0 = $[2];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ name: "Sathya" }],
};
```
### Eval output
(kind: ok) <div>{"name":"Sathya"}</div>
@@ -0,0 +1,17 @@
import { useMemo } from "react";
import { Stringify } from "shared-runtime";
function Component(props) {
let Component = Stringify;
Component = useMemo(() => {
return Component;
});
return <Component {...props} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ name: "Sathya" }],
};
@@ -1,29 +0,0 @@
## Input
```javascript
function Component(props) {
let Component = Foo;
Component = useMemo(() => {
return Component;
});
return <Component />;
}
```
## Error
```
6 | });
7 |
> 8 | return <Component />;
| ^^^^^^^^^ [ReactForget] Invariant: Expected all references to a variable to be consistently local or context references. Identifier <unknown> Component$2 is referenced as a local variable, but was previously referenced as a context variable (8:8)
9 | }
10 |
```
@@ -1,9 +0,0 @@
function Component(props) {
let Component = Foo;
Component = useMemo(() => {
return Component;
});
return <Component />;
}