@samitouri / QOS-React-1 / commits / 191064b55c

Add ReactiveFunctionValue variant

Per the previous PR, we don't have a way to rewrite an arbitrary subset of a ReactiveFunction into a function expression, since FunctionExpression's contents is still in HIR. While long-term our plan is to move to HIR everywhere, this PR adds a stopgap of adding a ReactiveFunctionValue variant of ReactiveValue. As a reminder, ReactiveValue is a union of (HIR) InstructionValue | SequenceExpression | LogicalExpression | ConditionalExpression. For now i did a first stab at the visitors and transforms with the idea that: * By default, visitors/transforms _don't_ look into these function expressions, since we didn't previously traverse into (HIR-based) FunctionExpression either * But there is a visitor/transform method that you can override if you need to.

Joe Savona committed Feb 16, 2024 at 14:59 UTC 191064b55c4001306f16d8d7cb42de89b0c0262d
6 files changed +74 -3
compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts
+9 -1
@@ -95,7 +95,15 @@ export type ReactiveValue =
95 | ReactiveLogicalValue
96 | ReactiveSequenceValue
97 | ReactiveTernaryValue
98 - | ReactiveOptionalCallValue;
98 + | ReactiveOptionalCallValue
99 + | ReactiveFunctionValue;
100 +
101 +export type ReactiveFunctionValue = {
102 + kind: "ReactiveFunctionValue";
103 + fn: ReactiveFunction;
104 + dependencies: Array<Place>;
105 + loc: SourceLocation;
106 +};
107
108 export type ReactiveLogicalValue = {
109 kind: "LogicalExpression";
compiler/packages/babel-plugin-react-forget/src/HIR/PrintHIR.ts
+5
@@ -6,6 +6,7 @@
6 */
7
8 import generate from "@babel/generator";
9 +import { printReactiveFunction } from "..";
10 import { CompilerError } from "../CompilerError";
11 import DisjointSet from "../Utils/DisjointSet";
12 import { assertExhaustive } from "../Utils/utils";
@@ -600,6 +601,10 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
601 value = `Memoize ${printPlace(instrValue.value)}`;
602 break;
603 }
604 + case "ReactiveFunctionValue": {
605 + value = `FunctionValue ${printReactiveFunction(instrValue.fn)}`;
606 + break;
607 + }
608 default: {
609 assertExhaustive(
610 instrValue,
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+1
@@ -1678,6 +1678,7 @@ function codegenInstructionValue(
1678 );
1679 break;
1680 }
1681 + case "ReactiveFunctionValue":
1682 case "Memoize":
1683 case "Debugger":
1684 case "DeclareLocal":
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PropagateScopeDependencies.ts
+8
@@ -573,6 +573,14 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
573 }
574 break;
575 }
576 + case "ReactiveFunctionValue": {
577 + CompilerError.invariant(false, {
578 + reason: `Unexpected ReactiveFunctionValue`,
579 + loc: value.loc,
580 + description: null,
581 + suggestions: null,
582 + });
583 + }
584 default: {
585 for (const operand of eachInstructionValueOperand(value)) {
586 context.visitOperand(operand);
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PruneNonEscapingScopes.ts
+8
@@ -702,6 +702,14 @@ function computeMemoizationInputs(
702 rvalues: operands,
703 };
704 }
705 + case "ReactiveFunctionValue": {
706 + CompilerError.invariant(false, {
707 + reason: `Unexpected ReactiveFunctionValue node`,
708 + description: null,
709 + loc: value.loc,
710 + suggestions: null,
711 + });
712 + }
713 case "UnsupportedNode": {
714 CompilerError.invariant(false, {
715 reason: `Unexpected unsupported node`,
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/visitors.ts
+43 -2
@@ -35,6 +35,12 @@ export class ReactiveFunctionVisitor<TState = void> {
35 visitID(_id: InstructionId, _state: TState): void {}
36 visitLValue(_id: InstructionId, _lvalue: Place, _state: TState): void {}
37 visitPlace(_id: InstructionId, _place: Place, _state: TState): void {}
38 + visitReactiveFunctionValue(
39 + _id: InstructionId,
40 + _dependencies: Array<Place>,
41 + _fn: ReactiveFunction,
42 + _state: TState
43 + ): void {}
44
45 visitValue(id: InstructionId, value: ReactiveValue, state: TState): void {
46 this.traverseValue(id, value, state);
@@ -63,8 +69,17 @@ export class ReactiveFunctionVisitor<TState = void> {
69 this.visitValue(value.id, value.value, state);
70 break;
71 }
72 + case "ReactiveFunctionValue": {
73 + this.visitReactiveFunctionValue(
74 + id,
75 + value.dependencies,
76 + value.fn,
77 + state
78 + );
79 + break;
80 + }
81 default: {
67 - for (const place of eachReactiveValueOperand(value)) {
82 + for (const place of eachInstructionValueOperand(value)) {
83 this.visitPlace(id, place, state);
84 }
85 }
@@ -308,6 +323,16 @@ export class ReactiveFunctionTransform<
323 return { kind: "keep" };
324 }
325
326 + transformReactiveFunctionValue(
327 + id: InstructionId,
328 + dependencies: Array<Place>,
329 + fn: ReactiveFunction,
330 + state: TState
331 + ): { kind: "keep" } | { kind: "replace"; value: ReactiveFunction } {
332 + this.visitReactiveFunctionValue(id, dependencies, fn, state);
333 + return { kind: "keep" };
334 + }
335 +
336 override traverseValue(
337 id: InstructionId,
338 value: ReactiveValue,
@@ -357,8 +382,20 @@ export class ReactiveFunctionTransform<
382 }
383 break;
384 }
385 + case "ReactiveFunctionValue": {
386 + const nextValue = this.transformReactiveFunctionValue(
387 + id,
388 + value.dependencies,
389 + value.fn,
390 + state
391 + );
392 + if (nextValue.kind === "replace") {
393 + value.fn = nextValue.value;
394 + }
395 + break;
396 + }
397 default: {
361 - for (const place of eachReactiveValueOperand(value)) {
398 + for (const place of eachInstructionValueOperand(value)) {
399 this.visitPlace(id, place, state);
400 }
401 }
@@ -523,6 +560,10 @@ export function* eachReactiveValueOperand(
560 yield* eachReactiveValueOperand(instrValue.alternate);
561 break;
562 }
563 + case "ReactiveFunctionValue": {
564 + yield* instrValue.dependencies;
565 + break;
566 + }
567 default: {
568 yield* eachInstructionValueOperand(instrValue);
569 }