@samitouri / QOS-React-2 / commits / f985d6cdba

PropertyDelete/ComputedDelete instructions

Joe Savona committed Mar 8, 2023 at 12:04 UTC f985d6cdba078fc49b787c900f14dae8d4e379ee
13 files changed +233 -30
compiler/forget/src/HIR/BuildHIR.ts
+35 -6
@@ -1560,12 +1560,41 @@ function lowerExpression(
1560 }
1561 case "UnaryExpression": {
1562 let expr = exprPath as NodePath<t.UnaryExpression>;
1563 - return {
1564 - kind: "UnaryExpression",
1565 - operator: expr.node.operator,
1566 - value: lowerExpressionToTemporary(builder, expr.get("argument")),
1567 - loc: exprLoc,
1568 - };
1563 + if (expr.node.operator === "delete") {
1564 + const argument = expr.get("argument");
1565 + if (argument.isMemberExpression()) {
1566 + const { object, property } = lowerMemberExpression(builder, argument);
1567 + if (typeof property === "string") {
1568 + return {
1569 + kind: "PropertyDelete",
1570 + object,
1571 + property,
1572 + loc: exprLoc,
1573 + };
1574 + } else {
1575 + return {
1576 + kind: "ComputedDelete",
1577 + object,
1578 + property,
1579 + loc: exprLoc,
1580 + };
1581 + }
1582 + } else {
1583 + builder.errors.push({
1584 + reason: `(BuildHIR::lowerExpression) delete on a non-member expression has no semantic meaning`,
1585 + severity: ErrorSeverity.InvalidInput,
1586 + nodePath: expr,
1587 + });
1588 + return { kind: "UnsupportedNode", node: expr.node, loc: exprLoc };
1589 + }
1590 + } else {
1591 + return {
1592 + kind: "UnaryExpression",
1593 + operator: expr.node.operator,
1594 + value: lowerExpressionToTemporary(builder, expr.get("argument")),
1595 + loc: exprLoc,
1596 + };
1597 + }
1598 }
1599 case "TypeCastExpression": {
1600 let expr = exprPath as NodePath<t.TypeCastExpression>;
compiler/forget/src/HIR/HIR.ts
+14
@@ -544,6 +544,13 @@ export type InstructionValue =
544 optional: boolean;
545 loc: SourceLocation;
546 }
547 + // `delete object.property`
548 + | {
549 + kind: "PropertyDelete";
550 + object: Place;
551 + property: string;
552 + loc: SourceLocation;
553 + }
554
555 // store `object[index] = value` - like PropertyStore but with a dynamic property
556 | {
@@ -560,6 +567,13 @@ export type InstructionValue =
567 property: Place;
568 loc: SourceLocation;
569 }
570 + // `delete object[property]`
571 + | {
572 + kind: "ComputedDelete";
573 + object: Place;
574 + property: Place;
575 + loc: SourceLocation;
576 + }
577 | { kind: "LoadGlobal"; name: string; loc: SourceLocation }
578 | FunctionExpression
579 | {
compiler/forget/src/HIR/PrintHIR.ts
+12
@@ -351,6 +351,12 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
351 } = ${printPlace(instrValue.value)}`;
352 break;
353 }
354 + case "PropertyDelete": {
355 + value = `PropertyDelete ${printPlace(instrValue.object)}.${
356 + instrValue.property
357 + }`;
358 + break;
359 + }
360 case "ComputedLoad": {
361 value = `ComputedLoad ${printPlace(instrValue.object)}[${printPlace(
362 instrValue.property
@@ -363,6 +369,12 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
369 )}] = ${printPlace(instrValue.value)}`;
370 break;
371 }
372 + case "ComputedDelete": {
373 + value = `ComputedDelete ${printPlace(instrValue.object)}[${printPlace(
374 + instrValue.property
375 + )}]`;
376 + break;
377 + }
378 case "FunctionExpression": {
379 const fn = printFunction(instrValue.loweredFunc)
380 .split("\n")
compiler/forget/src/HIR/visitors.ts
+18
@@ -80,6 +80,10 @@ export function* eachInstructionValueOperand(
80 yield instrValue.object;
81 break;
82 }
83 + case "PropertyDelete": {
84 + yield instrValue.object;
85 + break;
86 + }
87 case "PropertyStore": {
88 yield instrValue.object;
89 yield instrValue.value;
@@ -90,6 +94,11 @@ export function* eachInstructionValueOperand(
94 yield instrValue.property;
95 break;
96 }
97 + case "ComputedDelete": {
98 + yield instrValue.object;
99 + yield instrValue.property;
100 + break;
101 + }
102 case "ComputedStore": {
103 yield instrValue.object;
104 yield instrValue.property;
@@ -254,6 +263,10 @@ export function mapInstructionOperands(
263 instrValue.object = fn(instrValue.object);
264 break;
265 }
266 + case "PropertyDelete": {
267 + instrValue.object = fn(instrValue.object);
268 + break;
269 + }
270 case "PropertyStore": {
271 instrValue.object = fn(instrValue.object);
272 instrValue.value = fn(instrValue.value);
@@ -264,6 +277,11 @@ export function mapInstructionOperands(
277 instrValue.property = fn(instrValue.property);
278 break;
279 }
280 + case "ComputedDelete": {
281 + instrValue.object = fn(instrValue.object);
282 + instrValue.property = fn(instrValue.property);
283 + break;
284 + }
285 case "ComputedStore": {
286 instrValue.object = fn(instrValue.object);
287 instrValue.property = fn(instrValue.property);
compiler/forget/src/Inference/InferReferenceEffects.ts
+13
@@ -716,6 +716,12 @@ function inferBlock(
716 lvalue.effect = Effect.Store;
717 continue;
718 }
719 + case "PropertyDelete": {
720 + // `delete` returns a boolean (immutable) and modifies the object
721 + valueKind = ValueKind.Immutable;
722 + effectKind = Effect.Mutate;
723 + break;
724 + }
725 case "PropertyLoad": {
726 if (!state.isDefined(instrValue.object)) {
727 // TODO @josephsavona: improve handling of globals
@@ -749,6 +755,13 @@ function inferBlock(
755 lvalue.effect = Effect.Store;
756 continue;
757 }
758 + case "ComputedDelete": {
759 + state.reference(instrValue.object, Effect.Mutate);
760 + state.reference(instrValue.property, Effect.Read);
761 + state.initialize(instrValue, ValueKind.Immutable);
762 + state.reference(instr.lvalue, Effect.Mutate);
763 + continue;
764 + }
765 case "ComputedLoad": {
766 if (!state.isDefined(instrValue.object)) {
767 // TODO @josephsavona: improve handling of globals
compiler/forget/src/Optimization/DeadCodeElimination.ts
+2
@@ -175,8 +175,10 @@ function pruneableValue(
175 return true;
176 }
177 case "CallExpression":
178 + case "ComputedDelete":
179 case "ComputedCall":
180 case "ComputedStore":
181 + case "PropertyDelete":
182 case "PropertyCall":
183 case "PropertyStore": {
184 // Mutating instructions are not safe to prune.
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+21
@@ -711,6 +711,16 @@ function codegenInstructionValue(
711 }
712 break;
713 }
714 + case "PropertyDelete": {
715 + value = t.unaryExpression(
716 + "delete",
717 + t.memberExpression(
718 + codegenPlace(cx, instrValue.object),
719 + t.identifier(instrValue.property)
720 + )
721 + );
722 + break;
723 + }
724 case "ComputedStore": {
725 value = t.assignmentExpression(
726 "=",
@@ -731,6 +741,17 @@ function codegenInstructionValue(
741 );
742 break;
743 }
744 + case "ComputedDelete": {
745 + value = t.unaryExpression(
746 + "delete",
747 + t.memberExpression(
748 + codegenPlace(cx, instrValue.object),
749 + codegenPlace(cx, instrValue.property),
750 + true
751 + )
752 + );
753 + break;
754 + }
755 case "LoadLocal": {
756 value = codegenPlace(cx, instrValue.place);
757 break;
compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts
+2
@@ -205,7 +205,9 @@ function mayAllocate(value: InstructionValue): boolean {
205 case "BinaryExpression":
206 case "LoadLocal":
207 case "PropertyLoad":
208 + case "PropertyDelete":
209 case "ComputedLoad":
210 + case "ComputedDelete":
211 case "JSXText":
212 case "UnaryExpression":
213 case "TemplateLiteral":
compiler/forget/src/__tests__/fixtures/hir/delete-computed-property.expect.md new
+35
@@ -0,0 +1,35 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const x = { a: props.a, b: props.b };
7 + const key = "b";
8 + delete x[key];
9 + return x;
10 +}
11 +
12 +```
13 +
14 +## Code
15 +
16 +```javascript
17 +function Component(props) {
18 + const $ = React.unstable_useMemoCache(3);
19 + const c_0 = $[0] !== props.a;
20 + const c_1 = $[1] !== props.b;
21 + let x;
22 + if (c_0 || c_1) {
23 + x = { a: props.a, b: props.b };
24 + delete x["b"];
25 + $[0] = props.a;
26 + $[1] = props.b;
27 + $[2] = x;
28 + } else {
29 + x = $[2];
30 + }
31 + return x;
32 +}
33 +
34 +```
35 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/delete-computed-property.js new
+6
@@ -0,0 +1,6 @@
1 +function Component(props) {
2 + const x = { a: props.a, b: props.b };
3 + const key = "b";
4 + delete x[key];
5 + return x;
6 +}
compiler/forget/src/__tests__/fixtures/hir/delete-property.expect.md new
+34
@@ -0,0 +1,34 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const x = { a: props.a, b: props.b };
7 + delete x.b;
8 + return x;
9 +}
10 +
11 +```
12 +
13 +## Code
14 +
15 +```javascript
16 +function Component(props) {
17 + const $ = React.unstable_useMemoCache(3);
18 + const c_0 = $[0] !== props.a;
19 + const c_1 = $[1] !== props.b;
20 + let x;
21 + if (c_0 || c_1) {
22 + x = { a: props.a, b: props.b };
23 + delete x.b;
24 + $[0] = props.a;
25 + $[1] = props.b;
26 + $[2] = x;
27 + } else {
28 + x = $[2];
29 + }
30 + return x;
31 +}
32 +
33 +```
34 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/delete-property.js new
+5
@@ -0,0 +1,5 @@
1 +function Component(props) {
2 + const x = { a: props.a, b: props.b };
3 + delete x.b;
4 + return x;
5 +}
compiler/forget/src/__tests__/fixtures/hir/unary-expr.expect.md
+36 -24
@@ -20,44 +20,56 @@ function component(a) {
20
21 ```javascript
22 function component(a) {
23 - const $ = React.unstable_useMemoCache(10);
23 + const $ = React.unstable_useMemoCache(14);
24 const c_0 = $[0] !== a;
25 let t0;
26 + let t;
27 + let z;
28 + let p;
29 + let q;
30 if (c_0) {
27 - t0 = { t: a };
31 + t = { t: a };
32 + z = +t.t;
33 + q = -t.t;
34 + p = void t.t;
35 + t0 = delete t.t;
36 $[0] = a;
37 $[1] = t0;
38 + $[2] = t;
39 + $[3] = z;
40 + $[4] = p;
41 + $[5] = q;
42 } else {
43 t0 = $[1];
44 + t = $[2];
45 + z = $[3];
46 + p = $[4];
47 + q = $[5];
48 }
33 - const t = t0;
34 - const z = +t.t;
35 - const q = -t.t;
36 - const p = void t.t;
37 - const n = delete t.t;
49 + const n = t0;
50 const m = !t.t;
51 const e = ~t.t;
52 const f = typeof t.t;
41 - const c_2 = $[2] !== z;
42 - const c_3 = $[3] !== p;
43 - const c_4 = $[4] !== q;
44 - const c_5 = $[5] !== n;
45 - const c_6 = $[6] !== m;
46 - const c_7 = $[7] !== e;
47 - const c_8 = $[8] !== f;
53 + const c_6 = $[6] !== z;
54 + const c_7 = $[7] !== p;
55 + const c_8 = $[8] !== q;
56 + const c_9 = $[9] !== n;
57 + const c_10 = $[10] !== m;
58 + const c_11 = $[11] !== e;
59 + const c_12 = $[12] !== f;
60 let t1;
49 - if (c_2 || c_3 || c_4 || c_5 || c_6 || c_7 || c_8) {
61 + if (c_6 || c_7 || c_8 || c_9 || c_10 || c_11 || c_12) {
62 t1 = { z, p, q, n, m, e, f };
51 - $[2] = z;
52 - $[3] = p;
53 - $[4] = q;
54 - $[5] = n;
55 - $[6] = m;
56 - $[7] = e;
57 - $[8] = f;
58 - $[9] = t1;
63 + $[6] = z;
64 + $[7] = p;
65 + $[8] = q;
66 + $[9] = n;
67 + $[10] = m;
68 + $[11] = e;
69 + $[12] = f;
70 + $[13] = t1;
71 } else {
60 - t1 = $[9];
72 + t1 = $[13];
73 }
74 return t1;
75 }