Support RegExp literals

New InstructionValue variant since RegExp literals are valid expressions.
This commit is contained in:
Joe Savona
2023-03-27 10:34:10 -07:00
parent 179b24b56e
commit 61e97dc278
11 changed files with 101 additions and 0 deletions
+9
View File
@@ -1810,6 +1810,15 @@ function lowerExpression(
loc: exprLoc,
};
}
case "RegExpLiteral": {
let expr = exprPath as NodePath<t.RegExpLiteral>;
return {
kind: "RegExpLiteral",
pattern: expr.node.pattern,
flags: expr.node.flags,
loc: expr.node.loc ?? GeneratedSource,
};
}
default: {
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${exprPath.type} expressions`,
+6
View File
@@ -565,6 +565,12 @@ export type InstructionValue =
loc: SourceLocation;
}
| { kind: "JsxFragment"; children: Array<Place>; loc: SourceLocation }
| {
kind: "RegExpLiteral";
pattern: string;
flags: string;
loc: SourceLocation;
}
// store `object.property = value`
| {
+4
View File
@@ -444,6 +444,10 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
value = `OptionalCall ${printInstructionValue(instrValue.call)}`;
break;
}
case "RegExpLiteral": {
value = `RegExp /${instrValue.pattern}/${instrValue.flags}`;
break;
}
default: {
assertExhaustive(
instrValue,
+2
View File
@@ -170,6 +170,7 @@ export function* eachInstructionValueOperand(
yield* instrValue.subexprs;
break;
}
case "RegExpLiteral":
case "LoadGlobal":
case "UnsupportedNode":
case "Primitive":
@@ -422,6 +423,7 @@ export function mapInstructionOperands(
instrValue.subexprs = instrValue.subexprs.map(fn);
break;
}
case "RegExpLiteral":
case "LoadGlobal":
case "UnsupportedNode":
case "Primitive":
@@ -666,6 +666,12 @@ function inferBlock(
effectKind = Effect.Read;
break;
}
case "RegExpLiteral": {
// RegExp instances are mutable objects
valueKind = ValueKind.Mutable;
effectKind = Effect.Mutate;
break;
}
case "LoadGlobal":
case "JSXText":
case "Primitive": {
@@ -207,6 +207,7 @@ function pruneableValue(value: InstructionValue, state: State): boolean {
// Potentially safe to prune, since they should just be creating new values
return false;
}
case "RegExpLiteral":
case "LoadGlobal":
case "ArrayExpression":
case "BinaryExpression":
@@ -903,6 +903,10 @@ function codegenInstructionValue(
value = t.identifier(instrValue.name);
break;
}
case "RegExpLiteral": {
value = t.regExpLiteral(instrValue.pattern, instrValue.flags);
break;
}
case "DeclareLocal":
case "Destructure":
case "StoreLocal": {
@@ -235,6 +235,7 @@ function mayAllocate(value: InstructionValue): boolean {
case "Primitive": {
return false;
}
case "RegExpLiteral":
case "MethodCall":
case "PropertyStore":
case "ComputedStore":
@@ -509,6 +509,7 @@ function computeMemoizationInputs(
};
}
case "OptionalCall":
case "RegExpLiteral":
case "FunctionExpression":
case "TaggedTemplateExpression":
case "CallExpression":
@@ -0,0 +1,57 @@
## Input
```javascript
function Component(props) {
const pattern = /foo/g;
const value = makeValue();
// We treat RegExp instances as mutable objects (bc they are)
// so by default we assume this could be mutating `value`:
if (pattern.test(value)) {
return <div>{value}</div>;
}
return <div>Default</div>;
}
```
## Code
```javascript
function Component(props) {
const $ = React.unstable_useMemoCache(4);
let t0;
let value;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const pattern = /foo/g;
value = makeValue();
t0 = pattern.test(value);
$[0] = t0;
$[1] = value;
} else {
t0 = $[0];
value = $[1];
}
if (t0) {
let t1;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
t1 = <div>{value}</div>;
$[2] = t1;
} else {
t1 = $[2];
}
return t1;
}
let t2;
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
t2 = <div>Default</div>;
$[3] = t2;
} else {
t2 = $[3];
}
return t2;
}
```
@@ -0,0 +1,10 @@
function Component(props) {
const pattern = /foo/g;
const value = makeValue();
// We treat RegExp instances as mutable objects (bc they are)
// so by default we assume this could be mutating `value`:
if (pattern.test(value)) {
return <div>{value}</div>;
}
return <div>Default</div>;
}