From 1b214525b22b650eb4a5b349d3e927b5a15d9326 Mon Sep 17 00:00:00 2001 From: Mofei Zhang Date: Tue, 5 Nov 2024 18:34:35 -0500 Subject: [PATCH] [compiler] Delete propagateScopeDeps (non-hir) `enablePropagateScopeDepsHIR` is now used extensively in Meta. This has been tested for over two weeks in our e2e tests and production. The rest of this stack deletes `LoweredFunction.dependencies`, which the non-hir version of `PropagateScopeDeps` depends on. To avoid a more forked HIR (non-hir with dependencies and hir with no dependencies), let's go ahead and clean up the non-hir version of PropagateScopeDepsHIR. Note that all fixture changes in this PR were previously reviewed when they were copied to `propagate-scope-deps-hir-fork`. Will clean up / merge these duplicate fixtures in a later PR ' --- .../src/Entrypoint/Pipeline.ts | 24 +- .../src/HIR/Environment.ts | 10 - .../PropagateScopeDependencies.ts | 1324 ----------------- .../src/ReactiveScopes/index.ts | 1 - ...ug-invalid-hoisting-functionexpr.expect.md | 4 +- ...-try-catch-maybe-null-dependency.expect.md | 16 +- .../capturing-func-mutate-2.expect.md | 4 +- .../conditional-break-labeled.expect.md | 18 +- .../conditional-early-return.expect.md | 78 +- .../compiler/conditional-on-mutable.expect.md | 24 +- ...rly-return-within-reactive-scope.expect.md | 26 +- ...rly-return-within-reactive-scope.expect.md | 20 +- ...ession-with-conditional-optional.expect.md | 50 + ...r-expression-with-conditional-optional.js} | 0 ...mber-expression-with-conditional.expect.md | 50 + ...nal-member-expression-with-conditional.js} | 0 ...-optional-call-chain-in-optional.expect.md | 2 +- ...unctionexpr-conditional-access-2.expect.md | 4 +- .../functionexpr-conditional-access-2.tsx | 2 +- ...r\342\200\223conditional-access.expect.md" | 8 +- ...tionexpr\342\200\223conditional-access.js" | 2 +- .../iife-return-modified-later-phi.expect.md | 11 +- ...equential-optional-chain-nonnull.expect.md | 4 +- .../compiler/nested-optional-chains.expect.md | 12 +- ...consequent-alternate-both-return.expect.md | 11 +- ...ession-with-conditional-optional.expect.md | 74 - ...mber-expression-with-conditional.expect.md | 74 - ...rly-return-within-reactive-scope.expect.md | 22 +- ...ence-array-push-consecutive-phis.expect.md | 18 +- .../phi-type-inference-array-push.expect.md | 11 +- ...hi-type-inference-property-store.expect.md | 11 +- ...ack-conditional-access-own-scope.expect.md | 50 + ...eCallback-conditional-access-own-scope.ts} | 0 ...ck-infer-conditional-value-block.expect.md | 59 + ...Callback-infer-conditional-value-block.ts} | 0 ...less-specific-conditional-access.expect.md | 2 + ...ack-conditional-access-own-scope.expect.md | 58 - ...ck-infer-conditional-value-block.expect.md | 63 - ...properties-inside-optional-chain.expect.md | 4 +- ...-in-returned-function-expression.expect.md | 4 +- ...function-cond-access-not-hoisted.expect.md | 4 +- ...e-uncond-optional-chain-and-cond.expect.md | 4 +- .../join-uncond-scopes-cond-deps.expect.md | 15 +- .../promote-uncond.expect.md | 11 +- .../ssa-cascading-eliminated-phis.expect.md | 25 +- .../compiler/ssa-leave-case.expect.md | 11 +- ...ernary-destruction-with-mutation.expect.md | 12 +- ...ssa-renaming-ternary-destruction.expect.md | 11 +- ...a-renaming-ternary-with-mutation.expect.md | 12 +- .../compiler/ssa-renaming-ternary.expect.md | 11 +- ...onditional-ternary-with-mutation.expect.md | 12 +- ...a-renaming-unconditional-ternary.expect.md | 12 +- ...ming-unconditional-with-mutation.expect.md | 12 +- ...-via-destructuring-with-mutation.expect.md | 12 +- .../ssa-renaming-with-mutation.expect.md | 12 +- .../switch-non-final-default.expect.md | 31 +- .../fixtures/compiler/switch.expect.md | 26 +- .../try-catch-mutate-outer-value.expect.md | 16 +- ...-expression-returns-caught-value.expect.md | 4 +- ...ject-method-returns-caught-value.expect.md | 4 +- .../useMemo-multiple-if-else.expect.md | 22 +- 61 files changed, 566 insertions(+), 1868 deletions(-) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateScopeDependencies.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{optional-member-expression-with-conditional-optional.js => error.hoist-optional-member-expression-with-conditional-optional.js} (100%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{optional-member-expression-with-conditional.js => error.hoist-optional-member-expression-with-conditional.js} (100%) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-with-conditional-optional.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-with-conditional.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-conditional-access-own-scope.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/{useCallback-conditional-access-own-scope.ts => error.hoist-useCallback-conditional-access-own-scope.ts} (100%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-infer-conditional-value-block.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/{useCallback-infer-conditional-value-block.ts => error.hoist-useCallback-infer-conditional-value-block.ts} (100%) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-conditional-access-own-scope.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-infer-conditional-value-block.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index 7ae520a144c9b..1127e91029328 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -57,7 +57,6 @@ import { mergeReactiveScopesThatInvalidateTogether, promoteUsedTemporaries, propagateEarlyReturns, - propagateScopeDependencies, pruneHoistedContexts, pruneNonEscapingScopes, pruneNonReactiveDependencies, @@ -348,14 +347,12 @@ function* runWithEnvironment( }); assertTerminalSuccessorsExist(hir); assertTerminalPredsExist(hir); - if (env.config.enablePropagateDepsInHIR) { - propagateScopeDependenciesHIR(hir); - yield log({ - kind: 'hir', - name: 'PropagateScopeDependenciesHIR', - value: hir, - }); - } + propagateScopeDependenciesHIR(hir); + yield log({ + kind: 'hir', + name: 'PropagateScopeDependenciesHIR', + value: hir, + }); if (env.config.inlineJsxTransform) { inlineJsxTransform(hir, env.config.inlineJsxTransform); @@ -383,15 +380,6 @@ function* runWithEnvironment( }); assertScopeInstructionsWithinScopes(reactiveFunction); - if (!env.config.enablePropagateDepsInHIR) { - propagateScopeDependencies(reactiveFunction); - yield log({ - kind: 'reactive', - name: 'PropagateScopeDependencies', - value: reactiveFunction, - }); - } - pruneNonEscapingScopes(reactiveFunction); yield log({ kind: 'reactive', diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 1189f2e125886..1d2e155848802 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -231,16 +231,6 @@ const EnvironmentConfigSchema = z.object({ */ enableUseTypeAnnotations: z.boolean().default(false), - enablePropagateDepsInHIR: z.boolean().default(false), - - /** - * Enables inference of optional dependency chains. Without this flag - * a property chain such as `props?.items?.foo` will infer as a dep on - * just `props`. With this flag enabled, we'll infer that full path as - * the dependency. - */ - enableOptionalDependencies: z.boolean().default(true), - /** * Enables inlining ReactElement object literals in place of JSX * An alternative to the standard JSX transform which replaces JSX with React's jsxProd() runtime diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateScopeDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateScopeDependencies.ts deleted file mode 100644 index dc1142b271e77..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateScopeDependencies.ts +++ /dev/null @@ -1,1324 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import {CompilerError} from '../CompilerError'; -import {Environment} from '../HIR'; -import { - areEqualPaths, - BlockId, - DeclarationId, - GeneratedSource, - Identifier, - InstructionId, - InstructionKind, - isObjectMethodType, - isRefValueType, - isUseRefType, - makeInstructionId, - Place, - PrunedReactiveScopeBlock, - ReactiveFunction, - ReactiveInstruction, - ReactiveOptionalCallValue, - ReactiveScope, - ReactiveScopeBlock, - ReactiveScopeDependency, - ReactiveTerminalStatement, - ReactiveValue, - ScopeId, -} from '../HIR/HIR'; -import {eachInstructionValueOperand, eachPatternOperand} from '../HIR/visitors'; -import {empty, Stack} from '../Utils/Stack'; -import {assertExhaustive, Iterable_some} from '../Utils/utils'; -import { - ReactiveScopeDependencyTree, - ReactiveScopePropertyDependency, -} from './DeriveMinimalDependencies'; -import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors'; - -/* - * Infers the dependencies of each scope to include variables whose values - * are non-stable and created prior to the start of the scope. Also propagates - * dependencies upwards, so that parent scope dependencies are the union of - * their direct dependencies and those of their child scopes. - */ -export function propagateScopeDependencies(fn: ReactiveFunction): void { - const escapingTemporaries: TemporariesUsedOutsideDefiningScope = { - declarations: new Map(), - usedOutsideDeclaringScope: new Set(), - }; - visitReactiveFunction(fn, new FindPromotedTemporaries(), escapingTemporaries); - - const context = new Context(escapingTemporaries.usedOutsideDeclaringScope); - for (const param of fn.params) { - if (param.kind === 'Identifier') { - context.declare(param.identifier, { - id: makeInstructionId(0), - scope: empty(), - }); - } else { - context.declare(param.place.identifier, { - id: makeInstructionId(0), - scope: empty(), - }); - } - } - visitReactiveFunction(fn, new PropagationVisitor(fn.env), context); -} - -type TemporariesUsedOutsideDefiningScope = { - /* - * tracks all relevant temporary declarations (currently LoadLocal and PropertyLoad) - * and the scope where they are defined - */ - declarations: Map; - // temporaries used outside of their defining scope - usedOutsideDeclaringScope: Set; -}; -class FindPromotedTemporaries extends ReactiveFunctionVisitor { - scopes: Array = []; - - override visitScope( - scope: ReactiveScopeBlock, - state: TemporariesUsedOutsideDefiningScope, - ): void { - this.scopes.push(scope.scope.id); - this.traverseScope(scope, state); - this.scopes.pop(); - } - - override visitInstruction( - instruction: ReactiveInstruction, - state: TemporariesUsedOutsideDefiningScope, - ): void { - // Visit all places first, then record temporaries which may need to be promoted - this.traverseInstruction(instruction, state); - - const scope = this.scopes.at(-1); - if (instruction.lvalue === null || scope === undefined) { - return; - } - switch (instruction.value.kind) { - case 'LoadLocal': - case 'LoadContext': - case 'PropertyLoad': { - state.declarations.set( - instruction.lvalue.identifier.declarationId, - scope, - ); - break; - } - default: { - break; - } - } - } - - override visitPlace( - _id: InstructionId, - place: Place, - state: TemporariesUsedOutsideDefiningScope, - ): void { - const declaringScope = state.declarations.get( - place.identifier.declarationId, - ); - if (declaringScope === undefined) { - return; - } - if (this.scopes.indexOf(declaringScope) === -1) { - // Declaring scope is not active === used outside declaring scope - state.usedOutsideDeclaringScope.add(place.identifier.declarationId); - } - } -} - -type DeclMap = Map; -type Decl = { - id: InstructionId; - scope: Stack; -}; - -/** - * TraversalState and PoisonState is used to track the poisoned state of a scope. - * - * A scope is poisoned when either of these conditions hold: - * - one of its own nested blocks is a jump target (for break/continues) - * - it is a outermost scope and contains a throw / return - * - * When a scope is poisoned, all dependencies (from instructions and inner scopes) - * are added as conditionally accessed. - */ -type ScopeTraversalState = { - value: ReactiveScope; - ownBlocks: Stack; -}; - -class PoisonState { - poisonedBlocks: Set = new Set(); - poisonedScopes: Set = new Set(); - isPoisoned: boolean = false; - - constructor( - poisonedBlocks: Set, - poisonedScopes: Set, - isPoisoned: boolean, - ) { - this.poisonedBlocks = poisonedBlocks; - this.poisonedScopes = poisonedScopes; - this.isPoisoned = isPoisoned; - } - - clone(): PoisonState { - return new PoisonState( - new Set(this.poisonedBlocks), - new Set(this.poisonedScopes), - this.isPoisoned, - ); - } - - take(other: PoisonState): PoisonState { - const copy = new PoisonState( - this.poisonedBlocks, - this.poisonedScopes, - this.isPoisoned, - ); - this.poisonedBlocks = other.poisonedBlocks; - this.poisonedScopes = other.poisonedScopes; - this.isPoisoned = other.isPoisoned; - return copy; - } - - merge( - others: Array, - currentScope: ScopeTraversalState | null, - ): void { - for (const other of others) { - for (const id of other.poisonedBlocks) { - this.poisonedBlocks.add(id); - } - for (const id of other.poisonedScopes) { - this.poisonedScopes.add(id); - } - } - this.#invalidate(currentScope); - } - - #invalidate(currentScope: ScopeTraversalState | null): void { - if (currentScope != null) { - if (this.poisonedScopes.has(currentScope.value.id)) { - this.isPoisoned = true; - return; - } else if ( - currentScope.ownBlocks.find(blockId => this.poisonedBlocks.has(blockId)) - ) { - this.isPoisoned = true; - return; - } - } - this.isPoisoned = false; - } - - /** - * Mark a block or scope as poisoned and update the `isPoisoned` flag. - * - * @param targetBlock id of the block which ends non-linear control flow. - * For a break/continue instruction, this is the target block. - * Throw and return instructions have no target and will poison the earliest - * active scope - */ - addPoisonTarget( - target: BlockId | null, - activeScopes: Stack, - ): void { - const currentScope = activeScopes.value; - if (target == null && currentScope != null) { - let cursor = activeScopes; - while (true) { - const next = cursor.pop(); - if (next.value == null) { - const poisonedScope = cursor.value!.value.id; - this.poisonedScopes.add(poisonedScope); - if (poisonedScope === currentScope?.value.id) { - this.isPoisoned = true; - } - break; - } else { - cursor = next; - } - } - } else if (target != null) { - this.poisonedBlocks.add(target); - if ( - !this.isPoisoned && - currentScope?.ownBlocks.find(blockId => blockId === target) - ) { - this.isPoisoned = true; - } - } - } - - /** - * Invoked during traversal when a poisoned scope becomes inactive - * @param id - * @param currentScope - */ - removeMaybePoisonedScope( - id: ScopeId, - currentScope: ScopeTraversalState | null, - ): void { - this.poisonedScopes.delete(id); - this.#invalidate(currentScope); - } - - removeMaybePoisonedBlock( - id: BlockId, - currentScope: ScopeTraversalState | null, - ): void { - this.poisonedBlocks.delete(id); - this.#invalidate(currentScope); - } -} - -class Context { - #temporariesUsedOutsideScope: Set; - #declarations: DeclMap = new Map(); - #reassignments: Map = new Map(); - // Reactive dependencies used in the current reactive scope. - #dependencies: ReactiveScopeDependencyTree = - new ReactiveScopeDependencyTree(); - /* - * We keep a sidemap for temporaries created by PropertyLoads, and do - * not store any control flow (i.e. #inConditionalWithinScope) here. - * - a ReactiveScope (A) containing a PropertyLoad may differ from the - * ReactiveScope (B) that uses the produced temporary. - * - codegen will inline these PropertyLoads back into scope (B) - */ - #properties: Map = new Map(); - #temporaries: Map = new Map(); - #inConditionalWithinScope: boolean = false; - /* - * Reactive dependencies used unconditionally in the current conditional. - * Composed of dependencies: - * - directly accessed within block (added in visitDep) - * - accessed by all cfg branches (added through promoteDeps) - */ - #depsInCurrentConditional: ReactiveScopeDependencyTree = - new ReactiveScopeDependencyTree(); - #scopes: Stack = empty(); - poisonState: PoisonState = new PoisonState(new Set(), new Set(), false); - - constructor(temporariesUsedOutsideScope: Set) { - this.#temporariesUsedOutsideScope = temporariesUsedOutsideScope; - } - - enter(scope: ReactiveScope, fn: () => void): Set { - // Save context of previous scope - const prevInConditional = this.#inConditionalWithinScope; - const previousDependencies = this.#dependencies; - const prevDepsInConditional: ReactiveScopeDependencyTree | null = this - .isPoisoned - ? this.#depsInCurrentConditional - : null; - if (prevDepsInConditional != null) { - this.#depsInCurrentConditional = new ReactiveScopeDependencyTree(); - } - - /* - * Set context for new scope - * A nested scope should add all deps it directly uses as its own - * unconditional deps, regardless of whether the nested scope is itself - * within a conditional - */ - const scopedDependencies = new ReactiveScopeDependencyTree(); - this.#inConditionalWithinScope = false; - this.#dependencies = scopedDependencies; - this.#scopes = this.#scopes.push({ - value: scope, - ownBlocks: empty(), - }); - this.poisonState.isPoisoned = false; - - fn(); - - // Restore context of previous scope - this.#scopes = this.#scopes.pop(); - this.poisonState.removeMaybePoisonedScope(scope.id, this.#scopes.value); - - this.#dependencies = previousDependencies; - this.#inConditionalWithinScope = prevInConditional; - - // Derive minimal dependencies now, since next line may mutate scopedDependencies - const minInnerScopeDependencies = - scopedDependencies.deriveMinimalDependencies(); - - /* - * propagate dependencies upward using the same rules as normal dependency - * collection. child scopes may have dependencies on values created within - * the outer scope, which necessarily cannot be dependencies of the outer - * scope - */ - this.#dependencies.addDepsFromInnerScope( - scopedDependencies, - this.#inConditionalWithinScope || this.isPoisoned, - this.#checkValidDependency.bind(this), - ); - - if (prevDepsInConditional != null) { - // Outer scope is poisoned - prevDepsInConditional.addDepsFromInnerScope( - this.#depsInCurrentConditional, - true, - this.#checkValidDependency.bind(this), - ); - this.#depsInCurrentConditional = prevDepsInConditional; - } - - return minInnerScopeDependencies; - } - - isUsedOutsideDeclaringScope(place: Place): boolean { - return this.#temporariesUsedOutsideScope.has( - place.identifier.declarationId, - ); - } - - /* - * Prints dependency tree to string for debugging. - * @param includeAccesses - * @returns string representation of DependencyTree - */ - printDeps(includeAccesses: boolean = false): string { - return this.#dependencies.printDeps(includeAccesses); - } - - /* - * We track and return unconditional accesses / deps within this conditional. - * If an object property is always used (i.e. in every conditional path), we - * want to promote it to an unconditional access / dependency. - * - * The caller of `enterConditional` is responsible determining for promotion. - * i.e. call promoteDepsFromExhaustiveConditionals to merge returned results. - * - * e.g. we want to mark props.a.b as an unconditional dep here - * if (foo(...)) { - * access(props.a.b); - * } else { - * access(props.a.b); - * } - */ - enterConditional(fn: () => void): ReactiveScopeDependencyTree { - const prevInConditional = this.#inConditionalWithinScope; - const prevUncondAccessed = this.#depsInCurrentConditional; - this.#inConditionalWithinScope = true; - this.#depsInCurrentConditional = new ReactiveScopeDependencyTree(); - fn(); - const result = this.#depsInCurrentConditional; - this.#inConditionalWithinScope = prevInConditional; - this.#depsInCurrentConditional = prevUncondAccessed; - return result; - } - - /* - * Add dependencies from exhaustive CFG paths into the current ReactiveDeps - * tree. If a property is used in every CFG path, it is promoted to an - * unconditional access / dependency here. - * @param depsInConditionals - */ - promoteDepsFromExhaustiveConditionals( - depsInConditionals: Array, - ): void { - this.#dependencies.promoteDepsFromExhaustiveConditionals( - depsInConditionals, - ); - this.#depsInCurrentConditional.promoteDepsFromExhaustiveConditionals( - depsInConditionals, - ); - } - - /* - * Records where a value was declared, and optionally, the scope where the value originated from. - * This is later used to determine if a dependency should be added to a scope; if the current - * scope we are visiting is the same scope where the value originates, it can't be a dependency - * on itself. - */ - declare(identifier: Identifier, decl: Decl): void { - if (!this.#declarations.has(identifier.declarationId)) { - this.#declarations.set(identifier.declarationId, decl); - } - this.#reassignments.set(identifier, decl); - } - - declareTemporary(lvalue: Place, place: Place): void { - this.#temporaries.set(lvalue.identifier, place); - } - - resolveTemporary(place: Place): Place { - return this.#temporaries.get(place.identifier) ?? place; - } - - #getProperty( - object: Place, - property: string, - optional: boolean, - ): ReactiveScopePropertyDependency { - const resolvedObject = this.resolveTemporary(object); - const resolvedDependency = this.#properties.get(resolvedObject.identifier); - let objectDependency: ReactiveScopePropertyDependency; - /* - * (1) Create the base property dependency as either a LoadLocal (from a temporary) - * or a deep copy of an existing property dependency. - */ - if (resolvedDependency === undefined) { - objectDependency = { - identifier: resolvedObject.identifier, - path: [], - }; - } else { - objectDependency = { - identifier: resolvedDependency.identifier, - path: [...resolvedDependency.path], - }; - } - - objectDependency.path.push({property, optional}); - - return objectDependency; - } - - declareProperty( - lvalue: Place, - object: Place, - property: string, - optional: boolean, - ): void { - const nextDependency = this.#getProperty(object, property, optional); - this.#properties.set(lvalue.identifier, nextDependency); - } - - // Checks if identifier is a valid dependency in the current scope - #checkValidDependency(maybeDependency: ReactiveScopeDependency): boolean { - // ref.current access is not a valid dep - if ( - isUseRefType(maybeDependency.identifier) && - maybeDependency.path.at(0)?.property === 'current' - ) { - return false; - } - - // ref value is not a valid dep - if (isRefValueType(maybeDependency.identifier)) { - return false; - } - - /* - * object methods are not deps because they will be codegen'ed back in to - * the object literal. - */ - if (isObjectMethodType(maybeDependency.identifier)) { - return false; - } - - const identifier = maybeDependency.identifier; - /* - * If this operand is used in a scope, has a dynamic value, and was defined - * before this scope, then its a dependency of the scope. - */ - const currentDeclaration = - this.#reassignments.get(identifier) ?? - this.#declarations.get(identifier.declarationId); - const currentScope = this.currentScope.value?.value; - return ( - currentScope != null && - currentDeclaration !== undefined && - currentDeclaration.id < currentScope.range.start && - (currentDeclaration.scope == null || - currentDeclaration.scope.value?.value !== currentScope) - ); - } - - #isScopeActive(scope: ReactiveScope): boolean { - if (this.#scopes === null) { - return false; - } - return this.#scopes.find(state => state.value === scope); - } - - get currentScope(): Stack { - return this.#scopes; - } - - get isPoisoned(): boolean { - return this.poisonState.isPoisoned; - } - - visitOperand(place: Place): void { - const resolved = this.resolveTemporary(place); - /* - * if this operand is a temporary created for a property load, try to resolve it to - * the expanded Place. Fall back to using the operand as-is. - */ - - let dependency: ReactiveScopePropertyDependency = { - identifier: resolved.identifier, - path: [], - }; - if (resolved.identifier.name === null) { - const propertyDependency = this.#properties.get(resolved.identifier); - if (propertyDependency !== undefined) { - dependency = {...propertyDependency}; - } - } - this.visitDependency(dependency); - } - - visitProperty(object: Place, property: string, optional: boolean): void { - const nextDependency = this.#getProperty(object, property, optional); - this.visitDependency(nextDependency); - } - - visitDependency(maybeDependency: ReactiveScopePropertyDependency): void { - /* - * Any value used after its originally defining scope has concluded must be added as an - * output of its defining scope. Regardless of whether its a const or not, - * some later code needs access to the value. If the current - * scope we are visiting is the same scope where the value originates, it can't be a dependency - * on itself. - */ - - /* - * if originalDeclaration is undefined here, then this is a free var - * (all other decls e.g. `let x;` should be initialized in BuildHIR) - */ - const originalDeclaration = this.#declarations.get( - maybeDependency.identifier.declarationId, - ); - if ( - originalDeclaration !== undefined && - originalDeclaration.scope.value !== null - ) { - originalDeclaration.scope.each(scope => { - if ( - !this.#isScopeActive(scope.value) && - // TODO LeaveSSA: key scope.declarations by DeclarationId - !Iterable_some( - scope.value.declarations.values(), - decl => - decl.identifier.declarationId === - maybeDependency.identifier.declarationId, - ) - ) { - scope.value.declarations.set(maybeDependency.identifier.id, { - identifier: maybeDependency.identifier, - scope: originalDeclaration.scope.value!.value, - }); - } - }); - } - - if (this.#checkValidDependency(maybeDependency)) { - const isPoisoned = this.isPoisoned; - this.#depsInCurrentConditional.add(maybeDependency, isPoisoned); - /* - * Add info about this dependency to the existing tree - * We do not try to join/reduce dependencies here due to missing info - */ - this.#dependencies.add( - maybeDependency, - this.#inConditionalWithinScope || isPoisoned, - ); - } - } - - /* - * Record a variable that is declared in some other scope and that is being reassigned in the - * current one as a {@link ReactiveScope.reassignments} - */ - visitReassignment(place: Place): void { - const currentScope = this.currentScope.value?.value; - if ( - currentScope != null && - !Iterable_some( - currentScope.reassignments, - identifier => - identifier.declarationId === place.identifier.declarationId, - ) && - this.#checkValidDependency({identifier: place.identifier, path: []}) - ) { - // TODO LeaveSSA: scope.reassignments should be keyed by declarationid - currentScope.reassignments.add(place.identifier); - } - } - - pushLabeledBlock(id: BlockId): void { - const currentScope = this.#scopes.value; - if (currentScope != null) { - currentScope.ownBlocks = currentScope.ownBlocks.push(id); - } - } - popLabeledBlock(id: BlockId): void { - const currentScope = this.#scopes.value; - if (currentScope != null) { - const last = currentScope.ownBlocks.value; - currentScope.ownBlocks = currentScope.ownBlocks.pop(); - - CompilerError.invariant(last != null && last === id, { - reason: '[PropagateScopeDependencies] Misformed block stack', - loc: GeneratedSource, - }); - } - this.poisonState.removeMaybePoisonedBlock(id, currentScope); - } -} - -class PropagationVisitor extends ReactiveFunctionVisitor { - env: Environment; - - constructor(env: Environment) { - super(); - this.env = env; - } - - override visitScope(scope: ReactiveScopeBlock, context: Context): void { - const scopeDependencies = context.enter(scope.scope, () => { - this.visitBlock(scope.instructions, context); - }); - for (const candidateDep of scopeDependencies) { - if ( - !Iterable_some( - scope.scope.dependencies, - existingDep => - existingDep.identifier.declarationId === - candidateDep.identifier.declarationId && - areEqualPaths(existingDep.path, candidateDep.path), - ) - ) { - scope.scope.dependencies.add(candidateDep); - } - } - /* - * TODO LeaveSSA: fix existing bug with duplicate deps and reassignments - * see fixture ssa-cascading-eliminated-phis, note that we cache `x` - * twice because its both a dep and a reassignment. - * - * for (const reassignment of scope.scope.reassignments) { - * if ( - * Iterable_some( - * scope.scope.dependencies.values(), - * dep => - * dep.identifier.declarationId === reassignment.declarationId && - * dep.path.length === 0, - * ) - * ) { - * scope.scope.reassignments.delete(reassignment); - * } - * } - */ - } - - override visitPrunedScope( - scopeBlock: PrunedReactiveScopeBlock, - context: Context, - ): void { - /* - * NOTE: we explicitly throw away the deps, we only enter() the scope to record its - * declarations - */ - const _scopeDepdencies = context.enter(scopeBlock.scope, () => { - this.visitBlock(scopeBlock.instructions, context); - }); - } - - override visitInstruction( - instruction: ReactiveInstruction, - context: Context, - ): void { - const {id, value, lvalue} = instruction; - this.visitInstructionValue(context, id, value, lvalue); - if (lvalue == null) { - return; - } - context.declare(lvalue.identifier, { - id, - scope: context.currentScope, - }); - } - - extractOptionalProperty( - context: Context, - optionalValue: ReactiveOptionalCallValue, - lvalue: Place, - ): { - lvalue: Place; - object: Place; - property: string; - optional: boolean; - } | null { - const sequence = optionalValue.value; - CompilerError.invariant(sequence.kind === 'SequenceExpression', { - reason: 'Expected OptionalExpression value to be a SequenceExpression', - description: `Found a \`${sequence.kind}\``, - loc: sequence.loc, - }); - /** - * Base case: inner ` "?." ` - *``` - * = OptionalExpression optional=true (`optionalValue` is here) - * Sequence (`sequence` is here) - * t0 = LoadLocal - * Sequence - * t1 = PropertyLoad t0 . - * LoadLocal t1 - * ``` - */ - if ( - sequence.instructions.length === 1 && - sequence.instructions[0].lvalue !== null && - sequence.instructions[0].value.kind === 'LoadLocal' && - sequence.instructions[0].value.place.identifier.name !== null && - !context.isUsedOutsideDeclaringScope(sequence.instructions[0].lvalue) && - sequence.value.kind === 'SequenceExpression' && - sequence.value.instructions.length === 1 && - sequence.value.instructions[0].value.kind === 'PropertyLoad' && - sequence.value.instructions[0].value.object.identifier.id === - sequence.instructions[0].lvalue.identifier.id && - sequence.value.instructions[0].lvalue !== null && - sequence.value.value.kind === 'LoadLocal' && - sequence.value.value.place.identifier.id === - sequence.value.instructions[0].lvalue.identifier.id - ) { - context.declareTemporary( - sequence.instructions[0].lvalue, - sequence.instructions[0].value.place, - ); - const propertyLoad = sequence.value.instructions[0].value; - return { - lvalue, - object: propertyLoad.object, - property: propertyLoad.property, - optional: optionalValue.optional, - }; - } - /** - * Base case 2: inner ` "." "?." - * ``` - * = OptionalExpression optional=true (`optionalValue` is here) - * Sequence (`sequence` is here) - * t0 = Sequence - * t1 = LoadLocal - * ... // see note - * PropertyLoad t1 . - * [46] Sequence - * t2 = PropertyLoad t0 . - * [46] LoadLocal t2 - * ``` - * - * Note that it's possible to have additional inner chained non-optional - * property loads at "...", from an expression like `a?.b.c.d.e`. We could - * expand to support this case by relaxing the check on the inner sequence - * length, ensuring all instructions after the first LoadLocal are PropertyLoad - * and then iterating to ensure that the lvalue of the previous is always - * the object of the next PropertyLoad, w the final lvalue as the object - * of the sequence.value's object. - * - * But this case is likely rare in practice, usually once you're optional - * chaining all property accesses are optional (not `a?.b.c` but `a?.b?.c`). - * Also, HIR-based PropagateScopeDeps will handle this case so it doesn't - * seem worth it to optimize for that edge-case here. - */ - if ( - sequence.instructions.length === 1 && - sequence.instructions[0].lvalue !== null && - sequence.instructions[0].value.kind === 'SequenceExpression' && - sequence.instructions[0].value.instructions.length === 1 && - sequence.instructions[0].value.instructions[0].lvalue !== null && - sequence.instructions[0].value.instructions[0].value.kind === - 'LoadLocal' && - sequence.instructions[0].value.instructions[0].value.place.identifier - .name !== null && - !context.isUsedOutsideDeclaringScope( - sequence.instructions[0].value.instructions[0].lvalue, - ) && - sequence.instructions[0].value.value.kind === 'PropertyLoad' && - sequence.instructions[0].value.value.object.identifier.id === - sequence.instructions[0].value.instructions[0].lvalue.identifier.id && - sequence.value.kind === 'SequenceExpression' && - sequence.value.instructions.length === 1 && - sequence.value.instructions[0].lvalue !== null && - sequence.value.instructions[0].value.kind === 'PropertyLoad' && - sequence.value.instructions[0].value.object.identifier.id === - sequence.instructions[0].lvalue.identifier.id && - sequence.value.value.kind === 'LoadLocal' && - sequence.value.value.place.identifier.id === - sequence.value.instructions[0].lvalue.identifier.id - ) { - // LoadLocal - context.declareTemporary( - sequence.instructions[0].value.instructions[0].lvalue, - sequence.instructions[0].value.instructions[0].value.place, - ); - // PropertyLoad . (the inner non-optional property) - context.declareProperty( - sequence.instructions[0].lvalue, - sequence.instructions[0].value.value.object, - sequence.instructions[0].value.value.property, - false, - ); - const propertyLoad = sequence.value.instructions[0].value; - return { - lvalue, - object: propertyLoad.object, - property: propertyLoad.property, - optional: optionalValue.optional, - }; - } - - /** - * Composed case: - * - ` "." or "?." ` - * - ` "." or "?>" ` - * - * This case is convoluted, note how `t0` appears as an lvalue *twice* - * and then is an operand of an intermediate LoadLocal and then the - * object of the final PropertyLoad: - * - * ``` - * = OptionalExpression optional=false (`optionalValue` is here) - * Sequence (`sequence` is here) - * t0 = Sequence - * t0 = - * - * LoadLocal t0 - * Sequence - * t1 = PropertyLoad t0. - * LoadLocal t1 - * ``` - */ - if ( - sequence.instructions.length === 1 && - sequence.instructions[0].value.kind === 'SequenceExpression' && - sequence.instructions[0].value.instructions.length === 1 && - sequence.instructions[0].value.instructions[0].lvalue !== null && - sequence.instructions[0].value.instructions[0].value.kind === - 'OptionalExpression' && - sequence.instructions[0].value.value.kind === 'LoadLocal' && - sequence.instructions[0].value.value.place.identifier.id === - sequence.instructions[0].value.instructions[0].lvalue.identifier.id && - sequence.value.kind === 'SequenceExpression' && - sequence.value.instructions.length === 1 && - sequence.value.instructions[0].lvalue !== null && - sequence.value.instructions[0].value.kind === 'PropertyLoad' && - sequence.value.instructions[0].value.object.identifier.id === - sequence.instructions[0].value.value.place.identifier.id && - sequence.value.value.kind === 'LoadLocal' && - sequence.value.value.place.identifier.id === - sequence.value.instructions[0].lvalue.identifier.id - ) { - const {lvalue: innerLvalue, value: innerOptional} = - sequence.instructions[0].value.instructions[0]; - const innerProperty = this.extractOptionalProperty( - context, - innerOptional, - innerLvalue, - ); - if (innerProperty === null) { - return null; - } - context.declareProperty( - innerProperty.lvalue, - innerProperty.object, - innerProperty.property, - innerProperty.optional, - ); - const propertyLoad = sequence.value.instructions[0].value; - return { - lvalue, - object: propertyLoad.object, - property: propertyLoad.property, - optional: optionalValue.optional, - }; - } - return null; - } - - visitOptionalExpression( - context: Context, - id: InstructionId, - value: ReactiveOptionalCallValue, - lvalue: Place | null, - ): void { - /** - * If this is the first optional=true optional in a recursive OptionalExpression - * subtree, we check to see if the subtree is of the form: - * ``` - * NestedOptional = - * ` . / ?. ` - * ` . / ?. ` - * ``` - * - * Ie strictly a chain like `foo?.bar?.baz` or `a?.b.c`. If the subtree contains - * any other types of expressions - for example `foo?.[makeKey(a)]` - then this - * will return null and we'll go to the default handling below. - * - * If the tree does match the NestedOptional shape, then we'll have recorded - * a sequence of declareProperty calls, and the final visitProperty call here - * will record that optional chain as a dependency (since we know it's about - * to be referenced via its lvalue which is non-null). - */ - if ( - lvalue !== null && - value.optional && - this.env.config.enableOptionalDependencies - ) { - const inner = this.extractOptionalProperty(context, value, lvalue); - if (inner !== null) { - context.visitProperty(inner.object, inner.property, inner.optional); - return; - } - } - - // Otherwise we treat everything after the optional as conditional - const inner = value.value; - /* - * OptionalExpression value is a SequenceExpression where the instructions - * represent the code prior to the `?` and the final value represents the - * conditional code that follows. - */ - CompilerError.invariant(inner.kind === 'SequenceExpression', { - reason: 'Expected OptionalExpression value to be a SequenceExpression', - description: `Found a \`${value.kind}\``, - loc: value.loc, - suggestions: null, - }); - // Instructions are the unconditionally executed portion before the `?` - for (const instr of inner.instructions) { - this.visitInstruction(instr, context); - } - // The final value is the conditional portion following the `?` - context.enterConditional(() => { - this.visitReactiveValue(context, id, inner.value, null); - }); - } - - visitReactiveValue( - context: Context, - id: InstructionId, - value: ReactiveValue, - lvalue: Place | null, - ): void { - switch (value.kind) { - case 'OptionalExpression': { - this.visitOptionalExpression(context, id, value, lvalue); - break; - } - case 'LogicalExpression': { - this.visitReactiveValue(context, id, value.left, null); - context.enterConditional(() => { - this.visitReactiveValue(context, id, value.right, null); - }); - break; - } - case 'ConditionalExpression': { - this.visitReactiveValue(context, id, value.test, null); - - const consequentDeps = context.enterConditional(() => { - this.visitReactiveValue(context, id, value.consequent, null); - }); - const alternateDeps = context.enterConditional(() => { - this.visitReactiveValue(context, id, value.alternate, null); - }); - context.promoteDepsFromExhaustiveConditionals([ - consequentDeps, - alternateDeps, - ]); - break; - } - case 'SequenceExpression': { - for (const instr of value.instructions) { - this.visitInstruction(instr, context); - } - this.visitInstructionValue(context, id, value.value, null); - break; - } - case 'FunctionExpression': { - if (this.env.config.enableTreatFunctionDepsAsConditional) { - context.enterConditional(() => { - for (const operand of eachInstructionValueOperand(value)) { - context.visitOperand(operand); - } - }); - } else { - for (const operand of eachInstructionValueOperand(value)) { - context.visitOperand(operand); - } - } - break; - } - case 'ReactiveFunctionValue': { - CompilerError.invariant(false, { - reason: `Unexpected ReactiveFunctionValue`, - loc: value.loc, - description: null, - suggestions: null, - }); - } - default: { - for (const operand of eachInstructionValueOperand(value)) { - context.visitOperand(operand); - } - } - } - } - - visitInstructionValue( - context: Context, - id: InstructionId, - value: ReactiveValue, - lvalue: Place | null, - ): void { - if (value.kind === 'LoadLocal' && lvalue !== null) { - if ( - value.place.identifier.name !== null && - lvalue.identifier.name === null && - !context.isUsedOutsideDeclaringScope(lvalue) - ) { - context.declareTemporary(lvalue, value.place); - } else { - context.visitOperand(value.place); - } - } else if (value.kind === 'PropertyLoad') { - if (lvalue !== null && !context.isUsedOutsideDeclaringScope(lvalue)) { - context.declareProperty(lvalue, value.object, value.property, false); - } else { - context.visitProperty(value.object, value.property, false); - } - } else if (value.kind === 'StoreLocal') { - context.visitOperand(value.value); - if (value.lvalue.kind === InstructionKind.Reassign) { - context.visitReassignment(value.lvalue.place); - } - context.declare(value.lvalue.place.identifier, { - id, - scope: context.currentScope, - }); - } else if ( - value.kind === 'DeclareLocal' || - value.kind === 'DeclareContext' - ) { - /* - * Some variables may be declared and never initialized. We need - * to retain (and hoist) these declarations if they are included - * in a reactive scope. One approach is to simply add all `DeclareLocal`s - * as scope declarations. - */ - - /* - * We add context variable declarations here, not at `StoreContext`, since - * context Store / Loads are modeled as reads and mutates to the underlying - * variable reference (instead of through intermediate / inlined temporaries) - */ - context.declare(value.lvalue.place.identifier, { - id, - scope: context.currentScope, - }); - } else if (value.kind === 'Destructure') { - context.visitOperand(value.value); - for (const place of eachPatternOperand(value.lvalue.pattern)) { - if (value.lvalue.kind === InstructionKind.Reassign) { - context.visitReassignment(place); - } - context.declare(place.identifier, { - id, - scope: context.currentScope, - }); - } - } else { - this.visitReactiveValue(context, id, value, lvalue); - } - } - - enterTerminal(stmt: ReactiveTerminalStatement, context: Context): void { - if (stmt.label != null) { - context.pushLabeledBlock(stmt.label.id); - } - const terminal = stmt.terminal; - switch (terminal.kind) { - case 'continue': - case 'break': { - context.poisonState.addPoisonTarget( - terminal.target, - context.currentScope, - ); - break; - } - case 'throw': - case 'return': { - context.poisonState.addPoisonTarget(null, context.currentScope); - break; - } - } - } - exitTerminal(stmt: ReactiveTerminalStatement, context: Context): void { - if (stmt.label != null) { - context.popLabeledBlock(stmt.label.id); - } - } - - override visitTerminal( - stmt: ReactiveTerminalStatement, - context: Context, - ): void { - this.enterTerminal(stmt, context); - const terminal = stmt.terminal; - switch (terminal.kind) { - case 'break': - case 'continue': { - break; - } - case 'return': { - context.visitOperand(terminal.value); - break; - } - case 'throw': { - context.visitOperand(terminal.value); - break; - } - case 'for': { - this.visitReactiveValue(context, terminal.id, terminal.init, null); - this.visitReactiveValue(context, terminal.id, terminal.test, null); - context.enterConditional(() => { - this.visitBlock(terminal.loop, context); - if (terminal.update !== null) { - this.visitReactiveValue( - context, - terminal.id, - terminal.update, - null, - ); - } - }); - break; - } - case 'for-of': { - this.visitReactiveValue(context, terminal.id, terminal.init, null); - context.enterConditional(() => { - this.visitBlock(terminal.loop, context); - }); - break; - } - case 'for-in': { - this.visitReactiveValue(context, terminal.id, terminal.init, null); - context.enterConditional(() => { - this.visitBlock(terminal.loop, context); - }); - break; - } - case 'do-while': { - this.visitBlock(terminal.loop, context); - context.enterConditional(() => { - this.visitReactiveValue(context, terminal.id, terminal.test, null); - }); - break; - } - case 'while': { - this.visitReactiveValue(context, terminal.id, terminal.test, null); - context.enterConditional(() => { - this.visitBlock(terminal.loop, context); - }); - break; - } - case 'if': { - context.visitOperand(terminal.test); - const {consequent, alternate} = terminal; - /* - * Consequent and alternate branches are mutually exclusive, - * so we save and restore the poison state here. - */ - const prevPoisonState = context.poisonState.clone(); - const depsInIf = context.enterConditional(() => { - this.visitBlock(consequent, context); - }); - if (alternate !== null) { - const ifPoisonState = context.poisonState.take(prevPoisonState); - const depsInElse = context.enterConditional(() => { - this.visitBlock(alternate, context); - }); - context.poisonState.merge( - [ifPoisonState], - context.currentScope.value, - ); - context.promoteDepsFromExhaustiveConditionals([depsInIf, depsInElse]); - } - break; - } - case 'switch': { - context.visitOperand(terminal.test); - const isDefaultOnly = - terminal.cases.length === 1 && terminal.cases[0].test == null; - if (isDefaultOnly) { - const case_ = terminal.cases[0]; - if (case_.block != null) { - this.visitBlock(case_.block, context); - break; - } - } - const depsInCases = []; - let foundDefault = false; - /** - * Switch branches are mutually exclusive - */ - const prevPoisonState = context.poisonState.clone(); - const mutExPoisonStates: Array = []; - /* - * This can underestimate unconditional accesses due to the current - * CFG representation for fallthrough. This is safe. It only - * reduces granularity of dependencies. - */ - for (const {test, block} of terminal.cases) { - if (test !== null) { - context.visitOperand(test); - } else { - foundDefault = true; - } - if (block !== undefined) { - mutExPoisonStates.push( - context.poisonState.take(prevPoisonState.clone()), - ); - depsInCases.push( - context.enterConditional(() => { - this.visitBlock(block, context); - }), - ); - } - } - if (foundDefault) { - context.promoteDepsFromExhaustiveConditionals(depsInCases); - } - context.poisonState.merge( - mutExPoisonStates, - context.currentScope.value, - ); - break; - } - case 'label': { - this.visitBlock(terminal.block, context); - break; - } - case 'try': { - this.visitBlock(terminal.block, context); - this.visitBlock(terminal.handler, context); - break; - } - default: { - assertExhaustive( - terminal, - `Unexpected terminal kind \`${(terminal as any).kind}\``, - ); - } - } - this.exitTerminal(stmt, context); - } -} diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/index.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/index.ts index eb778305611cf..8841ae92795c8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/index.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/index.ts @@ -17,7 +17,6 @@ export {mergeReactiveScopesThatInvalidateTogether} from './MergeReactiveScopesTh export {printReactiveFunction} from './PrintReactiveFunction'; export {promoteUsedTemporaries} from './PromoteUsedTemporaries'; export {propagateEarlyReturns} from './PropagateEarlyReturns'; -export {propagateScopeDependencies} from './PropagateScopeDependencies'; export {pruneAllReactiveScopes} from './PruneAllReactiveScopes'; export {pruneHoistedContexts} from './PruneHoistedContexts'; export {pruneNonEscapingScopes} from './PruneNonEscapingScopes'; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-hoisting-functionexpr.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-hoisting-functionexpr.expect.md index e4e47dfde9e2b..d6331db4e7ea3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-hoisting-functionexpr.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-hoisting-functionexpr.expect.md @@ -58,7 +58,7 @@ function Component(t0) { const $ = _c(5); const { obj, isObjNull } = t0; let t1; - if ($[0] !== isObjNull || $[1] !== obj.prop) { + if ($[0] !== isObjNull || $[1] !== obj) { t1 = () => { if (!isObjNull) { return obj.prop; @@ -67,7 +67,7 @@ function Component(t0) { } }; $[0] = isObjNull; - $[1] = obj.prop; + $[1] = obj; $[2] = t1; } else { t1 = $[2]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-try-catch-maybe-null-dependency.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-try-catch-maybe-null-dependency.expect.md index 56ca1f7722e45..839821b349a6f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-try-catch-maybe-null-dependency.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-try-catch-maybe-null-dependency.expect.md @@ -38,16 +38,24 @@ import { identity } from "shared-runtime"; * try-catch block, as that might throw */ function useFoo(maybeNullObject) { - const $ = _c(2); + const $ = _c(4); let y; - if ($[0] !== maybeNullObject.value.inner) { + if ($[0] !== maybeNullObject) { y = []; try { - y.push(identity(maybeNullObject.value.inner)); + let t0; + if ($[2] !== maybeNullObject.value.inner) { + t0 = identity(maybeNullObject.value.inner); + $[2] = maybeNullObject.value.inner; + $[3] = t0; + } else { + t0 = $[3]; + } + y.push(t0); } catch { y.push("null"); } - $[0] = maybeNullObject.value.inner; + $[0] = maybeNullObject; $[1] = y; } else { y = $[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-mutate-2.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-mutate-2.expect.md index 53deac41495c6..b31a16da90e3f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-mutate-2.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-mutate-2.expect.md @@ -37,7 +37,7 @@ function component(a, b) { } const y = t0; let z; - if ($[2] !== a || $[3] !== y.b) { + if ($[2] !== a || $[3] !== y) { z = { a }; const x = function () { z.a = 2; @@ -45,7 +45,7 @@ function component(a, b) { x(); $[2] = a; - $[3] = y.b; + $[3] = y; $[4] = z; } else { z = $[4]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conditional-break-labeled.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conditional-break-labeled.expect.md index 76648c251a101..3f795b604e382 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conditional-break-labeled.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conditional-break-labeled.expect.md @@ -33,9 +33,14 @@ import { c as _c } from "react/compiler-runtime"; /** * props.b *does* influence `a` */ function Component(props) { - const $ = _c(2); + const $ = _c(5); let a; - if ($[0] !== props) { + if ( + $[0] !== props.a || + $[1] !== props.b || + $[2] !== props.c || + $[3] !== props.d + ) { a = []; a.push(props.a); bb0: { @@ -47,10 +52,13 @@ function Component(props) { } a.push(props.d); - $[0] = props; - $[1] = a; + $[0] = props.a; + $[1] = props.b; + $[2] = props.c; + $[3] = props.d; + $[4] = a; } else { - a = $[1]; + a = $[4]; } return a; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conditional-early-return.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conditional-early-return.expect.md index 82537902bfa19..5e708b95c6fe9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conditional-early-return.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conditional-early-return.expect.md @@ -70,10 +70,10 @@ import { c as _c } from "react/compiler-runtime"; /** * props.b does *not* influence `a` */ function ComponentA(props) { - const $ = _c(3); + const $ = _c(5); let a_DEBUG; let t0; - if ($[0] !== props) { + if ($[0] !== props.a || $[1] !== props.b || $[2] !== props.d) { t0 = Symbol.for("react.early_return_sentinel"); bb0: { a_DEBUG = []; @@ -85,12 +85,14 @@ function ComponentA(props) { a_DEBUG.push(props.d); } - $[0] = props; - $[1] = a_DEBUG; - $[2] = t0; + $[0] = props.a; + $[1] = props.b; + $[2] = props.d; + $[3] = a_DEBUG; + $[4] = t0; } else { - a_DEBUG = $[1]; - t0 = $[2]; + a_DEBUG = $[3]; + t0 = $[4]; } if (t0 !== Symbol.for("react.early_return_sentinel")) { return t0; @@ -102,9 +104,14 @@ function ComponentA(props) { * props.b *does* influence `a` */ function ComponentB(props) { - const $ = _c(2); + const $ = _c(5); let a; - if ($[0] !== props) { + if ( + $[0] !== props.a || + $[1] !== props.b || + $[2] !== props.c || + $[3] !== props.d + ) { a = []; a.push(props.a); if (props.b) { @@ -112,10 +119,13 @@ function ComponentB(props) { } a.push(props.d); - $[0] = props; - $[1] = a; + $[0] = props.a; + $[1] = props.b; + $[2] = props.c; + $[3] = props.d; + $[4] = a; } else { - a = $[1]; + a = $[4]; } return a; } @@ -124,10 +134,15 @@ function ComponentB(props) { * props.b *does* influence `a`, but only in a way that is never observable */ function ComponentC(props) { - const $ = _c(3); + const $ = _c(6); let a; let t0; - if ($[0] !== props) { + if ( + $[0] !== props.a || + $[1] !== props.b || + $[2] !== props.c || + $[3] !== props.d + ) { t0 = Symbol.for("react.early_return_sentinel"); bb0: { a = []; @@ -140,12 +155,15 @@ function ComponentC(props) { a.push(props.d); } - $[0] = props; - $[1] = a; - $[2] = t0; + $[0] = props.a; + $[1] = props.b; + $[2] = props.c; + $[3] = props.d; + $[4] = a; + $[5] = t0; } else { - a = $[1]; - t0 = $[2]; + a = $[4]; + t0 = $[5]; } if (t0 !== Symbol.for("react.early_return_sentinel")) { return t0; @@ -157,10 +175,15 @@ function ComponentC(props) { * props.b *does* influence `a` */ function ComponentD(props) { - const $ = _c(3); + const $ = _c(6); let a; let t0; - if ($[0] !== props) { + if ( + $[0] !== props.a || + $[1] !== props.b || + $[2] !== props.c || + $[3] !== props.d + ) { t0 = Symbol.for("react.early_return_sentinel"); bb0: { a = []; @@ -173,12 +196,15 @@ function ComponentD(props) { a.push(props.d); } - $[0] = props; - $[1] = a; - $[2] = t0; + $[0] = props.a; + $[1] = props.b; + $[2] = props.c; + $[3] = props.d; + $[4] = a; + $[5] = t0; } else { - a = $[1]; - t0 = $[2]; + a = $[4]; + t0 = $[5]; } if (t0 !== Symbol.for("react.early_return_sentinel")) { return t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conditional-on-mutable.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conditional-on-mutable.expect.md index ad638cf28d871..fa8348c200972 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conditional-on-mutable.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conditional-on-mutable.expect.md @@ -36,9 +36,9 @@ function mayMutate() {} ```javascript import { c as _c } from "react/compiler-runtime"; function ComponentA(props) { - const $ = _c(2); + const $ = _c(4); let t0; - if ($[0] !== props) { + if ($[0] !== props.p0 || $[1] !== props.p1 || $[2] !== props.p2) { const a = []; const b = []; if (b) { @@ -49,18 +49,20 @@ function ComponentA(props) { } t0 = ; - $[0] = props; - $[1] = t0; + $[0] = props.p0; + $[1] = props.p1; + $[2] = props.p2; + $[3] = t0; } else { - t0 = $[1]; + t0 = $[3]; } return t0; } function ComponentB(props) { - const $ = _c(2); + const $ = _c(4); let t0; - if ($[0] !== props) { + if ($[0] !== props.p0 || $[1] !== props.p1 || $[2] !== props.p2) { const a = []; const b = []; if (mayMutate(b)) { @@ -71,10 +73,12 @@ function ComponentB(props) { } t0 = ; - $[0] = props; - $[1] = t0; + $[0] = props.p0; + $[1] = props.p1; + $[2] = props.p2; + $[3] = t0; } else { - t0 = $[1]; + t0 = $[3]; } return t0; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/early-return-nested-early-return-within-reactive-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/early-return-nested-early-return-within-reactive-scope.expect.md index 2d33981f73fd1..68b0122ea92dd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/early-return-nested-early-return-within-reactive-scope.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/early-return-nested-early-return-within-reactive-scope.expect.md @@ -31,9 +31,9 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(5); + const $ = _c(7); let t0; - if ($[0] !== props) { + if ($[0] !== props.a || $[1] !== props.b || $[2] !== props.cond) { t0 = Symbol.for("react.early_return_sentinel"); bb0: { const x = []; @@ -41,12 +41,12 @@ function Component(props) { x.push(props.a); if (props.b) { let t1; - if ($[2] !== props.b) { + if ($[4] !== props.b) { t1 = [props.b]; - $[2] = props.b; - $[3] = t1; + $[4] = props.b; + $[5] = t1; } else { - t1 = $[3]; + t1 = $[5]; } const y = t1; x.push(y); @@ -58,20 +58,22 @@ function Component(props) { break bb0; } else { let t1; - if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { t1 = foo(); - $[4] = t1; + $[6] = t1; } else { - t1 = $[4]; + t1 = $[6]; } t0 = t1; break bb0; } } - $[0] = props; - $[1] = t0; + $[0] = props.a; + $[1] = props.b; + $[2] = props.cond; + $[3] = t0; } else { - t0 = $[1]; + t0 = $[3]; } if (t0 !== Symbol.for("react.early_return_sentinel")) { return t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/early-return-within-reactive-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/early-return-within-reactive-scope.expect.md index 6c3525e9e77eb..31df829e0c203 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/early-return-within-reactive-scope.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/early-return-within-reactive-scope.expect.md @@ -45,9 +45,9 @@ import { c as _c } from "react/compiler-runtime"; import { makeArray } from "shared-runtime"; function Component(props) { - const $ = _c(4); + const $ = _c(6); let t0; - if ($[0] !== props) { + if ($[0] !== props.a || $[1] !== props.b || $[2] !== props.cond) { t0 = Symbol.for("react.early_return_sentinel"); bb0: { const x = []; @@ -57,21 +57,23 @@ function Component(props) { break bb0; } else { let t1; - if ($[2] !== props.b) { + if ($[4] !== props.b) { t1 = makeArray(props.b); - $[2] = props.b; - $[3] = t1; + $[4] = props.b; + $[5] = t1; } else { - t1 = $[3]; + t1 = $[5]; } t0 = t1; break bb0; } } - $[0] = props; - $[1] = t0; + $[0] = props.a; + $[1] = props.b; + $[2] = props.cond; + $[3] = t0; } else { - t0 = $[1]; + t0 = $[3]; } if (t0 !== Symbol.for("react.early_return_sentinel")) { return t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.expect.md new file mode 100644 index 0000000000000..d9c2b599998b7 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.expect.md @@ -0,0 +1,50 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies +import {ValidateMemoization} from 'shared-runtime'; +function Component(props) { + const data = useMemo(() => { + const x = []; + x.push(props?.items); + if (props.cond) { + x.push(props?.items); + } + return x; + }, [props?.items, props.cond]); + return ( + + ); +} + +``` + + +## Error + +``` + 2 | import {ValidateMemoization} from 'shared-runtime'; + 3 | function Component(props) { +> 4 | const data = useMemo(() => { + | ^^^^^^^ +> 5 | const x = []; + | ^^^^^^^^^^^^^^^^^ +> 6 | x.push(props?.items); + | ^^^^^^^^^^^^^^^^^ +> 7 | if (props.cond) { + | ^^^^^^^^^^^^^^^^^ +> 8 | x.push(props?.items); + | ^^^^^^^^^^^^^^^^^ +> 9 | } + | ^^^^^^^^^^^^^^^^^ +> 10 | return x; + | ^^^^^^^^^^^^^^^^^ +> 11 | }, [props?.items, props.cond]); + | ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (4:11) + 12 | return ( + 13 | + 14 | ); +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-with-conditional-optional.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-with-conditional-optional.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.expect.md new file mode 100644 index 0000000000000..57b7d48facbd5 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.expect.md @@ -0,0 +1,50 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies +import {ValidateMemoization} from 'shared-runtime'; +function Component(props) { + const data = useMemo(() => { + const x = []; + x.push(props?.items); + if (props.cond) { + x.push(props.items); + } + return x; + }, [props?.items, props.cond]); + return ( + + ); +} + +``` + + +## Error + +``` + 2 | import {ValidateMemoization} from 'shared-runtime'; + 3 | function Component(props) { +> 4 | const data = useMemo(() => { + | ^^^^^^^ +> 5 | const x = []; + | ^^^^^^^^^^^^^^^^^ +> 6 | x.push(props?.items); + | ^^^^^^^^^^^^^^^^^ +> 7 | if (props.cond) { + | ^^^^^^^^^^^^^^^^^ +> 8 | x.push(props.items); + | ^^^^^^^^^^^^^^^^^ +> 9 | } + | ^^^^^^^^^^^^^^^^^ +> 10 | return x; + | ^^^^^^^^^^^^^^^^^ +> 11 | }, [props?.items, props.cond]); + | ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (4:11) + 12 | return ( + 13 | + 14 | ); +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-with-conditional.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-with-conditional.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md index 75c5d61d407f0..8bf7f5bc71d0e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md @@ -25,7 +25,7 @@ export const FIXTURE_ENTRYPONT = { 1 | function useFoo(props: {value: {x: string; y: string} | null}) { 2 | const value = props.value; > 3 | return createArray(value?.x, value?.y)?.join(', '); - | ^^^^^^^^ Todo: Unexpected terminal kind `optional` for optional test block (3:3) + | ^^^^^^^^ Todo: Unexpected terminal kind `optional` for optional fallthrough block (3:3) 4 | } 5 | 6 | function createArray(...args: Array): Array { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr-conditional-access-2.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr-conditional-access-2.expect.md index 32498e1379206..5614560c6c4dc 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr-conditional-access-2.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr-conditional-access-2.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @enableTreatFunctionDepsAsConditional @enablePropagateDepsInHIR:false +// @enableTreatFunctionDepsAsConditional import {Stringify} from 'shared-runtime'; function Component({props}) { @@ -20,7 +20,7 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @enableTreatFunctionDepsAsConditional @enablePropagateDepsInHIR:false +import { c as _c } from "react/compiler-runtime"; // @enableTreatFunctionDepsAsConditional import { Stringify } from "shared-runtime"; function Component(t0) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr-conditional-access-2.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr-conditional-access-2.tsx index 2ede54db5f364..ab3e00f9ba2b1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr-conditional-access-2.tsx +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr-conditional-access-2.tsx @@ -1,4 +1,4 @@ -// @enableTreatFunctionDepsAsConditional @enablePropagateDepsInHIR:false +// @enableTreatFunctionDepsAsConditional import {Stringify} from 'shared-runtime'; function Component({props}) { diff --git "a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr\342\200\223conditional-access.expect.md" "b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr\342\200\223conditional-access.expect.md" index 4a62bf6f249bb..c7aa3e3b75446 100644 --- "a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr\342\200\223conditional-access.expect.md" +++ "b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr\342\200\223conditional-access.expect.md" @@ -2,7 +2,7 @@ ## Input ```javascript -// @enableTreatFunctionDepsAsConditional @enablePropagateDepsInHIR:false +// @enableTreatFunctionDepsAsConditional function Component(props) { function getLength() { return props.bar.length; @@ -21,15 +21,15 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @enableTreatFunctionDepsAsConditional @enablePropagateDepsInHIR:false +import { c as _c } from "react/compiler-runtime"; // @enableTreatFunctionDepsAsConditional function Component(props) { const $ = _c(5); let t0; - if ($[0] !== props) { + if ($[0] !== props.bar) { t0 = function getLength() { return props.bar.length; }; - $[0] = props; + $[0] = props.bar; $[1] = t0; } else { t0 = $[1]; diff --git "a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr\342\200\223conditional-access.js" "b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr\342\200\223conditional-access.js" index 9bff3e5cdb53b..6e59fb947d150 100644 --- "a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr\342\200\223conditional-access.js" +++ "b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/functionexpr\342\200\223conditional-access.js" @@ -1,4 +1,4 @@ -// @enableTreatFunctionDepsAsConditional @enablePropagateDepsInHIR:false +// @enableTreatFunctionDepsAsConditional function Component(props) { function getLength() { return props.bar.length; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/iife-return-modified-later-phi.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/iife-return-modified-later-phi.expect.md index bed1c329f0d10..22f967883b09a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/iife-return-modified-later-phi.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/iife-return-modified-later-phi.expect.md @@ -26,9 +26,9 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(2); + const $ = _c(3); let items; - if ($[0] !== props) { + if ($[0] !== props.a || $[1] !== props.cond) { let t0; if (props.cond) { t0 = []; @@ -38,10 +38,11 @@ function Component(props) { items = t0; items?.push(props.a); - $[0] = props; - $[1] = items; + $[0] = props.a; + $[1] = props.cond; + $[2] = items; } else { - items = $[1]; + items = $[2]; } return items; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-sequential-optional-chain-nonnull.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-sequential-optional-chain-nonnull.expect.md index 31e2cadf9f7db..f415c20528bfb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-sequential-optional-chain-nonnull.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-sequential-optional-chain-nonnull.expect.md @@ -33,11 +33,11 @@ function useFoo(t0) { const $ = _c(2); const { a } = t0; let x; - if ($[0] !== a.b.c.d) { + if ($[0] !== a.b.c.d.e) { x = []; x.push(a?.b.c?.d.e); x.push(a.b?.c.d?.e); - $[0] = a.b.c.d; + $[0] = a.b.c.d.e; $[1] = x; } else { x = $[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/nested-optional-chains.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/nested-optional-chains.expect.md index 0acf33b2ed87f..92a24194a359d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/nested-optional-chains.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/nested-optional-chains.expect.md @@ -120,29 +120,29 @@ function useFoo(t0) { } const x = t1; let t2; - if ($[2] !== prop2?.inner) { + if ($[2] !== prop2?.inner.value) { t2 = identity(prop2?.inner.value)?.toString(); - $[2] = prop2?.inner; + $[2] = prop2?.inner.value; $[3] = t2; } else { t2 = $[3]; } const y = t2; let t3; - if ($[4] !== prop3 || $[5] !== prop4) { + if ($[4] !== prop3 || $[5] !== prop4?.inner) { t3 = prop3?.fn(prop4?.inner.value).toString(); $[4] = prop3; - $[5] = prop4; + $[5] = prop4?.inner; $[6] = t3; } else { t3 = $[6]; } const z = t3; let t4; - if ($[7] !== prop5 || $[8] !== prop6) { + if ($[7] !== prop5 || $[8] !== prop6?.inner) { t4 = prop5?.fn(prop6?.inner.value)?.toString(); $[7] = prop5; - $[8] = prop6; + $[8] = prop6?.inner; $[9] = t4; } else { t4 = $[9]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-mutated-in-consequent-alternate-both-return.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-mutated-in-consequent-alternate-both-return.expect.md index 8a20f9186b447..b5534114c08a1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-mutated-in-consequent-alternate-both-return.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-mutated-in-consequent-alternate-both-return.expect.md @@ -29,9 +29,9 @@ import { c as _c } from "react/compiler-runtime"; import { makeObject_Primitives } from "shared-runtime"; function Component(props) { - const $ = _c(2); + const $ = _c(3); let t0; - if ($[0] !== props) { + if ($[0] !== props.cond || $[1] !== props.value) { t0 = Symbol.for("react.early_return_sentinel"); bb0: { const object = makeObject_Primitives(); @@ -45,10 +45,11 @@ function Component(props) { break bb0; } } - $[0] = props; - $[1] = t0; + $[0] = props.cond; + $[1] = props.value; + $[2] = t0; } else { - t0 = $[1]; + t0 = $[2]; } if (t0 !== Symbol.for("react.early_return_sentinel")) { return t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-with-conditional-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-with-conditional-optional.expect.md deleted file mode 100644 index 2674d78c997e7..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-with-conditional-optional.expect.md +++ /dev/null @@ -1,74 +0,0 @@ - -## Input - -```javascript -// @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies -import {ValidateMemoization} from 'shared-runtime'; -function Component(props) { - const data = useMemo(() => { - const x = []; - x.push(props?.items); - if (props.cond) { - x.push(props?.items); - } - return x; - }, [props?.items, props.cond]); - return ( - - ); -} - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies -import { ValidateMemoization } from "shared-runtime"; -function Component(props) { - const $ = _c(9); - - props?.items; - let t0; - let x; - if ($[0] !== props?.items || $[1] !== props.cond) { - x = []; - x.push(props?.items); - if (props.cond) { - x.push(props?.items); - } - $[0] = props?.items; - $[1] = props.cond; - $[2] = x; - } else { - x = $[2]; - } - t0 = x; - const data = t0; - - const t1 = props?.items; - let t2; - if ($[3] !== props.cond || $[4] !== t1) { - t2 = [t1, props.cond]; - $[3] = props.cond; - $[4] = t1; - $[5] = t2; - } else { - t2 = $[5]; - } - let t3; - if ($[6] !== data || $[7] !== t2) { - t3 = ; - $[6] = data; - $[7] = t2; - $[8] = t3; - } else { - t3 = $[8]; - } - return t3; -} - -``` - -### Eval output -(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-with-conditional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-with-conditional.expect.md deleted file mode 100644 index 1d4a50d2858ea..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-with-conditional.expect.md +++ /dev/null @@ -1,74 +0,0 @@ - -## Input - -```javascript -// @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies -import {ValidateMemoization} from 'shared-runtime'; -function Component(props) { - const data = useMemo(() => { - const x = []; - x.push(props?.items); - if (props.cond) { - x.push(props.items); - } - return x; - }, [props?.items, props.cond]); - return ( - - ); -} - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies -import { ValidateMemoization } from "shared-runtime"; -function Component(props) { - const $ = _c(9); - - props?.items; - let t0; - let x; - if ($[0] !== props?.items || $[1] !== props.cond) { - x = []; - x.push(props?.items); - if (props.cond) { - x.push(props.items); - } - $[0] = props?.items; - $[1] = props.cond; - $[2] = x; - } else { - x = $[2]; - } - t0 = x; - const data = t0; - - const t1 = props?.items; - let t2; - if ($[3] !== props.cond || $[4] !== t1) { - t2 = [t1, props.cond]; - $[3] = props.cond; - $[4] = t1; - $[5] = t2; - } else { - t2 = $[5]; - } - let t3; - if ($[6] !== data || $[7] !== t2) { - t3 = ; - $[6] = data; - $[7] = t2; - $[8] = t3; - } else { - t3 = $[8]; - } - return t3; -} - -``` - -### Eval output -(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/partial-early-return-within-reactive-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/partial-early-return-within-reactive-scope.expect.md index 42b3b21a20903..87c9485d99a4e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/partial-early-return-within-reactive-scope.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/partial-early-return-within-reactive-scope.expect.md @@ -30,10 +30,10 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(4); + const $ = _c(6); let t0; let y; - if ($[0] !== props) { + if ($[0] !== props.a || $[1] !== props.b || $[2] !== props.cond) { t0 = Symbol.for("react.early_return_sentinel"); bb0: { const x = []; @@ -43,11 +43,11 @@ function Component(props) { break bb0; } else { let t1; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { t1 = foo(); - $[3] = t1; + $[5] = t1; } else { - t1 = $[3]; + t1 = $[5]; } y = t1; if (props.b) { @@ -56,12 +56,14 @@ function Component(props) { } } } - $[0] = props; - $[1] = t0; - $[2] = y; + $[0] = props.a; + $[1] = props.b; + $[2] = props.cond; + $[3] = t0; + $[4] = y; } else { - t0 = $[1]; - y = $[2]; + t0 = $[3]; + y = $[4]; } if (t0 !== Symbol.for("react.early_return_sentinel")) { return t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push-consecutive-phis.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push-consecutive-phis.expect.md index f17bcc92cb7ce..16edbf2e23690 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push-consecutive-phis.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push-consecutive-phis.expect.md @@ -49,7 +49,7 @@ import { c as _c } from "react/compiler-runtime"; import { makeArray } from "shared-runtime"; function Component(props) { - const $ = _c(3); + const $ = _c(6); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = {}; @@ -59,7 +59,12 @@ function Component(props) { } const x = t0; let t1; - if ($[1] !== props) { + if ( + $[1] !== props.cond || + $[2] !== props.cond2 || + $[3] !== props.value || + $[4] !== props.value2 + ) { let y; if (props.cond) { if (props.cond2) { @@ -74,10 +79,13 @@ function Component(props) { y.push(x); t1 = [x, y]; - $[1] = props; - $[2] = t1; + $[1] = props.cond; + $[2] = props.cond2; + $[3] = props.value; + $[4] = props.value2; + $[5] = t1; } else { - t1 = $[2]; + t1 = $[5]; } return t1; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.expect.md index f58eed10fda5a..58e2c8f869a6a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.expect.md @@ -36,7 +36,7 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(3); + const $ = _c(4); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = {}; @@ -46,7 +46,7 @@ function Component(props) { } const x = t0; let t1; - if ($[1] !== props) { + if ($[1] !== props.cond || $[2] !== props.value) { let y; if (props.cond) { y = [props.value]; @@ -57,10 +57,11 @@ function Component(props) { y.push(x); t1 = [x, y]; - $[1] = props; - $[2] = t1; + $[1] = props.cond; + $[2] = props.value; + $[3] = t1; } else { - t1 = $[2]; + t1 = $[3]; } return t1; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-property-store.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-property-store.expect.md index 70551c8e9d7b0..9223c61200128 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-property-store.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-property-store.expect.md @@ -32,7 +32,7 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; // @debug function Component(props) { - const $ = _c(3); + const $ = _c(4); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = {}; @@ -42,7 +42,7 @@ function Component(props) { } const x = t0; let t1; - if ($[1] !== props) { + if ($[1] !== props.a || $[2] !== props.cond) { let y; if (props.cond) { y = {}; @@ -53,10 +53,11 @@ function Component(props) { y.x = x; t1 = [x, y]; - $[1] = props; - $[2] = t1; + $[1] = props.a; + $[2] = props.cond; + $[3] = t1; } else { - t1 = $[2]; + t1 = $[3]; } return t1; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-conditional-access-own-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-conditional-access-own-scope.expect.md new file mode 100644 index 0000000000000..8579b773e6218 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-conditional-access-own-scope.expect.md @@ -0,0 +1,50 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees +import {useCallback} from 'react'; + +function Component({propA, propB}) { + return useCallback(() => { + if (propA) { + return { + value: propB.x.y, + }; + } + }, [propA, propB.x.y]); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{propA: 1, propB: {x: {y: []}}}], +}; + +``` + + +## Error + +``` + 3 | + 4 | function Component({propA, propB}) { +> 5 | return useCallback(() => { + | ^^^^^^^ +> 6 | if (propA) { + | ^^^^^^^^^^^^^^^^ +> 7 | return { + | ^^^^^^^^^^^^^^^^ +> 8 | value: propB.x.y, + | ^^^^^^^^^^^^^^^^ +> 9 | }; + | ^^^^^^^^^^^^^^^^ +> 10 | } + | ^^^^^^^^^^^^^^^^ +> 11 | }, [propA, propB.x.y]); + | ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (5:11) + 12 | } + 13 | + 14 | export const FIXTURE_ENTRYPOINT = { +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-conditional-access-own-scope.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-conditional-access-own-scope.ts similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-conditional-access-own-scope.ts rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-conditional-access-own-scope.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-infer-conditional-value-block.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-infer-conditional-value-block.expect.md new file mode 100644 index 0000000000000..e77e79fd98e0e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-infer-conditional-value-block.expect.md @@ -0,0 +1,59 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees +import {useCallback} from 'react'; +import {identity, mutate} from 'shared-runtime'; + +function useHook(propA, propB) { + return useCallback(() => { + const x = {}; + if (identity(null) ?? propA.a) { + mutate(x); + return { + value: propB.x.y, + }; + } + }, [propA.a, propB.x.y]); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{a: 1}, {x: {y: 3}}], +}; + +``` + + +## Error + +``` + 4 | + 5 | function useHook(propA, propB) { +> 6 | return useCallback(() => { + | ^^^^^^^ +> 7 | const x = {}; + | ^^^^^^^^^^^^^^^^^ +> 8 | if (identity(null) ?? propA.a) { + | ^^^^^^^^^^^^^^^^^ +> 9 | mutate(x); + | ^^^^^^^^^^^^^^^^^ +> 10 | return { + | ^^^^^^^^^^^^^^^^^ +> 11 | value: propB.x.y, + | ^^^^^^^^^^^^^^^^^ +> 12 | }; + | ^^^^^^^^^^^^^^^^^ +> 13 | } + | ^^^^^^^^^^^^^^^^^ +> 14 | }, [propA.a, propB.x.y]); + | ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (6:14) + +CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (6:14) + 15 | } + 16 | + 17 | export const FIXTURE_ENTRYPOINT = { +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-infer-conditional-value-block.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-infer-conditional-value-block.ts similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-infer-conditional-value-block.ts rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-infer-conditional-value-block.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useCallback-infer-less-specific-conditional-access.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useCallback-infer-less-specific-conditional-access.expect.md index 940b3975c160d..955d391f912cf 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useCallback-infer-less-specific-conditional-access.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useCallback-infer-less-specific-conditional-access.expect.md @@ -44,6 +44,8 @@ function Component({propA, propB}) { | ^^^^^^^^^^^^^^^^^ > 14 | }, [propA?.a, propB.x.y]); | ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (6:14) + +CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (6:14) 15 | } 16 | ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-conditional-access-own-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-conditional-access-own-scope.expect.md deleted file mode 100644 index a90492f7a1774..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-conditional-access-own-scope.expect.md +++ /dev/null @@ -1,58 +0,0 @@ - -## Input - -```javascript -// @validatePreserveExistingMemoizationGuarantees -import {useCallback} from 'react'; - -function Component({propA, propB}) { - return useCallback(() => { - if (propA) { - return { - value: propB.x.y, - }; - } - }, [propA, propB.x.y]); -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{propA: 1, propB: {x: {y: []}}}], -}; - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees -import { useCallback } from "react"; - -function Component(t0) { - const $ = _c(3); - const { propA, propB } = t0; - let t1; - if ($[0] !== propA || $[1] !== propB.x.y) { - t1 = () => { - if (propA) { - return { value: propB.x.y }; - } - }; - $[0] = propA; - $[1] = propB.x.y; - $[2] = t1; - } else { - t1 = $[2]; - } - return t1; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{ propA: 1, propB: { x: { y: [] } } }], -}; - -``` - -### Eval output -(kind: ok) "[[ function params=0 ]]" \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-infer-conditional-value-block.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-infer-conditional-value-block.expect.md deleted file mode 100644 index d6c01643f5e31..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-infer-conditional-value-block.expect.md +++ /dev/null @@ -1,63 +0,0 @@ - -## Input - -```javascript -// @validatePreserveExistingMemoizationGuarantees -import {useCallback} from 'react'; -import {identity, mutate} from 'shared-runtime'; - -function useHook(propA, propB) { - return useCallback(() => { - const x = {}; - if (identity(null) ?? propA.a) { - mutate(x); - return { - value: propB.x.y, - }; - } - }, [propA.a, propB.x.y]); -} - -export const FIXTURE_ENTRYPOINT = { - fn: useHook, - params: [{a: 1}, {x: {y: 3}}], -}; - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees -import { useCallback } from "react"; -import { identity, mutate } from "shared-runtime"; - -function useHook(propA, propB) { - const $ = _c(3); - let t0; - if ($[0] !== propA.a || $[1] !== propB.x.y) { - t0 = () => { - const x = {}; - if (identity(null) ?? propA.a) { - mutate(x); - return { value: propB.x.y }; - } - }; - $[0] = propA.a; - $[1] = propB.x.y; - $[2] = t0; - } else { - t0 = $[2]; - } - return t0; -} - -export const FIXTURE_ENTRYPOINT = { - fn: useHook, - params: [{ a: 1 }, { x: { y: 3 } }], -}; - -``` - -### Eval output -(kind: ok) "[[ function params=0 ]]" \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-dependencies-non-optional-properties-inside-optional-chain.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-dependencies-non-optional-properties-inside-optional-chain.expect.md index 12a84b14f449a..896a547fec25c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-dependencies-non-optional-properties-inside-optional-chain.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-dependencies-non-optional-properties-inside-optional-chain.expect.md @@ -15,9 +15,9 @@ import { c as _c } from "react/compiler-runtime"; function Component(props) { const $ = _c(2); let t0; - if ($[0] !== props.post.feedback.comments) { + if ($[0] !== props.post.feedback.comments?.edges) { t0 = props.post.feedback.comments?.edges?.map(render); - $[0] = props.post.feedback.comments; + $[0] = props.post.feedback.comments?.edges; $[1] = t0; } else { t0 = $[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reassigned-phi-in-returned-function-expression.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reassigned-phi-in-returned-function-expression.expect.md index 5c6c680e05e91..39ce103cca071 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reassigned-phi-in-returned-function-expression.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reassigned-phi-in-returned-function-expression.expect.md @@ -23,7 +23,7 @@ import { c as _c } from "react/compiler-runtime"; function Component(props) { const $ = _c(2); let t0; - if ($[0] !== props.str) { + if ($[0] !== props) { t0 = () => { let str; if (arguments.length) { @@ -34,7 +34,7 @@ function Component(props) { global.log(str); }; - $[0] = props.str; + $[0] = props; $[1] = t0; } else { t0 = $[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/bug-infer-function-cond-access-not-hoisted.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/bug-infer-function-cond-access-not-hoisted.expect.md index 18e9faf63b5dc..f68c826507207 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/bug-infer-function-cond-access-not-hoisted.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/bug-infer-function-cond-access-not-hoisted.expect.md @@ -38,7 +38,7 @@ function Foo(t0) { const $ = _c(3); const { a, shouldReadA } = t0; let t1; - if ($[0] !== a.b.c || $[1] !== shouldReadA) { + if ($[0] !== a || $[1] !== shouldReadA) { t1 = ( { @@ -50,7 +50,7 @@ function Foo(t0) { shouldInvokeFns={true} /> ); - $[0] = a.b.c; + $[0] = a; $[1] = shouldReadA; $[2] = t1; } else { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/bug-merge-uncond-optional-chain-and-cond.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/bug-merge-uncond-optional-chain-and-cond.expect.md index 9a95e7dc875b4..fa265ae1f8d74 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/bug-merge-uncond-optional-chain-and-cond.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/bug-merge-uncond-optional-chain-and-cond.expect.md @@ -65,12 +65,12 @@ function useFoo(t0) { const $ = _c(2); const { screen } = t0; let t1; - if ($[0] !== screen?.title_text) { + if ($[0] !== screen) { t1 = screen?.title_text != null ? "(not null)" : identity({ title: screen.title_text }); - $[0] = screen?.title_text; + $[0] = screen; $[1] = t1; } else { t1 = $[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/join-uncond-scopes-cond-deps.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/join-uncond-scopes-cond-deps.expect.md index c54d0828ecefe..37d347cd9a0b4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/join-uncond-scopes-cond-deps.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/join-uncond-scopes-cond-deps.expect.md @@ -61,20 +61,13 @@ import { c as _c } from "react/compiler-runtime"; // This tests an optimization, import { CONST_TRUE, setProperty } from "shared-runtime"; function useJoinCondDepsInUncondScopes(props) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== props.a.b) { const y = {}; - let x; - if ($[2] !== props) { - x = {}; - if (CONST_TRUE) { - setProperty(x, props.a.b); - } - $[2] = props; - $[3] = x; - } else { - x = $[3]; + const x = {}; + if (CONST_TRUE) { + setProperty(x, props.a.b); } setProperty(y, props.a.b); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/promote-uncond.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/promote-uncond.expect.md index d3a61a1019c4b..83e050da29fc0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/promote-uncond.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/promote-uncond.expect.md @@ -34,19 +34,20 @@ import { identity } from "shared-runtime"; // and promote it to an unconditional dependency. function usePromoteUnconditionalAccessToDependency(props, other) { - const $ = _c(3); + const $ = _c(4); let x; - if ($[0] !== other || $[1] !== props.a) { + if ($[0] !== other || $[1] !== props.a.a.a || $[2] !== props.a.b) { x = {}; x.a = props.a.a.a; if (identity(other)) { x.c = props.a.b.c; } $[0] = other; - $[1] = props.a; - $[2] = x; + $[1] = props.a.a.a; + $[2] = props.a.b; + $[3] = x; } else { - x = $[2]; + x = $[3]; } return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-cascading-eliminated-phis.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-cascading-eliminated-phis.expect.md index 6af0cf0af7bca..c39b85e5ba636 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-cascading-eliminated-phis.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-cascading-eliminated-phis.expect.md @@ -36,10 +36,16 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(4); + const $ = _c(7); let x = 0; let values; - if ($[0] !== props || $[1] !== x) { + if ( + $[0] !== props.a || + $[1] !== props.b || + $[2] !== props.c || + $[3] !== props.d || + $[4] !== x + ) { values = []; const y = props.a || props.b; values.push(y); @@ -53,13 +59,16 @@ function Component(props) { } values.push(x); - $[0] = props; - $[1] = x; - $[2] = values; - $[3] = x; + $[0] = props.a; + $[1] = props.b; + $[2] = props.c; + $[3] = props.d; + $[4] = x; + $[5] = values; + $[6] = x; } else { - values = $[2]; - x = $[3]; + values = $[5]; + x = $[6]; } return values; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-leave-case.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-leave-case.expect.md index a10ad5fae4108..dd61d1fee1280 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-leave-case.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-leave-case.expect.md @@ -39,9 +39,9 @@ import { c as _c } from "react/compiler-runtime"; import { Stringify } from "shared-runtime"; function Component(props) { - const $ = _c(2); + const $ = _c(3); let t0; - if ($[0] !== props) { + if ($[0] !== props.p0 || $[1] !== props.p1) { const x = []; let y; if (props.p0) { @@ -55,10 +55,11 @@ function Component(props) { {y} ); - $[0] = props; - $[1] = t0; + $[0] = props.p0; + $[1] = props.p1; + $[2] = t0; } else { - t0 = $[1]; + t0 = $[2]; } return t0; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction-with-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction-with-mutation.expect.md index 3e7fd4bf5f859..c6c7489a4e66d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction-with-mutation.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction-with-mutation.expect.md @@ -31,17 +31,19 @@ import { c as _c } from "react/compiler-runtime"; import { mutate } from "shared-runtime"; function useFoo(props) { - const $ = _c(2); + const $ = _c(4); let x; - if ($[0] !== props) { + if ($[0] !== props.bar || $[1] !== props.cond || $[2] !== props.foo) { x = []; x.push(props.bar); props.cond ? (([x] = [[]]), x.push(props.foo)) : null; mutate(x); - $[0] = props; - $[1] = x; + $[0] = props.bar; + $[1] = props.cond; + $[2] = props.foo; + $[3] = x; } else { - x = $[1]; + x = $[3]; } return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction.expect.md index 9b3aad524ce14..693b94d886ce2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction.expect.md @@ -26,7 +26,7 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function useFoo(props) { - const $ = _c(4); + const $ = _c(5); let x; if ($[0] !== props.bar) { x = []; @@ -36,12 +36,13 @@ function useFoo(props) { } else { x = $[1]; } - if ($[2] !== props) { + if ($[2] !== props.cond || $[3] !== props.foo) { props.cond ? (([x] = [[]]), x.push(props.foo)) : null; - $[2] = props; - $[3] = x; + $[2] = props.cond; + $[3] = props.foo; + $[4] = x; } else { - x = $[3]; + x = $[4]; } return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary-with-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary-with-mutation.expect.md index de9466c4daa9d..283e55630b898 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary-with-mutation.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary-with-mutation.expect.md @@ -31,17 +31,19 @@ import { c as _c } from "react/compiler-runtime"; import { mutate } from "shared-runtime"; function useFoo(props) { - const $ = _c(2); + const $ = _c(4); let x; - if ($[0] !== props) { + if ($[0] !== props.bar || $[1] !== props.cond || $[2] !== props.foo) { x = []; x.push(props.bar); props.cond ? ((x = []), x.push(props.foo)) : null; mutate(x); - $[0] = props; - $[1] = x; + $[0] = props.bar; + $[1] = props.cond; + $[2] = props.foo; + $[3] = x; } else { - x = $[1]; + x = $[3]; } return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary.expect.md index e199863257bfe..97cfa052aff98 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-ternary.expect.md @@ -26,7 +26,7 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function useFoo(props) { - const $ = _c(4); + const $ = _c(5); let x; if ($[0] !== props.bar) { x = []; @@ -36,12 +36,13 @@ function useFoo(props) { } else { x = $[1]; } - if ($[2] !== props) { + if ($[2] !== props.cond || $[3] !== props.foo) { props.cond ? ((x = []), x.push(props.foo)) : null; - $[2] = props; - $[3] = x; + $[2] = props.cond; + $[3] = props.foo; + $[4] = x; } else { - x = $[3]; + x = $[4]; } return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary-with-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary-with-mutation.expect.md index 16981f69cda90..1c4b48cb7c40f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary-with-mutation.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary-with-mutation.expect.md @@ -31,17 +31,19 @@ export const FIXTURE_ENTRYPOINT = { import { c as _c } from "react/compiler-runtime"; import { arrayPush } from "shared-runtime"; function useFoo(props) { - const $ = _c(2); + const $ = _c(4); let x; - if ($[0] !== props) { + if ($[0] !== props.bar || $[1] !== props.cond || $[2] !== props.foo) { x = []; x.push(props.bar); props.cond ? ((x = []), x.push(props.foo)) : ((x = []), x.push(props.bar)); arrayPush(x, 4); - $[0] = props; - $[1] = x; + $[0] = props.bar; + $[1] = props.cond; + $[2] = props.foo; + $[3] = x; } else { - x = $[1]; + x = $[3]; } return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary.expect.md index 99b50ac231342..58723f9fa0b91 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary.expect.md @@ -28,7 +28,7 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function useFoo(props) { - const $ = _c(4); + const $ = _c(6); let x; if ($[0] !== props.bar) { x = []; @@ -38,12 +38,14 @@ function useFoo(props) { } else { x = $[1]; } - if ($[2] !== props) { + if ($[2] !== props.bar || $[3] !== props.cond || $[4] !== props.foo) { props.cond ? ((x = []), x.push(props.foo)) : ((x = []), x.push(props.bar)); - $[2] = props; - $[3] = x; + $[2] = props.bar; + $[3] = props.cond; + $[4] = props.foo; + $[5] = x; } else { - x = $[3]; + x = $[5]; } return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-with-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-with-mutation.expect.md index f4689e5795552..9f1e21d7c7497 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-with-mutation.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-with-mutation.expect.md @@ -39,9 +39,9 @@ import { c as _c } from "react/compiler-runtime"; import { mutate } from "shared-runtime"; function useFoo(props) { - const $ = _c(2); + const $ = _c(4); let x; - if ($[0] !== props) { + if ($[0] !== props.bar || $[1] !== props.cond || $[2] !== props.foo) { x = []; x.push(props.bar); if (props.cond) { @@ -53,10 +53,12 @@ function useFoo(props) { } mutate(x); - $[0] = props; - $[1] = x; + $[0] = props.bar; + $[1] = props.cond; + $[2] = props.foo; + $[3] = x; } else { - x = $[1]; + x = $[3]; } return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-via-destructuring-with-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-via-destructuring-with-mutation.expect.md index ed1056c47c0f8..81cc777522bca 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-via-destructuring-with-mutation.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-via-destructuring-with-mutation.expect.md @@ -35,9 +35,9 @@ import { c as _c } from "react/compiler-runtime"; import { mutate } from "shared-runtime"; function useFoo(props) { - const $ = _c(2); + const $ = _c(4); let x; - if ($[0] !== props) { + if ($[0] !== props.bar || $[1] !== props.cond || $[2] !== props.foo) { ({ x } = { x: [] }); x.push(props.bar); if (props.cond) { @@ -46,10 +46,12 @@ function useFoo(props) { } mutate(x); - $[0] = props; - $[1] = x; + $[0] = props.bar; + $[1] = props.cond; + $[2] = props.foo; + $[3] = x; } else { - x = $[1]; + x = $[3]; } return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-with-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-with-mutation.expect.md index 26cd73a82b53f..f48cec2c238ba 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-with-mutation.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssa-renaming-with-mutation.expect.md @@ -35,9 +35,9 @@ import { c as _c } from "react/compiler-runtime"; import { mutate } from "shared-runtime"; function useFoo(props) { - const $ = _c(2); + const $ = _c(4); let x; - if ($[0] !== props) { + if ($[0] !== props.bar || $[1] !== props.cond || $[2] !== props.foo) { x = []; x.push(props.bar); if (props.cond) { @@ -46,10 +46,12 @@ function useFoo(props) { } mutate(x); - $[0] = props; - $[1] = x; + $[0] = props.bar; + $[1] = props.cond; + $[2] = props.foo; + $[3] = x; } else { - x = $[1]; + x = $[3]; } return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/switch-non-final-default.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/switch-non-final-default.expect.md index 788109636b680..804acfce19b48 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/switch-non-final-default.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/switch-non-final-default.expect.md @@ -33,10 +33,10 @@ function Component(props) { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(7); + const $ = _c(8); let t0; let y; - if ($[0] !== props) { + if ($[0] !== props.p0 || $[1] !== props.p2) { const x = []; bb0: switch (props.p0) { case 1: { @@ -45,11 +45,11 @@ function Component(props) { case true: { x.push(props.p2); let t1; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { t1 = []; - $[3] = t1; + $[4] = t1; } else { - t1 = $[3]; + t1 = $[4]; } y = t1; } @@ -62,23 +62,24 @@ function Component(props) { } t0 = ; - $[0] = props; - $[1] = t0; - $[2] = y; + $[0] = props.p0; + $[1] = props.p2; + $[2] = t0; + $[3] = y; } else { - t0 = $[1]; - y = $[2]; + t0 = $[2]; + y = $[3]; } const child = t0; y.push(props.p4); let t1; - if ($[4] !== child || $[5] !== y) { + if ($[5] !== child || $[6] !== y) { t1 = {child}; - $[4] = child; - $[5] = y; - $[6] = t1; + $[5] = child; + $[6] = y; + $[7] = t1; } else { - t1 = $[6]; + t1 = $[7]; } return t1; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/switch.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/switch.expect.md index 2628982655deb..33a7ae3193053 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/switch.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/switch.expect.md @@ -28,10 +28,10 @@ function Component(props) { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(6); + const $ = _c(8); let t0; let y; - if ($[0] !== props) { + if ($[0] !== props.p0 || $[1] !== props.p2 || $[2] !== props.p3) { const x = []; switch (props.p0) { case true: { @@ -44,23 +44,25 @@ function Component(props) { } t0 = ; - $[0] = props; - $[1] = t0; - $[2] = y; + $[0] = props.p0; + $[1] = props.p2; + $[2] = props.p3; + $[3] = t0; + $[4] = y; } else { - t0 = $[1]; - y = $[2]; + t0 = $[3]; + y = $[4]; } const child = t0; y.push(props.p4); let t1; - if ($[3] !== child || $[4] !== y) { + if ($[5] !== child || $[6] !== y) { t1 = {child}; - $[3] = child; - $[4] = y; - $[5] = t1; + $[5] = child; + $[6] = y; + $[7] = t1; } else { - t1 = $[5]; + t1 = $[7]; } return t1; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-mutate-outer-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-mutate-outer-value.expect.md index 856d1326407ab..cab72226d27ef 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-mutate-outer-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-mutate-outer-value.expect.md @@ -28,9 +28,9 @@ import { c as _c } from "react/compiler-runtime"; const { shallowCopy, throwErrorWithMessage } = require("shared-runtime"); function Component(props) { - const $ = _c(3); + const $ = _c(5); let x; - if ($[0] !== props.a) { + if ($[0] !== props) { x = []; try { let t0; @@ -42,9 +42,17 @@ function Component(props) { } x.push(t0); } catch { - x.push(shallowCopy({ a: props.a })); + let t0; + if ($[3] !== props.a) { + t0 = shallowCopy({ a: props.a }); + $[3] = props.a; + $[4] = t0; + } else { + t0 = $[4]; + } + x.push(t0); } - $[0] = props.a; + $[0] = props; $[1] = x; } else { x = $[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-function-expression-returns-caught-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-function-expression-returns-caught-value.expect.md index f2e46a6aff315..db8877f061bc1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-function-expression-returns-caught-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-function-expression-returns-caught-value.expect.md @@ -31,7 +31,7 @@ import { throwInput } from "shared-runtime"; function Component(props) { const $ = _c(4); let t0; - if ($[0] !== props.value) { + if ($[0] !== props) { t0 = () => { try { throwInput([props.value]); @@ -40,7 +40,7 @@ function Component(props) { return e; } }; - $[0] = props.value; + $[0] = props; $[1] = t0; } else { t0 = $[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-object-method-returns-caught-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-object-method-returns-caught-value.expect.md index 83f97ff6cbfbc..b760716a0c72f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-object-method-returns-caught-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-object-method-returns-caught-value.expect.md @@ -33,7 +33,7 @@ import { throwInput } from "shared-runtime"; function Component(props) { const $ = _c(2); let t0; - if ($[0] !== props.value) { + if ($[0] !== props) { const object = { foo() { try { @@ -46,7 +46,7 @@ function Component(props) { }; t0 = object.foo(); - $[0] = props.value; + $[0] = props; $[1] = t0; } else { t0 = $[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useMemo-multiple-if-else.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useMemo-multiple-if-else.expect.md index 05e465000d6cd..23b05b548221f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useMemo-multiple-if-else.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useMemo-multiple-if-else.expect.md @@ -33,11 +33,16 @@ import { c as _c } from "react/compiler-runtime"; import { useMemo } from "react"; function Component(props) { - const $ = _c(3); + const $ = _c(6); let t0; bb0: { let y; - if ($[0] !== props) { + if ( + $[0] !== props.a || + $[1] !== props.b || + $[2] !== props.cond || + $[3] !== props.cond2 + ) { y = []; if (props.cond) { y.push(props.a); @@ -48,12 +53,15 @@ function Component(props) { } y.push(props.b); - $[0] = props; - $[1] = y; - $[2] = t0; + $[0] = props.a; + $[1] = props.b; + $[2] = props.cond; + $[3] = props.cond2; + $[4] = y; + $[5] = t0; } else { - y = $[1]; - t0 = $[2]; + y = $[4]; + t0 = $[5]; } t0 = y; }