@samitouri / QOS-React-2 / commits / 61e97dc278

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 +}