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
}