diff --git a/compiler/packages/babel-plugin-react-forget/src/HIR/FindContextIdentifiers.ts b/compiler/packages/babel-plugin-react-forget/src/HIR/FindContextIdentifiers.ts index a7a3b6813b..dd8f0bfc8a 100644 --- a/compiler/packages/babel-plugin-react-forget/src/HIR/FindContextIdentifiers.ts +++ b/compiler/packages/babel-plugin-react-forget/src/HIR/FindContextIdentifiers.ts @@ -11,71 +11,44 @@ import { CompilerError } from "../CompilerError"; import { Set_union } from "../Utils/utils"; import { GeneratedSource } from "./HIR"; +type BabelFunction = + | NodePath + | NodePath + | NodePath + | NodePath; type FindContextIdentifierState = { - currentLambda: Array< - | NodePath - | NodePath - | NodePath - | NodePath - >; + currentFn: Array; reassigned: Set; referenced: Set; }; +const withFunctionScope = { + enter: function ( + path: BabelFunction, + state: FindContextIdentifierState + ): void { + state.currentFn.push(path); + }, + exit: function (_: BabelFunction, state: FindContextIdentifierState): void { + state.currentFn.pop(); + }, +}; + export function findContextIdentifiers( func: NodePath ): Set { const state: FindContextIdentifierState = { - currentLambda: [], + currentFn: [], reassigned: new Set(), referenced: new Set(), }; func.traverse( { - FunctionDeclaration: { - enter( - fn: NodePath, - state: FindContextIdentifierState - ): void { - state.currentLambda.push(fn); - }, - exit( - fn: NodePath, - state: FindContextIdentifierState - ): void { - state.currentLambda.pop(); - }, - }, - FunctionExpression: { - enter( - fn: NodePath, - state: FindContextIdentifierState - ): void { - state.currentLambda.push(fn); - }, - exit( - _fn: NodePath, - state: FindContextIdentifierState - ): void { - state.currentLambda.pop(); - }, - }, - - ArrowFunctionExpression: { - enter( - fn: NodePath, - state: FindContextIdentifierState - ): void { - state.currentLambda.push(fn); - }, - exit( - _fn: NodePath, - state: FindContextIdentifierState - ): void { - state.currentLambda.pop(); - }, - }, + FunctionDeclaration: withFunctionScope, + FunctionExpression: withFunctionScope, + ArrowFunctionExpression: withFunctionScope, + ObjectMethod: withFunctionScope, AssignmentExpression( path: NodePath, state: FindContextIdentifierState @@ -83,19 +56,13 @@ export function findContextIdentifiers( const left = path.get("left"); handleAssignment(state.reassigned, left); }, - ObjectMethod( - fn: NodePath, - state: FindContextIdentifierState - ): void { - state.currentLambda.push(fn); - }, Identifier( path: NodePath, state: FindContextIdentifierState ): void { - const currentLambda = state.currentLambda.at(-1); - if (currentLambda !== undefined) - handleIdentifier(currentLambda, state.referenced, path); + const currentFn = state.currentFn.at(-1); + if (currentFn !== undefined) + handleIdentifier(currentFn, state.referenced, path); }, }, state @@ -104,17 +71,13 @@ export function findContextIdentifiers( } function handleIdentifier( - currentLambda: - | NodePath - | NodePath - | NodePath - | NodePath, + currentFn: BabelFunction, referenced: Set, path: NodePath ): void { const name = path.node.name; const binding = path.scope.getBinding(name); - const bindingAboveLambdaScope = currentLambda.scope.parent.getBinding(name); + const bindingAboveLambdaScope = currentFn.scope.parent.getBinding(name); if (binding != null && binding === bindingAboveLambdaScope) { referenced.add(binding.identifier); @@ -126,7 +89,7 @@ function handleAssignment( lvalPath: NodePath ): void { /* - * Find all reassignments to identifiers declared outside of currentLambda + * Find all reassignments to identifiers declared outside of currentFn * This closely follows destructuring assignment assumptions and logic in BuildHIR */ const lvalNode = lvalPath.node; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-across-objectmethod-def.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-across-objectmethod-def.expect.md new file mode 100644 index 0000000000..3f4240592a --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-across-objectmethod-def.expect.md @@ -0,0 +1,51 @@ + +## Input + +```javascript +import { identity } from "shared-runtime"; + +// repro for context identifier scoping bug, in which x was +// inferred as a context variable. + +function Component() { + let x = 2; + const obj = { + method() {}, + }; + x = 4; + identity(obj); + // constant propagation should return 4 here + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + +``` + +## Code + +```javascript +import { identity } from "shared-runtime"; + +// repro for context identifier scoping bug, in which x was +// inferred as a context variable. + +function Component() { + const obj = { method() {} }; + + identity(obj); + return 4; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + +``` + +### Eval output +(kind: ok) 4 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-across-objectmethod-def.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-across-objectmethod-def.js new file mode 100644 index 0000000000..f7575d5e1e --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-across-objectmethod-def.js @@ -0,0 +1,20 @@ +import { identity } from "shared-runtime"; + +// repro for context identifier scoping bug, in which x was +// inferred as a context variable. + +function Component() { + let x = 2; + const obj = { + method() {}, + }; + x = 4; + identity(obj); + // constant propagation should return 4 here + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-objectmethod.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-objectmethod.expect.md new file mode 100644 index 0000000000..f0291aa32b --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-objectmethod.expect.md @@ -0,0 +1,64 @@ + +## Input + +```javascript +import { invoke } from "shared-runtime"; + +function Component({ cond }) { + let x = 2; + const obj = { + method(cond) { + if (cond) { + x = 4; + } + }, + }; + invoke(obj.method, cond); + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ cond: true }], +}; + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +import { invoke } from "shared-runtime"; + +function Component(t24) { + const $ = useMemoCache(2); + const { cond } = t24; + let x; + if ($[0] !== cond) { + x = 2; + const obj = { + method(cond_0) { + if (cond_0) { + x = 4; + } + }, + }; + + invoke(obj.method, cond); + $[0] = cond; + $[1] = x; + } else { + x = $[1]; + } + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ cond: true }], +}; + +``` + +### Eval output +(kind: ok) 4 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-objectmethod.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-objectmethod.js new file mode 100644 index 0000000000..ceabb24d8f --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-objectmethod.js @@ -0,0 +1,19 @@ +import { invoke } from "shared-runtime"; + +function Component({ cond }) { + let x = 2; + const obj = { + method(cond) { + if (cond) { + x = 4; + } + }, + }; + invoke(obj.method, cond); + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ cond: true }], +};