Home / File/ PruneAlwaysInvalidatingScopes.ts — react Source File

PruneAlwaysInvalidatingScopes.ts — react Source File

Architecture documentation for PruneAlwaysInvalidatingScopes.ts, a typescript file in the react codebase. 2 imports, 1 dependents.

File typescript BabelCompiler Validation 2 imports 1 dependents 1 functions 1 classes

Entity Profile

Dependency Diagram

graph LR
  fec8c712_326d_ff8d_0b77_541838e68d7b["PruneAlwaysInvalidatingScopes.ts"]
  0423f759_97e0_9101_4634_ed555abc5ca9["index.ts"]
  fec8c712_326d_ff8d_0b77_541838e68d7b --> 0423f759_97e0_9101_4634_ed555abc5ca9
  9dc3e5f8_0649_d981_e520_38c0ab672d18["."]
  fec8c712_326d_ff8d_0b77_541838e68d7b --> 9dc3e5f8_0649_d981_e520_38c0ab672d18
  e3cfc07a_10c8_5dcd_e270_e8e14c29309b["Pipeline.ts"]
  e3cfc07a_10c8_5dcd_e270_e8e14c29309b --> fec8c712_326d_ff8d_0b77_541838e68d7b
  style fec8c712_326d_ff8d_0b77_541838e68d7b fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

/**
 * 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 {ReactiveFunctionTransform, Transformed, visitReactiveFunction} from '.';
import {
  Identifier,
  ReactiveFunction,
  ReactiveInstruction,
  ReactiveScopeBlock,
  ReactiveStatement,
} from '../HIR';

/**
 * Some instructions will *always* produce a new value, and unless memoized will *always*
 * invalidate downstream reactive scopes. This pass finds such values and prunes downstream
 * memoization.
 *
 * NOTE: function calls are an edge-case: function calls *may* return primitives, so this
 * pass optimistically assumes they do. Therefore, unmemoized function calls will *not*
 * prune downstream memoization. Only guaranteed new allocations, such as object and array
 * literals, will cause pruning.
 */
export function pruneAlwaysInvalidatingScopes(fn: ReactiveFunction): void {
  visitReactiveFunction(fn, new Transform(), false);
}

class Transform extends ReactiveFunctionTransform<boolean> {
  alwaysInvalidatingValues: Set<Identifier> = new Set();
  unmemoizedValues: Set<Identifier> = new Set();

  override transformInstruction(
    instruction: ReactiveInstruction,
    withinScope: boolean,
  ): Transformed<ReactiveStatement> {
    this.visitInstruction(instruction, withinScope);

    const {lvalue, value} = instruction;
    switch (value.kind) {
      case 'ArrayExpression':
      case 'ObjectExpression':
      case 'JsxExpression':
      case 'JsxFragment':
      case 'NewExpression': {
        if (lvalue !== null) {
          this.alwaysInvalidatingValues.add(lvalue.identifier);
          if (!withinScope) {
            this.unmemoizedValues.add(lvalue.identifier);
          }
        }
        break;
      }
      case 'StoreLocal': {
        if (this.alwaysInvalidatingValues.has(value.value.identifier)) {
          this.alwaysInvalidatingValues.add(value.lvalue.place.identifier);
        }
        if (this.unmemoizedValues.has(value.value.identifier)) {
          this.unmemoizedValues.add(value.lvalue.place.identifier);
        }
        break;
      }
      case 'LoadLocal': {
        if (
          lvalue !== null &&
          this.alwaysInvalidatingValues.has(value.place.identifier)
        ) {
          this.alwaysInvalidatingValues.add(lvalue.identifier);
        }
        if (
          lvalue !== null &&
          this.unmemoizedValues.has(value.place.identifier)
        ) {
          this.unmemoizedValues.add(lvalue.identifier);
        }
        break;
      }
    }
    return {kind: 'keep'};
  }

  override transformScope(
    scopeBlock: ReactiveScopeBlock,
    _withinScope: boolean,
  ): Transformed<ReactiveStatement> {
    this.visitScope(scopeBlock, true);

    for (const dep of scopeBlock.scope.dependencies) {
      if (this.unmemoizedValues.has(dep.identifier)) {
        /*
         * This scope depends on an always-invalidating value so the scope will always invalidate:
         * prune it to avoid wasted comparisons
         */
        for (const [_, decl] of scopeBlock.scope.declarations) {
          if (this.alwaysInvalidatingValues.has(decl.identifier)) {
            this.unmemoizedValues.add(decl.identifier);
          }
        }
        for (const identifier of scopeBlock.scope.reassignments) {
          if (this.alwaysInvalidatingValues.has(identifier)) {
            this.unmemoizedValues.add(identifier);
          }
        }
        return {
          kind: 'replace',
          value: {
            kind: 'pruned-scope',
            scope: scopeBlock.scope,
            instructions: scopeBlock.instructions,
          },
        };
      }
    }
    return {kind: 'keep'};
  }
}

Domain

Subdomains

Classes

Dependencies

Frequently Asked Questions

What does PruneAlwaysInvalidatingScopes.ts do?
PruneAlwaysInvalidatingScopes.ts is a source file in the react codebase, written in typescript. It belongs to the BabelCompiler domain, Validation subdomain.
What functions are defined in PruneAlwaysInvalidatingScopes.ts?
PruneAlwaysInvalidatingScopes.ts defines 1 function(s): pruneAlwaysInvalidatingScopes.
What does PruneAlwaysInvalidatingScopes.ts depend on?
PruneAlwaysInvalidatingScopes.ts imports 2 module(s): ., index.ts.
What files import PruneAlwaysInvalidatingScopes.ts?
PruneAlwaysInvalidatingScopes.ts is imported by 1 file(s): Pipeline.ts.
Where is PruneAlwaysInvalidatingScopes.ts in the architecture?
PruneAlwaysInvalidatingScopes.ts is located at compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneAlwaysInvalidatingScopes.ts (domain: BabelCompiler, subdomain: Validation, directory: compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes).

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free