Test cases for optional call
Tests, focusing on two key behaviors: * Dependencies of the args are treated as conditional, since the call may not happen * Args cannot be memoized independently, even when that would be valid for a non-optional call.
Joe Savona committed
Mar 24, 2023 at 14:22 UTC
326ee664dc3c8e1694fd89f6fca8cb3f86e3b39a
7 files changed
+167
compiler/forget/src/HIR/BuildHIR.ts
+7
@@ -960,8 +960,13 @@ function lowerExpression(
960
const place = buildTemporaryPlace(builder, loc);
961
const continuationBlock = builder.reserve(builder.currentBlockKind());
962
963
+ // Lower the callee in the current block: the callee is always unconditionally evaluated
964
+ // The test block's branch will test on this value to determine whether to evaluate the call (consequent)
965
+ // or evaluate to undefined (alternate)
966
const callee = lowerExpressionToTemporary(builder, calleePath);
967
968
+ // block to evaluate if the callee is non-null/undefined. arguments are lowered in this block to preserve
969
+ // the semantic of conditional evaluation depending on the callee
970
const consequent = builder.enter("value", () => {
971
const args = lowerArguments(builder, expr.get("arguments"));
972
const temp = buildTemporaryPlace(builder, loc);
@@ -994,6 +999,8 @@ function lowerExpression(
999
id: makeInstructionId(0),
1000
};
1001
});
1002
+
1003
+ // block to evaluate if the callee is null/undefined, this sets the result of the call to undefined.
1004
const alternate = builder.enter("value", () => {
1005
const temp = buildTemporaryPlace(builder, loc);
1006
builder.push({
compiler/forget/src/__tests__/fixtures/compiler/call-with-independently-memoizable-arg.expect.md
new
+55
@@ -0,0 +1,55 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ const x = makeFunction(props);
7
+ const y = x(
8
+ <div>
9
+ <span>{props.text}</span>
10
+ </div>
11
+ );
12
+ return y;
13
+}
14
+
15
+```
16
+
17
+## Code
18
+
19
+```javascript
20
+function Component(props) {
21
+ const $ = React.unstable_useMemoCache(6);
22
+ const c_0 = $[0] !== props;
23
+ let t2;
24
+ if (c_0) {
25
+ const x = makeFunction(props);
26
+ const c_2 = $[2] !== props.text;
27
+ let t0;
28
+ if (c_2) {
29
+ t0 = <span>{props.text}</span>;
30
+ $[2] = props.text;
31
+ $[3] = t0;
32
+ } else {
33
+ t0 = $[3];
34
+ }
35
+ const c_4 = $[4] !== t0;
36
+ let t1;
37
+ if (c_4) {
38
+ t1 = <div>{t0}</div>;
39
+ $[4] = t0;
40
+ $[5] = t1;
41
+ } else {
42
+ t1 = $[5];
43
+ }
44
+ t2 = x(t1);
45
+ $[0] = props;
46
+ $[1] = t2;
47
+ } else {
48
+ t2 = $[1];
49
+ }
50
+ const y = t2;
51
+ return y;
52
+}
53
+
54
+```
55
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/call-with-independently-memoizable-arg.js
new
+9
@@ -0,0 +1,9 @@
1
+function Component(props) {
2
+ const x = makeFunction(props);
3
+ const y = x(
4
+ <div>
5
+ <span>{props.text}</span>
6
+ </div>
7
+ );
8
+ return y;
9
+}
compiler/forget/src/__tests__/fixtures/compiler/optional-call-with-independently-memoizable-arg.expect.md
new
+42
@@ -0,0 +1,42 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ const x = makeOptionalFunction(props);
7
+ // for a regular call, the JSX element could be independently memoized
8
+ // since it is an immutable value. however, because the call is optional,
9
+ // we can't extract out independent memoization for the element w/o
10
+ // forcing that argument to evaluate unconditionally
11
+ const y = x?.(
12
+ <div>
13
+ <span>{props.text}</span>
14
+ </div>
15
+ );
16
+ return y;
17
+}
18
+
19
+```
20
+
21
+## Code
22
+
23
+```javascript
24
+function Component(props) {
25
+ const $ = React.unstable_useMemoCache(2);
26
+ const c_0 = $[0] !== props;
27
+ let t0;
28
+ if (c_0) {
29
+ const x = makeOptionalFunction(props);
30
+
31
+ t0 = x?.(<div>{<span>{props.text}</span>}</div>);
32
+ $[0] = props;
33
+ $[1] = t0;
34
+ } else {
35
+ t0 = $[1];
36
+ }
37
+ const y = t0;
38
+ return y;
39
+}
40
+
41
+```
42
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/optional-call-with-independently-memoizable-arg.js
new
+13
@@ -0,0 +1,13 @@
1
+function Component(props) {
2
+ const x = makeOptionalFunction(props);
3
+ // for a regular call, the JSX element could be independently memoized
4
+ // since it is an immutable value. however, because the call is optional,
5
+ // we can't extract out independent memoization for the element w/o
6
+ // forcing that argument to evaluate unconditionally
7
+ const y = x?.(
8
+ <div>
9
+ <span>{props.text}</span>
10
+ </div>
11
+ );
12
+ return y;
13
+}
compiler/forget/src/__tests__/fixtures/compiler/optional-call.expect.md
new
+35
@@ -0,0 +1,35 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ const x = makeOptionalFunction(props);
7
+ const y = makeObject(props);
8
+ const z = x?.(y.a, props.a, foo(y.b), bar(props.b));
9
+ return z;
10
+}
11
+
12
+```
13
+
14
+## Code
15
+
16
+```javascript
17
+function Component(props) {
18
+ const $ = React.unstable_useMemoCache(2);
19
+ const c_0 = $[0] !== props;
20
+ let t0;
21
+ if (c_0) {
22
+ const x = makeOptionalFunction(props);
23
+ const y = makeObject(props);
24
+ t0 = x?.(y.a, props.a, foo(y.b), bar(props.b));
25
+ $[0] = props;
26
+ $[1] = t0;
27
+ } else {
28
+ t0 = $[1];
29
+ }
30
+ const z = t0;
31
+ return z;
32
+}
33
+
34
+```
35
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/optional-call.js
new
+6
@@ -0,0 +1,6 @@
1
+function Component(props) {
2
+ const x = makeOptionalFunction(props);
3
+ const y = makeObject(props);
4
+ const z = x?.(y.a, props.a, foo(y.b), bar(props.b));
5
+ return z;
6
+}