@samitouri / QOS-React-2 / commits / 8f18b8233f

[patch][contextvars] Patch for variables reassigned after objectmethod definitions

[patch][contextvars] Patch for variables reassigned after objectmethod definitions

Mofei Zhang committed Jan 18, 2024 at 18:29 UTC 8f18b8233f2e68d00e8d175a8bed61c9db601f50
5 files changed +183 -66
compiler/packages/babel-plugin-react-forget/src/HIR/FindContextIdentifiers.ts
+29 -66
@@ -11,71 +11,44 @@ import { CompilerError } from "../CompilerError";
11 import { Set_union } from "../Utils/utils";
12 import { GeneratedSource } from "./HIR";
13
14 +type BabelFunction =
15 + | NodePath<t.FunctionDeclaration>
16 + | NodePath<t.FunctionExpression>
17 + | NodePath<t.ArrowFunctionExpression>
18 + | NodePath<t.ObjectMethod>;
19 type FindContextIdentifierState = {
15 - currentLambda: Array<
16 - | NodePath<t.FunctionDeclaration>
17 - | NodePath<t.FunctionExpression>
18 - | NodePath<t.ArrowFunctionExpression>
19 - | NodePath<t.ObjectMethod>
20 - >;
20 + currentFn: Array<BabelFunction>;
21 reassigned: Set<t.Identifier>;
22 referenced: Set<t.Identifier>;
23 };
24
25 +const withFunctionScope = {
26 + enter: function (
27 + path: BabelFunction,
28 + state: FindContextIdentifierState
29 + ): void {
30 + state.currentFn.push(path);
31 + },
32 + exit: function (_: BabelFunction, state: FindContextIdentifierState): void {
33 + state.currentFn.pop();
34 + },
35 +};
36 +
37 export function findContextIdentifiers(
38 func: NodePath<t.Function>
39 ): Set<t.Identifier> {
40 const state: FindContextIdentifierState = {
29 - currentLambda: [],
41 + currentFn: [],
42 reassigned: new Set(),
43 referenced: new Set(),
44 };
45
46 func.traverse<FindContextIdentifierState>(
47 {
36 - FunctionDeclaration: {
37 - enter(
38 - fn: NodePath<t.FunctionDeclaration>,
39 - state: FindContextIdentifierState
40 - ): void {
41 - state.currentLambda.push(fn);
42 - },
43 - exit(
44 - fn: NodePath<t.FunctionDeclaration>,
45 - state: FindContextIdentifierState
46 - ): void {
47 - state.currentLambda.pop();
48 - },
49 - },
50 - FunctionExpression: {
51 - enter(
52 - fn: NodePath<t.FunctionExpression>,
53 - state: FindContextIdentifierState
54 - ): void {
55 - state.currentLambda.push(fn);
56 - },
57 - exit(
58 - _fn: NodePath<t.FunctionExpression>,
59 - state: FindContextIdentifierState
60 - ): void {
61 - state.currentLambda.pop();
62 - },
63 - },
64 -
65 - ArrowFunctionExpression: {
66 - enter(
67 - fn: NodePath<t.ArrowFunctionExpression>,
68 - state: FindContextIdentifierState
69 - ): void {
70 - state.currentLambda.push(fn);
71 - },
72 - exit(
73 - _fn: NodePath<t.ArrowFunctionExpression>,
74 - state: FindContextIdentifierState
75 - ): void {
76 - state.currentLambda.pop();
77 - },
78 - },
48 + FunctionDeclaration: withFunctionScope,
49 + FunctionExpression: withFunctionScope,
50 + ArrowFunctionExpression: withFunctionScope,
51 + ObjectMethod: withFunctionScope,
52 AssignmentExpression(
53 path: NodePath<t.AssignmentExpression>,
54 state: FindContextIdentifierState
@@ -83,19 +56,13 @@ export function findContextIdentifiers(
56 const left = path.get("left");
57 handleAssignment(state.reassigned, left);
58 },
86 - ObjectMethod(
87 - fn: NodePath<t.ObjectMethod>,
88 - state: FindContextIdentifierState
89 - ): void {
90 - state.currentLambda.push(fn);
91 - },
59 Identifier(
60 path: NodePath<t.Identifier>,
61 state: FindContextIdentifierState
62 ): void {
96 - const currentLambda = state.currentLambda.at(-1);
97 - if (currentLambda !== undefined)
98 - handleIdentifier(currentLambda, state.referenced, path);
63 + const currentFn = state.currentFn.at(-1);
64 + if (currentFn !== undefined)
65 + handleIdentifier(currentFn, state.referenced, path);
66 },
67 },
68 state
@@ -104,17 +71,13 @@ export function findContextIdentifiers(
71 }
72
73 function handleIdentifier(
107 - currentLambda:
108 - | NodePath<t.FunctionDeclaration>
109 - | NodePath<t.FunctionExpression>
110 - | NodePath<t.ArrowFunctionExpression>
111 - | NodePath<t.ObjectMethod>,
74 + currentFn: BabelFunction,
75 referenced: Set<t.Identifier>,
76 path: NodePath<t.Identifier>
77 ): void {
78 const name = path.node.name;
79 const binding = path.scope.getBinding(name);
117 - const bindingAboveLambdaScope = currentLambda.scope.parent.getBinding(name);
80 + const bindingAboveLambdaScope = currentFn.scope.parent.getBinding(name);
81
82 if (binding != null && binding === bindingAboveLambdaScope) {
83 referenced.add(binding.identifier);
@@ -126,7 +89,7 @@ function handleAssignment(
89 lvalPath: NodePath<t.LVal>
90 ): void {
91 /*
129 - * Find all reassignments to identifiers declared outside of currentLambda
92 + * Find all reassignments to identifiers declared outside of currentFn
93 * This closely follows destructuring assignment assumptions and logic in BuildHIR
94 */
95 const lvalNode = lvalPath.node;
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-across-objectmethod-def.expect.md new
+51
@@ -0,0 +1,51 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { identity } from "shared-runtime";
6 +
7 +// repro for context identifier scoping bug, in which x was
8 +// inferred as a context variable.
9 +
10 +function Component() {
11 + let x = 2;
12 + const obj = {
13 + method() {},
14 + };
15 + x = 4;
16 + identity(obj);
17 + // constant propagation should return 4 here
18 + return x;
19 +}
20 +
21 +export const FIXTURE_ENTRYPOINT = {
22 + fn: Component,
23 + params: [{}],
24 +};
25 +
26 +```
27 +
28 +## Code
29 +
30 +```javascript
31 +import { identity } from "shared-runtime";
32 +
33 +// repro for context identifier scoping bug, in which x was
34 +// inferred as a context variable.
35 +
36 +function Component() {
37 + const obj = { method() {} };
38 +
39 + identity(obj);
40 + return 4;
41 +}
42 +
43 +export const FIXTURE_ENTRYPOINT = {
44 + fn: Component,
45 + params: [{}],
46 +};
47 +
48 +```
49 +
50 +### Eval output
51 +(kind: ok) 4
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-across-objectmethod-def.js new
+20
@@ -0,0 +1,20 @@
1 +import { identity } from "shared-runtime";
2 +
3 +// repro for context identifier scoping bug, in which x was
4 +// inferred as a context variable.
5 +
6 +function Component() {
7 + let x = 2;
8 + const obj = {
9 + method() {},
10 + };
11 + x = 4;
12 + identity(obj);
13 + // constant propagation should return 4 here
14 + return x;
15 +}
16 +
17 +export const FIXTURE_ENTRYPOINT = {
18 + fn: Component,
19 + params: [{}],
20 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-objectmethod.expect.md new
+64
@@ -0,0 +1,64 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { invoke } from "shared-runtime";
6 +
7 +function Component({ cond }) {
8 + let x = 2;
9 + const obj = {
10 + method(cond) {
11 + if (cond) {
12 + x = 4;
13 + }
14 + },
15 + };
16 + invoke(obj.method, cond);
17 + return x;
18 +}
19 +
20 +export const FIXTURE_ENTRYPOINT = {
21 + fn: Component,
22 + params: [{ cond: true }],
23 +};
24 +
25 +```
26 +
27 +## Code
28 +
29 +```javascript
30 +import { unstable_useMemoCache as useMemoCache } from "react";
31 +import { invoke } from "shared-runtime";
32 +
33 +function Component(t24) {
34 + const $ = useMemoCache(2);
35 + const { cond } = t24;
36 + let x;
37 + if ($[0] !== cond) {
38 + x = 2;
39 + const obj = {
40 + method(cond_0) {
41 + if (cond_0) {
42 + x = 4;
43 + }
44 + },
45 + };
46 +
47 + invoke(obj.method, cond);
48 + $[0] = cond;
49 + $[1] = x;
50 + } else {
51 + x = $[1];
52 + }
53 + return x;
54 +}
55 +
56 +export const FIXTURE_ENTRYPOINT = {
57 + fn: Component,
58 + params: [{ cond: true }],
59 +};
60 +
61 +```
62 +
63 +### Eval output
64 +(kind: ok) 4
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-objectmethod.js new
+19
@@ -0,0 +1,19 @@
1 +import { invoke } from "shared-runtime";
2 +
3 +function Component({ cond }) {
4 + let x = 2;
5 + const obj = {
6 + method(cond) {
7 + if (cond) {
8 + x = 4;
9 + }
10 + },
11 + };
12 + invoke(obj.method, cond);
13 + return x;
14 +}
15 +
16 +export const FIXTURE_ENTRYPOINT = {
17 + fn: Component,
18 + params: [{ cond: true }],
19 +};