Support RegExp literals
New InstructionValue variant since RegExp literals are valid expressions.
Joe Savona committed
Mar 27, 2023 at 10:34 UTC
61e97dc2788558072ca5b01d2abac9ad7fdbda63
11 files changed
+101
compiler/forget/src/HIR/BuildHIR.ts
+9
@@ -1810,6 +1810,15 @@ function lowerExpression(
1810
loc: exprLoc,
1811
};
1812
}
1813
+ case "RegExpLiteral": {
1814
+ let expr = exprPath as NodePath<t.RegExpLiteral>;
1815
+ return {
1816
+ kind: "RegExpLiteral",
1817
+ pattern: expr.node.pattern,
1818
+ flags: expr.node.flags,
1819
+ loc: expr.node.loc ?? GeneratedSource,
1820
+ };
1821
+ }
1822
default: {
1823
builder.errors.push({
1824
reason: `(BuildHIR::lowerExpression) Handle ${exprPath.type} expressions`,
compiler/forget/src/HIR/HIR.ts
+6
@@ -565,6 +565,12 @@ export type InstructionValue =
565
loc: SourceLocation;
566
}
567
| { kind: "JsxFragment"; children: Array<Place>; loc: SourceLocation }
568
+ | {
569
+ kind: "RegExpLiteral";
570
+ pattern: string;
571
+ flags: string;
572
+ loc: SourceLocation;
573
+ }
574
575
// store `object.property = value`
576
| {
compiler/forget/src/HIR/PrintHIR.ts
+4
@@ -444,6 +444,10 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
444
value = `OptionalCall ${printInstructionValue(instrValue.call)}`;
445
break;
446
}
447
+ case "RegExpLiteral": {
448
+ value = `RegExp /${instrValue.pattern}/${instrValue.flags}`;
449
+ break;
450
+ }
451
default: {
452
assertExhaustive(
453
instrValue,
compiler/forget/src/HIR/visitors.ts
+2
@@ -170,6 +170,7 @@ export function* eachInstructionValueOperand(
170
yield* instrValue.subexprs;
171
break;
172
}
173
+ case "RegExpLiteral":
174
case "LoadGlobal":
175
case "UnsupportedNode":
176
case "Primitive":
@@ -422,6 +423,7 @@ export function mapInstructionOperands(
423
instrValue.subexprs = instrValue.subexprs.map(fn);
424
break;
425
}
426
+ case "RegExpLiteral":
427
case "LoadGlobal":
428
case "UnsupportedNode":
429
case "Primitive":
compiler/forget/src/Inference/InferReferenceEffects.ts
+6
@@ -666,6 +666,12 @@ function inferBlock(
666
effectKind = Effect.Read;
667
break;
668
}
669
+ case "RegExpLiteral": {
670
+ // RegExp instances are mutable objects
671
+ valueKind = ValueKind.Mutable;
672
+ effectKind = Effect.Mutate;
673
+ break;
674
+ }
675
case "LoadGlobal":
676
case "JSXText":
677
case "Primitive": {
compiler/forget/src/Optimization/DeadCodeElimination.ts
+1
@@ -207,6 +207,7 @@ function pruneableValue(value: InstructionValue, state: State): boolean {
207
// Potentially safe to prune, since they should just be creating new values
208
return false;
209
}
210
+ case "RegExpLiteral":
211
case "LoadGlobal":
212
case "ArrayExpression":
213
case "BinaryExpression":
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+4
@@ -903,6 +903,10 @@ function codegenInstructionValue(
903
value = t.identifier(instrValue.name);
904
break;
905
}
906
+ case "RegExpLiteral": {
907
+ value = t.regExpLiteral(instrValue.pattern, instrValue.flags);
908
+ break;
909
+ }
910
case "DeclareLocal":
911
case "Destructure":
912
case "StoreLocal": {
compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts
+1
@@ -235,6 +235,7 @@ function mayAllocate(value: InstructionValue): boolean {
235
case "Primitive": {
236
return false;
237
}
238
+ case "RegExpLiteral":
239
case "MethodCall":
240
case "PropertyStore":
241
case "ComputedStore":
compiler/forget/src/ReactiveScopes/PruneNonEscapingScopes.ts
+1
@@ -509,6 +509,7 @@ function computeMemoizationInputs(
509
};
510
}
511
case "OptionalCall":
512
+ case "RegExpLiteral":
513
case "FunctionExpression":
514
case "TaggedTemplateExpression":
515
case "CallExpression":
compiler/forget/src/__tests__/fixtures/compiler/regexp-literal.expect.md
new
+57
@@ -0,0 +1,57 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ const pattern = /foo/g;
7
+ const value = makeValue();
8
+ // We treat RegExp instances as mutable objects (bc they are)
9
+ // so by default we assume this could be mutating `value`:
10
+ if (pattern.test(value)) {
11
+ return <div>{value}</div>;
12
+ }
13
+ return <div>Default</div>;
14
+}
15
+
16
+```
17
+
18
+## Code
19
+
20
+```javascript
21
+function Component(props) {
22
+ const $ = React.unstable_useMemoCache(4);
23
+ let t0;
24
+ let value;
25
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
26
+ const pattern = /foo/g;
27
+ value = makeValue();
28
+
29
+ t0 = pattern.test(value);
30
+ $[0] = t0;
31
+ $[1] = value;
32
+ } else {
33
+ t0 = $[0];
34
+ value = $[1];
35
+ }
36
+ if (t0) {
37
+ let t1;
38
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
39
+ t1 = <div>{value}</div>;
40
+ $[2] = t1;
41
+ } else {
42
+ t1 = $[2];
43
+ }
44
+ return t1;
45
+ }
46
+ let t2;
47
+ if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
48
+ t2 = <div>Default</div>;
49
+ $[3] = t2;
50
+ } else {
51
+ t2 = $[3];
52
+ }
53
+ return t2;
54
+}
55
+
56
+```
57
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/regexp-literal.js
new
+10
@@ -0,0 +1,10 @@
1
+function Component(props) {
2
+ const pattern = /foo/g;
3
+ const value = makeValue();
4
+ // We treat RegExp instances as mutable objects (bc they are)
5
+ // so by default we assume this could be mutating `value`:
6
+ if (pattern.test(value)) {
7
+ return <div>{value}</div>;
8
+ }
9
+ return <div>Default</div>;
10
+}