diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index 331706190f..6c5b77a0b5 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -960,8 +960,13 @@ function lowerExpression( const place = buildTemporaryPlace(builder, loc); const continuationBlock = builder.reserve(builder.currentBlockKind()); + // Lower the callee in the current block: the callee is always unconditionally evaluated + // The test block's branch will test on this value to determine whether to evaluate the call (consequent) + // or evaluate to undefined (alternate) const callee = lowerExpressionToTemporary(builder, calleePath); + // block to evaluate if the callee is non-null/undefined. arguments are lowered in this block to preserve + // the semantic of conditional evaluation depending on the callee const consequent = builder.enter("value", () => { const args = lowerArguments(builder, expr.get("arguments")); const temp = buildTemporaryPlace(builder, loc); @@ -994,6 +999,8 @@ function lowerExpression( id: makeInstructionId(0), }; }); + + // block to evaluate if the callee is null/undefined, this sets the result of the call to undefined. const alternate = builder.enter("value", () => { const temp = buildTemporaryPlace(builder, loc); builder.push({ diff --git a/compiler/forget/src/__tests__/fixtures/compiler/call-with-independently-memoizable-arg.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/call-with-independently-memoizable-arg.expect.md new file mode 100644 index 0000000000..57e76125a0 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/call-with-independently-memoizable-arg.expect.md @@ -0,0 +1,55 @@ + +## Input + +```javascript +function Component(props) { + const x = makeFunction(props); + const y = x( +
+ {props.text} +
+ ); + return y; +} + +``` + +## Code + +```javascript +function Component(props) { + const $ = React.unstable_useMemoCache(6); + const c_0 = $[0] !== props; + let t2; + if (c_0) { + const x = makeFunction(props); + const c_2 = $[2] !== props.text; + let t0; + if (c_2) { + t0 = {props.text}; + $[2] = props.text; + $[3] = t0; + } else { + t0 = $[3]; + } + const c_4 = $[4] !== t0; + let t1; + if (c_4) { + t1 =
{t0}
; + $[4] = t0; + $[5] = t1; + } else { + t1 = $[5]; + } + t2 = x(t1); + $[0] = props; + $[1] = t2; + } else { + t2 = $[1]; + } + const y = t2; + return y; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/call-with-independently-memoizable-arg.js b/compiler/forget/src/__tests__/fixtures/compiler/call-with-independently-memoizable-arg.js new file mode 100644 index 0000000000..0675b44287 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/call-with-independently-memoizable-arg.js @@ -0,0 +1,9 @@ +function Component(props) { + const x = makeFunction(props); + const y = x( +
+ {props.text} +
+ ); + return y; +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/optional-call-with-independently-memoizable-arg.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/optional-call-with-independently-memoizable-arg.expect.md new file mode 100644 index 0000000000..6bab00a43d --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/optional-call-with-independently-memoizable-arg.expect.md @@ -0,0 +1,42 @@ + +## Input + +```javascript +function Component(props) { + const x = makeOptionalFunction(props); + // for a regular call, the JSX element could be independently memoized + // since it is an immutable value. however, because the call is optional, + // we can't extract out independent memoization for the element w/o + // forcing that argument to evaluate unconditionally + const y = x?.( +
+ {props.text} +
+ ); + return y; +} + +``` + +## Code + +```javascript +function Component(props) { + const $ = React.unstable_useMemoCache(2); + const c_0 = $[0] !== props; + let t0; + if (c_0) { + const x = makeOptionalFunction(props); + + t0 = x?.(
{{props.text}}
); + $[0] = props; + $[1] = t0; + } else { + t0 = $[1]; + } + const y = t0; + return y; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/optional-call-with-independently-memoizable-arg.js b/compiler/forget/src/__tests__/fixtures/compiler/optional-call-with-independently-memoizable-arg.js new file mode 100644 index 0000000000..9d9abf887f --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/optional-call-with-independently-memoizable-arg.js @@ -0,0 +1,13 @@ +function Component(props) { + const x = makeOptionalFunction(props); + // for a regular call, the JSX element could be independently memoized + // since it is an immutable value. however, because the call is optional, + // we can't extract out independent memoization for the element w/o + // forcing that argument to evaluate unconditionally + const y = x?.( +
+ {props.text} +
+ ); + return y; +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/optional-call.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/optional-call.expect.md new file mode 100644 index 0000000000..3c30aeb83d --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/optional-call.expect.md @@ -0,0 +1,35 @@ + +## Input + +```javascript +function Component(props) { + const x = makeOptionalFunction(props); + const y = makeObject(props); + const z = x?.(y.a, props.a, foo(y.b), bar(props.b)); + return z; +} + +``` + +## Code + +```javascript +function Component(props) { + const $ = React.unstable_useMemoCache(2); + const c_0 = $[0] !== props; + let t0; + if (c_0) { + const x = makeOptionalFunction(props); + const y = makeObject(props); + t0 = x?.(y.a, props.a, foo(y.b), bar(props.b)); + $[0] = props; + $[1] = t0; + } else { + t0 = $[1]; + } + const z = t0; + return z; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/optional-call.js b/compiler/forget/src/__tests__/fixtures/compiler/optional-call.js new file mode 100644 index 0000000000..f4610a49f3 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/optional-call.js @@ -0,0 +1,6 @@ +function Component(props) { + const x = makeOptionalFunction(props); + const y = makeObject(props); + const z = x?.(y.a, props.a, foo(y.b), bar(props.b)); + return z; +}