@samitouri / QOS-React-2 / commits / 231f29f6f3

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.

Joe Savona committed Mar 21, 2024 at 17:12 UTC 231f29f6f3ef5c8612cfbb050695d7a1e96ebd78
6 files changed +85 -41
compiler/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts
+3 -2
@@ -2916,8 +2916,9 @@ function lowerJsxElementName(
2916 if (exprPath.isJSXIdentifier()) {
2917 const tag: string = exprPath.node.name;
2918 if (tag.match(/^[A-Z]/)) {
2919 + const kind = getLoadKind(builder, exprPath);
2920 return lowerValueToTemporary(builder, {
2920 - kind: "LoadLocal",
2921 + kind: kind,
2922 place: lowerIdentifier(builder, exprPath),
2923 loc: exprLoc,
2924 });
@@ -3245,7 +3246,7 @@ function getStoreKind(
3246
3247 function getLoadKind(
3248 builder: HIRBuilder,
3248 - identifier: NodePath<t.Identifier>
3249 + identifier: NodePath<t.Identifier | t.JSXIdentifier>
3250 ): "LoadLocal" | "LoadContext" {
3251 const isContext = builder.isContextIdentifier(identifier);
3252 return isContext ? "LoadContext" : "LoadLocal";
compiler/packages/babel-plugin-react-forget/src/HIR/HIRBuilder.ts
+1 -1
@@ -267,7 +267,7 @@ export default class HIRBuilder {
267 return resolvedBinding;
268 }
269
270 - isContextIdentifier(path: NodePath<t.Identifier>): boolean {
270 + isContextIdentifier(path: NodePath<t.Identifier | t.JSXIdentifier>): boolean {
271 const binding = this.#resolveBabelBinding(path);
272 if (binding) {
273 return this.#env.isContextIdentifier(binding.identifier);
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-as-jsx-element-tag.expect.md new
+64
@@ -0,0 +1,64 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { useMemo } from "react";
6 +import { Stringify } from "shared-runtime";
7 +
8 +function Component(props) {
9 + let Component = Stringify;
10 +
11 + Component = useMemo(() => {
12 + return Component;
13 + });
14 +
15 + return <Component {...props} />;
16 +}
17 +
18 +export const FIXTURE_ENTRYPOINT = {
19 + fn: Component,
20 + params: [{ name: "Sathya" }],
21 +};
22 +
23 +```
24 +
25 +## Code
26 +
27 +```javascript
28 +import { useMemo, unstable_useMemoCache as useMemoCache } from "react";
29 +import { Stringify } from "shared-runtime";
30 +
31 +function Component(props) {
32 + const $ = useMemoCache(3);
33 + let Component;
34 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
35 + Component = Stringify;
36 +
37 + Component;
38 + let t0;
39 + t0 = Component;
40 + Component = t0;
41 + $[0] = Component;
42 + } else {
43 + Component = $[0];
44 + }
45 + let t0;
46 + if ($[1] !== props) {
47 + t0 = <Component {...props} />;
48 + $[1] = props;
49 + $[2] = t0;
50 + } else {
51 + t0 = $[2];
52 + }
53 + return t0;
54 +}
55 +
56 +export const FIXTURE_ENTRYPOINT = {
57 + fn: Component,
58 + params: [{ name: "Sathya" }],
59 +};
60 +
61 +```
62 +
63 +### Eval output
64 +(kind: ok) <div>{"name":"Sathya"}</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-as-jsx-element-tag.js new
+17
@@ -0,0 +1,17 @@
1 +import { useMemo } from "react";
2 +import { Stringify } from "shared-runtime";
3 +
4 +function Component(props) {
5 + let Component = Stringify;
6 +
7 + Component = useMemo(() => {
8 + return Component;
9 + });
10 +
11 + return <Component {...props} />;
12 +}
13 +
14 +export const FIXTURE_ENTRYPOINT = {
15 + fn: Component,
16 + params: [{ name: "Sathya" }],
17 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-context-variable-as-jsx-element-tag.expect.md deleted
-29
@@ -1,29 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -function Component(props) {
6 - let Component = Foo;
7 -
8 - Component = useMemo(() => {
9 - return Component;
10 - });
11 -
12 - return <Component />;
13 -}
14 -
15 -```
16 -
17 -
18 -## Error
19 -
20 -```
21 - 6 | });
22 - 7 |
23 -> 8 | return <Component />;
24 - | ^^^^^^^^^ [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)
25 - 9 | }
26 - 10 |
27 -```
28 -
29 -
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-context-variable-as-jsx-element-tag.js deleted
-9
@@ -1,9 +0,0 @@
1 -function Component(props) {
2 - let Component = Foo;
3 -
4 - Component = useMemo(() => {
5 - return Component;
6 - });
7 -
8 - return <Component />;
9 -}