@samitouri / QOS-React-2 / commits / 404f627c2c

Improve conditional dependency tracking for optional member expr inside optional call

Improve conditional dependency tracking for optional member expr inside optional call When we traverse an OptionalExpression in PropagateScopeDependencies, we previously considered the entire value to be optional. With the changes in this stack to more accurately model OptionalMemberExpression, the `object` portion of an OptionalMemberExpression is now evaluated within the OptionalExpression. This PR refines the handling of OptionalExpression accordingly, so that we only treat the optional portion as conditional.

Joe Savona committed May 2, 2023 at 16:49 UTC 404f627c2c26a2ffcb17459fe9eef5468c7a2701
5 files changed +95 -3
compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts
+21 -3
@@ -5,6 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 +import { CompilerError } from "../CompilerError";
9 import {
10 Identifier,
11 IdentifierId,
@@ -451,9 +452,26 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
452 ): void {
453 switch (value.kind) {
454 case "OptionalExpression": {
454 - context.enterConditional(() => {
455 - this.visitReactiveValue(context, id, value.value);
456 - });
455 + const inner = value.value;
456 + // OptionalExpression value is a SequenceExpression where the instructions
457 + // represent the code prior to the `?` and the final value represents the
458 + // conditional code that follows.
459 + if (inner.kind === "SequenceExpression") {
460 + // Instructions are the unconditionally executed portion before the `?`
461 + for (const instr of inner.instructions) {
462 + this.visitInstruction(instr, context);
463 + }
464 + // The final value is the conditional portion following the `?`
465 + context.enterConditional(() => {
466 + this.visitReactiveValue(context, id, inner.value);
467 + });
468 + } else {
469 + CompilerError.invariant(
470 + "Expected OptionalExpression value to be a SequenceExpression",
471 + value.loc,
472 + `Found a '${value.kind}'`
473 + );
474 + }
475 break;
476 }
477 case "LogicalExpression": {
compiler/forget/src/__tests__/fixtures/compiler/reactive-dependencies-non-optional-properties-inside-optional-chain.expect.md new
+30
@@ -0,0 +1,30 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + return props.post.feedback.comments?.edges?.map(render);
7 +}
8 +
9 +```
10 +
11 +## Code
12 +
13 +```javascript
14 +import { unstable_useMemoCache as useMemoCache } from "react";
15 +function Component(props) {
16 + const $ = useMemoCache(2);
17 + const c_0 = $[0] !== props.post.feedback.comments;
18 + let t0;
19 + if (c_0) {
20 + t0 = props.post.feedback.comments?.edges?.map(render);
21 + $[0] = props.post.feedback.comments;
22 + $[1] = t0;
23 + } else {
24 + t0 = $[1];
25 + }
26 + return t0;
27 +}
28 +
29 +```
30 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/reactive-dependencies-non-optional-properties-inside-optional-chain.js new
+3
@@ -0,0 +1,3 @@
1 +function Component(props) {
2 + return props.post.feedback.comments?.edges?.map(render);
3 +}
compiler/forget/src/__tests__/fixtures/compiler/reduce-reactive-dependencies-optional-member-expression.expect.md new
+35
@@ -0,0 +1,35 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const x = [];
7 + x.push(props.items?.length);
8 + x.push(props.items?.edges?.map?.(render)?.filter?.(Boolean) ?? []);
9 + return x;
10 +}
11 +
12 +```
13 +
14 +## Code
15 +
16 +```javascript
17 +import { unstable_useMemoCache as useMemoCache } from "react";
18 +function Component(props) {
19 + const $ = useMemoCache(2);
20 + const c_0 = $[0] !== props.items;
21 + let x;
22 + if (c_0) {
23 + x = [];
24 + x.push(props.items?.length);
25 + x.push(props.items?.edges?.map?.(render)?.filter?.(Boolean) ?? []);
26 + $[0] = props.items;
27 + $[1] = x;
28 + } else {
29 + x = $[1];
30 + }
31 + return x;
32 +}
33 +
34 +```
35 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/reduce-reactive-dependencies-optional-member-expression.js new
+6
@@ -0,0 +1,6 @@
1 +function Component(props) {
2 + const x = [];
3 + x.push(props.items?.length);
4 + x.push(props.items?.edges?.map?.(render)?.filter?.(Boolean) ?? []);
5 + return x;
6 +}