From 61e97dc2788558072ca5b01d2abac9ad7fdbda63 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Mon, 27 Mar 2023 10:34:10 -0700 Subject: [PATCH] Support RegExp literals New InstructionValue variant since RegExp literals are valid expressions. --- compiler/forget/src/HIR/BuildHIR.ts | 9 +++ compiler/forget/src/HIR/HIR.ts | 6 ++ compiler/forget/src/HIR/PrintHIR.ts | 4 ++ compiler/forget/src/HIR/visitors.ts | 2 + .../src/Inference/InferReferenceEffects.ts | 6 ++ .../src/Optimization/DeadCodeElimination.ts | 1 + .../ReactiveScopes/CodegenReactiveFunction.ts | 4 ++ .../InferReactiveScopeVariables.ts | 1 + .../ReactiveScopes/PruneNonEscapingScopes.ts | 1 + .../compiler/regexp-literal.expect.md | 57 +++++++++++++++++++ .../fixtures/compiler/regexp-literal.js | 10 ++++ 11 files changed, 101 insertions(+) create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/regexp-literal.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/regexp-literal.js diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index 7605164c26..87afe579c1 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -1810,6 +1810,15 @@ function lowerExpression( loc: exprLoc, }; } + case "RegExpLiteral": { + let expr = exprPath as NodePath; + 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`, diff --git a/compiler/forget/src/HIR/HIR.ts b/compiler/forget/src/HIR/HIR.ts index 8d050e6808..678c39471c 100644 --- a/compiler/forget/src/HIR/HIR.ts +++ b/compiler/forget/src/HIR/HIR.ts @@ -565,6 +565,12 @@ export type InstructionValue = loc: SourceLocation; } | { kind: "JsxFragment"; children: Array; loc: SourceLocation } + | { + kind: "RegExpLiteral"; + pattern: string; + flags: string; + loc: SourceLocation; + } // store `object.property = value` | { diff --git a/compiler/forget/src/HIR/PrintHIR.ts b/compiler/forget/src/HIR/PrintHIR.ts index e5c92702c9..f4d8e76fbe 100644 --- a/compiler/forget/src/HIR/PrintHIR.ts +++ b/compiler/forget/src/HIR/PrintHIR.ts @@ -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, diff --git a/compiler/forget/src/HIR/visitors.ts b/compiler/forget/src/HIR/visitors.ts index a34acc5083..bca0080d68 100644 --- a/compiler/forget/src/HIR/visitors.ts +++ b/compiler/forget/src/HIR/visitors.ts @@ -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": diff --git a/compiler/forget/src/Inference/InferReferenceEffects.ts b/compiler/forget/src/Inference/InferReferenceEffects.ts index 51198aac88..fb04c74f79 100644 --- a/compiler/forget/src/Inference/InferReferenceEffects.ts +++ b/compiler/forget/src/Inference/InferReferenceEffects.ts @@ -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": { diff --git a/compiler/forget/src/Optimization/DeadCodeElimination.ts b/compiler/forget/src/Optimization/DeadCodeElimination.ts index 1ef41881bb..fe7edaa888 100644 --- a/compiler/forget/src/Optimization/DeadCodeElimination.ts +++ b/compiler/forget/src/Optimization/DeadCodeElimination.ts @@ -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": diff --git a/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts index eda9cdd0f1..27c2263af1 100644 --- a/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -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": { diff --git a/compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts b/compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts index db107e997b..888cb83499 100644 --- a/compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts +++ b/compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts @@ -235,6 +235,7 @@ function mayAllocate(value: InstructionValue): boolean { case "Primitive": { return false; } + case "RegExpLiteral": case "MethodCall": case "PropertyStore": case "ComputedStore": diff --git a/compiler/forget/src/ReactiveScopes/PruneNonEscapingScopes.ts b/compiler/forget/src/ReactiveScopes/PruneNonEscapingScopes.ts index 4a9a997ebe..51d81fe8bb 100644 --- a/compiler/forget/src/ReactiveScopes/PruneNonEscapingScopes.ts +++ b/compiler/forget/src/ReactiveScopes/PruneNonEscapingScopes.ts @@ -509,6 +509,7 @@ function computeMemoizationInputs( }; } case "OptionalCall": + case "RegExpLiteral": case "FunctionExpression": case "TaggedTemplateExpression": case "CallExpression": diff --git a/compiler/forget/src/__tests__/fixtures/compiler/regexp-literal.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/regexp-literal.expect.md new file mode 100644 index 0000000000..c93caa89fb --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/regexp-literal.expect.md @@ -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
{value}
; + } + return
Default
; +} + +``` + +## 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 =
{value}
; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; + } + let t2; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t2 =
Default
; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/regexp-literal.js b/compiler/forget/src/__tests__/fixtures/compiler/regexp-literal.js new file mode 100644 index 0000000000..9b22bb7f95 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/regexp-literal.js @@ -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
{value}
; + } + return
Default
; +}