compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts TYPESCRIPT 1,997 lines View on github.com → Search inside
1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 */78import {BindingKind} from '@babel/traverse';9import * as t from '@babel/types';10import {11  CompilerDiagnostic,12  CompilerError,13  ErrorCategory,14} from '../CompilerError';15import {assertExhaustive} from '../Utils/utils';16import {Environment, ReactFunctionType} from './Environment';17import type {HookKind} from './ObjectShape';18import {Type, makeType} from './Types';19import {z} from 'zod/v4';20import type {AliasingEffect} from '../Inference/AliasingEffects';21import {isReservedWord} from '../Utils/Keyword';22import {Err, Ok, Result} from '../Utils/Result';2324/*25 * *******************************************************************************************26 * *******************************************************************************************27 * ************************************* Core Data Model *************************************28 * *******************************************************************************************29 * *******************************************************************************************30 */3132// AST -> (lowering) -> HIR -> (analysis) -> Reactive Scopes -> (codegen) -> AST3334/*35 * A location in a source file, intended to be used for providing diagnostic information and36 * transforming code while preserving source information (ie to emit source maps).37 *38 * `GeneratedSource` indicates that there is no single source location from which the code derives.39 */40export const GeneratedSource = Symbol();41export type SourceLocation = t.SourceLocation | typeof GeneratedSource;4243/*44 * A React function defines a computation that takes some set of reactive inputs45 * (props, hook arguments) and return a result (JSX, hook return value). Unlike46 * HIR, the data model is tree-shaped:47 *48 * ReactFunction49 *    ReactiveBlock50 *      ReactiveBlockScope*51 *       Place* (dependencies)52 *       (ReactiveInstruction | ReactiveTerminal)*53 *54 * Where ReactiveTerminal may recursively contain zero or more ReactiveBlocks.55 *56 * Each ReactiveBlockScope describes a set of dependencies as well as the instructions (and terminals)57 * within that scope.58 */59export type ReactiveFunction = {60  loc: SourceLocation;61  id: ValidIdentifierName | null;62  nameHint: string | null;63  params: Array<Place | SpreadPattern>;64  generator: boolean;65  async: boolean;66  body: ReactiveBlock;67  env: Environment;68  directives: Array<string>;69};7071export type ReactiveScopeBlock = {72  kind: 'scope';73  scope: ReactiveScope;74  instructions: ReactiveBlock;75};7677export type PrunedReactiveScopeBlock = {78  kind: 'pruned-scope';79  scope: ReactiveScope;80  instructions: ReactiveBlock;81};8283export type ReactiveBlock = Array<ReactiveStatement>;8485export type ReactiveStatement =86  | ReactiveInstructionStatement87  | ReactiveTerminalStatement88  | ReactiveScopeBlock89  | PrunedReactiveScopeBlock;9091export type ReactiveInstructionStatement = {92  kind: 'instruction';93  instruction: ReactiveInstruction;94};9596export type ReactiveTerminalStatement<97  Tterminal extends ReactiveTerminal = ReactiveTerminal,98> = {99  kind: 'terminal';100  terminal: Tterminal;101  label: {102    id: BlockId;103    implicit: boolean;104  } | null;105};106107export type ReactiveInstruction = {108  id: InstructionId;109  lvalue: Place | null;110  value: ReactiveValue;111  effects?: Array<AliasingEffect> | null; // TODO make non-optional112  loc: SourceLocation;113};114115export type ReactiveValue =116  | InstructionValue117  | ReactiveLogicalValue118  | ReactiveSequenceValue119  | ReactiveTernaryValue120  | ReactiveOptionalCallValue;121122export type ReactiveLogicalValue = {123  kind: 'LogicalExpression';124  operator: t.LogicalExpression['operator'];125  left: ReactiveValue;126  right: ReactiveValue;127  loc: SourceLocation;128};129130export type ReactiveTernaryValue = {131  kind: 'ConditionalExpression';132  test: ReactiveValue;133  consequent: ReactiveValue;134  alternate: ReactiveValue;135  loc: SourceLocation;136};137138export type ReactiveSequenceValue = {139  kind: 'SequenceExpression';140  instructions: Array<ReactiveInstruction>;141  id: InstructionId;142  value: ReactiveValue;143  loc: SourceLocation;144};145146export type ReactiveOptionalCallValue = {147  kind: 'OptionalExpression';148  id: InstructionId;149  value: ReactiveValue;150  optional: boolean;151  loc: SourceLocation;152};153154export type ReactiveTerminal =155  | ReactiveBreakTerminal156  | ReactiveContinueTerminal157  | ReactiveReturnTerminal158  | ReactiveThrowTerminal159  | ReactiveSwitchTerminal160  | ReactiveDoWhileTerminal161  | ReactiveWhileTerminal162  | ReactiveForTerminal163  | ReactiveForOfTerminal164  | ReactiveForInTerminal165  | ReactiveIfTerminal166  | ReactiveLabelTerminal167  | ReactiveTryTerminal;168169function _staticInvariantReactiveTerminalHasLocation(170  terminal: ReactiveTerminal,171): SourceLocation {172  // If this fails, it is because a variant of ReactiveTerminal is missing a .loc - add it!173  return terminal.loc;174}175176function _staticInvariantReactiveTerminalHasInstructionId(177  terminal: ReactiveTerminal,178): InstructionId {179  // If this fails, it is because a variant of ReactiveTerminal is missing a .id - add it!180  return terminal.id;181}182183export type ReactiveTerminalTargetKind = 'implicit' | 'labeled' | 'unlabeled';184export type ReactiveBreakTerminal = {185  kind: 'break';186  target: BlockId;187  id: InstructionId;188  targetKind: ReactiveTerminalTargetKind;189  loc: SourceLocation;190};191export type ReactiveContinueTerminal = {192  kind: 'continue';193  target: BlockId;194  id: InstructionId;195  targetKind: ReactiveTerminalTargetKind;196  loc: SourceLocation;197};198export type ReactiveReturnTerminal = {199  kind: 'return';200  value: Place;201  id: InstructionId;202  loc: SourceLocation;203};204export type ReactiveThrowTerminal = {205  kind: 'throw';206  value: Place;207  id: InstructionId;208  loc: SourceLocation;209};210export type ReactiveSwitchTerminal = {211  kind: 'switch';212  test: Place;213  cases: Array<{214    test: Place | null;215    block: ReactiveBlock | void;216  }>;217  id: InstructionId;218  loc: SourceLocation;219};220export type ReactiveDoWhileTerminal = {221  kind: 'do-while';222  loop: ReactiveBlock;223  test: ReactiveValue;224  id: InstructionId;225  loc: SourceLocation;226};227export type ReactiveWhileTerminal = {228  kind: 'while';229  test: ReactiveValue;230  loop: ReactiveBlock;231  id: InstructionId;232  loc: SourceLocation;233};234export type ReactiveForTerminal = {235  kind: 'for';236  init: ReactiveValue;237  test: ReactiveValue;238  update: ReactiveValue | null;239  loop: ReactiveBlock;240  id: InstructionId;241  loc: SourceLocation;242};243export type ReactiveForOfTerminal = {244  kind: 'for-of';245  init: ReactiveValue;246  test: ReactiveValue;247  loop: ReactiveBlock;248  id: InstructionId;249  loc: SourceLocation;250};251export type ReactiveForInTerminal = {252  kind: 'for-in';253  init: ReactiveValue;254  loop: ReactiveBlock;255  id: InstructionId;256  loc: SourceLocation;257};258export type ReactiveIfTerminal = {259  kind: 'if';260  test: Place;261  consequent: ReactiveBlock;262  alternate: ReactiveBlock | null;263  id: InstructionId;264  loc: SourceLocation;265};266export type ReactiveLabelTerminal = {267  kind: 'label';268  block: ReactiveBlock;269  id: InstructionId;270  loc: SourceLocation;271};272export type ReactiveTryTerminal = {273  kind: 'try';274  block: ReactiveBlock;275  handlerBinding: Place | null;276  handler: ReactiveBlock;277  id: InstructionId;278  loc: SourceLocation;279};280281// A function lowered to HIR form, ie where its body is lowered to an HIR control-flow graph282export type HIRFunction = {283  loc: SourceLocation;284  id: ValidIdentifierName | null;285  nameHint: string | null;286  fnType: ReactFunctionType;287  env: Environment;288  params: Array<Place | SpreadPattern>;289  returnTypeAnnotation: t.FlowType | t.TSType | null;290  returns: Place;291  context: Array<Place>;292  body: HIR;293  generator: boolean;294  async: boolean;295  directives: Array<string>;296  aliasingEffects: Array<AliasingEffect> | null;297};298299/*300 * Each reactive scope may have its own control-flow, so the instructions form301 * a control-flow graph. The graph comprises a set of basic blocks which reference302 * each other via terminal statements, as well as a reference to the entry block.303 */304export type HIR = {305  entry: BlockId;306307  /*308   * Basic blocks are stored as a map to aid certain operations that need to309   * lookup blocks by their id. However, the order of the items in the map is310   * reverse postorder, that is, barring cycles, predecessors appear before311   * successors. This is designed to facilitate forward data flow analysis.312   */313  blocks: Map<BlockId, BasicBlock>;314};315316/*317 * Each basic block within an instruction graph contains zero or more instructions318 * followed by a terminal node. Note that basic blocks always execute consecutively,319 * there can be no branching within a block other than for an exception. Exceptions320 * can occur pervasively and React runtime is responsible for resetting state when321 * an exception occurs, therefore the block model only represents explicit throw322 * statements and not implicit exceptions which may occur.323 */324export type BlockKind = 'block' | 'value' | 'loop' | 'sequence' | 'catch';325326/**327 * Returns true for "block" and "catch" block kinds which correspond to statements328 * in the source, including BlockStatement, CatchStatement.329 *330 * Inverse of isExpressionBlockKind()331 */332export function isStatementBlockKind(kind: BlockKind): boolean {333  return kind === 'block' || kind === 'catch';334}335336/**337 * Returns true for "value", "loop", and "sequence" block kinds which correspond to338 * expressions in the source, such as ConditionalExpression, LogicalExpression, loop339 * initializer/test/updaters, etc340 *341 * Inverse of isStatementBlockKind()342 */343export function isExpressionBlockKind(kind: BlockKind): boolean {344  return !isStatementBlockKind(kind);345}346347export type BasicBlock = {348  kind: BlockKind;349  id: BlockId;350  instructions: Array<Instruction>;351  terminal: Terminal;352  preds: Set<BlockId>;353  phis: Set<Phi>;354};355export type TBasicBlock<T extends Terminal> = BasicBlock & {terminal: T};356357/*358 * Terminal nodes generally represent statements that affect control flow, such as359 * for-of, if-else, return, etc.360 */361export type Terminal =362  | UnsupportedTerminal363  | UnreachableTerminal364  | ThrowTerminal365  | ReturnTerminal366  | GotoTerminal367  | IfTerminal368  | BranchTerminal369  | SwitchTerminal370  | ForTerminal371  | ForOfTerminal372  | ForInTerminal373  | DoWhileTerminal374  | WhileTerminal375  | LogicalTerminal376  | TernaryTerminal377  | OptionalTerminal378  | LabelTerminal379  | SequenceTerminal380  | MaybeThrowTerminal381  | TryTerminal382  | ReactiveScopeTerminal383  | PrunedScopeTerminal;384385export type TerminalWithFallthrough = Terminal & {fallthrough: BlockId};386387function _staticInvariantTerminalHasLocation(388  terminal: Terminal,389): SourceLocation {390  // If this fails, it is because a variant of Terminal is missing a .loc - add it!391  return terminal.loc;392}393394function _staticInvariantTerminalHasInstructionId(395  terminal: Terminal,396): InstructionId {397  // If this fails, it is because a variant of Terminal is missing a .id - add it!398  return terminal.id;399}400401function _staticInvariantTerminalHasFallthrough(402  terminal: Terminal,403): BlockId | never | undefined {404  // If this fails, it is because a variant of Terminal is missing a fallthrough annotation405  return terminal.fallthrough;406}407408/*409 * Terminal nodes allowed for a value block410 * A terminal that couldn't be lowered correctly.411 */412export type UnsupportedTerminal = {413  kind: 'unsupported';414  id: InstructionId;415  loc: SourceLocation;416  fallthrough?: never;417};418419/**420 * Terminal for an unreachable block.421 * Unreachable blocks are emitted when all control flow paths of a if/switch/try block diverge422 * before reaching the fallthrough.423 */424export type UnreachableTerminal = {425  kind: 'unreachable';426  id: InstructionId;427  loc: SourceLocation;428  fallthrough?: never;429};430431export type ThrowTerminal = {432  kind: 'throw';433  value: Place;434  id: InstructionId;435  loc: SourceLocation;436  fallthrough?: never;437};438export type Case = {test: Place | null; block: BlockId};439440export type ReturnVariant = 'Void' | 'Implicit' | 'Explicit';441export type ReturnTerminal = {442  kind: 'return';443  /**444   * Void:445   *   () => { ... }446   *   function() { ... }447   * Implicit (ArrowFunctionExpression only):448   *   () => foo449   * Explicit:450   *   () => { return ... }451   *   function () { return ... }452   */453  returnVariant: ReturnVariant;454  loc: SourceLocation;455  value: Place;456  id: InstructionId;457  fallthrough?: never;458  effects: Array<AliasingEffect> | null;459};460461export type GotoTerminal = {462  kind: 'goto';463  block: BlockId;464  variant: GotoVariant;465  id: InstructionId;466  loc: SourceLocation;467  fallthrough?: never;468};469470export enum GotoVariant {471  Break = 'Break',472  Continue = 'Continue',473  Try = 'Try',474}475476export type IfTerminal = {477  kind: 'if';478  test: Place;479  consequent: BlockId;480  alternate: BlockId;481  fallthrough: BlockId;482  id: InstructionId;483  loc: SourceLocation;484};485486export type BranchTerminal = {487  kind: 'branch';488  test: Place;489  consequent: BlockId;490  alternate: BlockId;491  id: InstructionId;492  loc: SourceLocation;493  fallthrough: BlockId;494};495496export type SwitchTerminal = {497  kind: 'switch';498  test: Place;499  cases: Array<Case>;500  fallthrough: BlockId;501  id: InstructionId;502  loc: SourceLocation;503};504505export type DoWhileTerminal = {506  kind: 'do-while';507  loop: BlockId;508  test: BlockId;509  fallthrough: BlockId;510  id: InstructionId;511  loc: SourceLocation;512};513514export type WhileTerminal = {515  kind: 'while';516  loc: SourceLocation;517  test: BlockId;518  loop: BlockId;519  fallthrough: BlockId;520  id: InstructionId;521};522523export type ForTerminal = {524  kind: 'for';525  loc: SourceLocation;526  init: BlockId;527  test: BlockId;528  update: BlockId | null;529  loop: BlockId;530  fallthrough: BlockId;531  id: InstructionId;532};533534export type ForOfTerminal = {535  kind: 'for-of';536  loc: SourceLocation;537  init: BlockId;538  test: BlockId;539  loop: BlockId;540  fallthrough: BlockId;541  id: InstructionId;542};543544export type ForInTerminal = {545  kind: 'for-in';546  loc: SourceLocation;547  init: BlockId;548  loop: BlockId;549  fallthrough: BlockId;550  id: InstructionId;551};552553export type LogicalTerminal = {554  kind: 'logical';555  operator: t.LogicalExpression['operator'];556  test: BlockId;557  fallthrough: BlockId;558  id: InstructionId;559  loc: SourceLocation;560};561562export type TernaryTerminal = {563  kind: 'ternary';564  test: BlockId;565  fallthrough: BlockId;566  id: InstructionId;567  loc: SourceLocation;568};569570export type LabelTerminal = {571  kind: 'label';572  block: BlockId;573  fallthrough: BlockId;574  id: InstructionId;575  loc: SourceLocation;576};577578export type OptionalTerminal = {579  kind: 'optional';580  /*581   * Specifies whether this node was optional. If false, it means that the original582   * node was part of an optional chain but this specific item was non-optional.583   * For example, in `a?.b.c?.()`, the `.b` access is non-optional but appears within584   * an optional chain.585   */586  optional: boolean;587  test: BlockId;588  fallthrough: BlockId;589  id: InstructionId;590  loc: SourceLocation;591};592593export type SequenceTerminal = {594  kind: 'sequence';595  block: BlockId;596  fallthrough: BlockId;597  id: InstructionId;598  loc: SourceLocation;599};600601export type TryTerminal = {602  kind: 'try';603  block: BlockId;604  handlerBinding: Place | null;605  handler: BlockId;606  // TODO: support `finally`607  fallthrough: BlockId;608  id: InstructionId;609  loc: SourceLocation;610};611612export type MaybeThrowTerminal = {613  kind: 'maybe-throw';614  continuation: BlockId;615  handler: BlockId | null;616  id: InstructionId;617  loc: SourceLocation;618  fallthrough?: never;619  effects: Array<AliasingEffect> | null;620};621622export type ReactiveScopeTerminal = {623  kind: 'scope';624  fallthrough: BlockId;625  block: BlockId;626  scope: ReactiveScope;627  id: InstructionId;628  loc: SourceLocation;629};630631export type PrunedScopeTerminal = {632  kind: 'pruned-scope';633  fallthrough: BlockId;634  block: BlockId;635  scope: ReactiveScope;636  id: InstructionId;637  loc: SourceLocation;638};639640/*641 * Instructions generally represent expressions but with all nesting flattened away,642 * such that all operands to each instruction are either primitive values OR are643 * references to a place, which may be a temporary that holds the results of a644 * previous instruction. So `foo(bar(a))` would decompose into two instructions,645 * one to store `tmp0 = bar(a)`, one for `foo(tmp0)`.646 *647 * Instructions generally store their value into a Place, though some instructions648 * may not produce a value that is necessary to track (for example, class definitions)649 * or may occur only for side-effects (many expression statements).650 */651export type Instruction = {652  id: InstructionId;653  lvalue: Place;654  value: InstructionValue;655  loc: SourceLocation;656  effects: Array<AliasingEffect> | null;657};658659export type TInstruction<T extends InstructionValue> = {660  id: InstructionId;661  lvalue: Place;662  value: T;663  effects: Array<AliasingEffect> | null;664  loc: SourceLocation;665};666667export type LValue = {668  place: Place;669  kind: InstructionKind;670};671672export type LValuePattern = {673  pattern: Pattern;674  kind: InstructionKind;675};676677export type ArrayExpression = {678  kind: 'ArrayExpression';679  elements: Array<Place | SpreadPattern | Hole>;680  loc: SourceLocation;681};682683export type Pattern = ArrayPattern | ObjectPattern;684685export type Hole = {686  kind: 'Hole';687};688689export type SpreadPattern = {690  kind: 'Spread';691  place: Place;692};693694export type ArrayPattern = {695  kind: 'ArrayPattern';696  items: Array<Place | SpreadPattern | Hole>;697  loc: SourceLocation;698};699700export type ObjectPattern = {701  kind: 'ObjectPattern';702  properties: Array<ObjectProperty | SpreadPattern>;703  loc: SourceLocation;704};705706export type ObjectPropertyKey =707  | {708      kind: 'string';709      name: string;710    }711  | {712      kind: 'identifier';713      name: string;714    }715  | {716      kind: 'computed';717      name: Place;718    }719  | {720      kind: 'number';721      name: number;722    };723724export type ObjectProperty = {725  kind: 'ObjectProperty';726  key: ObjectPropertyKey;727  type: 'property' | 'method';728  place: Place;729};730731export type LoweredFunction = {732  func: HIRFunction;733};734735export type ObjectMethod = {736  kind: 'ObjectMethod';737  loc: SourceLocation;738  loweredFunc: LoweredFunction;739};740741export enum InstructionKind {742  // const declaration743  Const = 'Const',744  // let declaration745  Let = 'Let',746  // assing a new value to a let binding747  Reassign = 'Reassign',748  // catch clause binding749  Catch = 'Catch',750751  // hoisted const declarations752  HoistedConst = 'HoistedConst',753754  // hoisted const declarations755  HoistedLet = 'HoistedLet',756757  HoistedFunction = 'HoistedFunction',758  Function = 'Function',759}760761export function convertHoistedLValueKind(762  kind: InstructionKind,763): InstructionKind | null {764  switch (kind) {765    case InstructionKind.HoistedLet:766      return InstructionKind.Let;767    case InstructionKind.HoistedConst:768      return InstructionKind.Const;769    case InstructionKind.HoistedFunction:770      return InstructionKind.Function;771    case InstructionKind.Let:772    case InstructionKind.Const:773    case InstructionKind.Function:774    case InstructionKind.Reassign:775    case InstructionKind.Catch:776      return null;777    default:778      assertExhaustive(kind, 'Unexpected lvalue kind');779  }780}781782function _staticInvariantInstructionValueHasLocation(783  value: InstructionValue,784): SourceLocation {785  // If this fails, it is because a variant of InstructionValue is missing a .loc - add it!786  return value.loc;787}788789export type Phi = {790  kind: 'Phi';791  place: Place;792  operands: Map<BlockId, Place>;793};794795/**796 * Valid ManualMemoDependencies are always of the form797 * `sourceDeclaredVariable.a.b?.c`, since this is documented798 * and enforced by the `react-hooks/exhaustive-deps` rule.799 *800 * `root` must either reference a ValidatedIdentifier or a global801 * variable.802 */803export type ManualMemoDependency = {804  root:805    | {806        kind: 'NamedLocal';807        value: Place;808        constant: boolean;809      }810    | {kind: 'Global'; identifierName: string};811  path: DependencyPath;812  loc: SourceLocation;813};814815export type StartMemoize = {816  kind: 'StartMemoize';817  // Start/FinishMemoize markers should have matching ids818  manualMemoId: number;819  /**820   * deps-list from source code, or null if one was not provided821   * (e.g. useMemo without a second arg)822   */823  deps: Array<ManualMemoDependency> | null;824  /**825   * The source location of the dependencies argument. Used for826   * emitting diagnostics with a suggested replacement827   */828  depsLoc: SourceLocation | null;829  hasInvalidDeps?: true;830  loc: SourceLocation;831};832export type FinishMemoize = {833  kind: 'FinishMemoize';834  // Start/FinishMemoize markers should have matching ids835  manualMemoId: number;836  decl: Place;837  pruned?: true;838  loc: SourceLocation;839};840841/*842 * Forget currently does not handle MethodCall correctly in843 * all cases. Specifically, we do not bind the receiver and method property844 * before calling to args. Until we add a SequenceExpression to inline all845 * instructions generated when lowering args, we have a limited representation846 * with some constraints.847 *848 * Forget currently makes these assumptions (checked in codegen):849 *   - {@link MethodCall.property} is a temporary produced by a PropertyLoad or ComputedLoad850 *     on {@link MethodCall.receiver}851 *   - {@link MethodCall.property} remains an rval (i.e. never promoted to a852 *     named identifier). We currently rely on this for codegen.853 *854 * Type inference does not currently guarantee that {@link MethodCall.property}855 * is a FunctionType.856 */857export type MethodCall = {858  kind: 'MethodCall';859  receiver: Place;860  property: Place;861  args: Array<Place | SpreadPattern>;862  loc: SourceLocation;863};864865export type CallExpression = {866  kind: 'CallExpression';867  callee: Place;868  args: Array<Place | SpreadPattern>;869  loc: SourceLocation;870  typeArguments?: Array<t.FlowType>;871};872873export type NewExpression = {874  kind: 'NewExpression';875  callee: Place;876  args: Array<Place | SpreadPattern>;877  loc: SourceLocation;878};879880export type LoadLocal = {881  kind: 'LoadLocal';882  place: Place;883  loc: SourceLocation;884};885export type LoadContext = {886  kind: 'LoadContext';887  place: Place;888  loc: SourceLocation;889};890891/*892 * The value of a given instruction. Note that values are not recursive: complex893 * values such as objects or arrays are always defined by instructions to define894 * their operands (saving to a temporary), then passing those temporaries as895 * the operands to the final instruction (ObjectExpression, ArrayExpression, etc).896 *897 * Operands are therefore always a Place.898 */899900export type InstructionValue =901  | LoadLocal902  | LoadContext903  | {904      kind: 'DeclareLocal';905      lvalue: LValue;906      type: t.FlowType | t.TSType | null;907      loc: SourceLocation;908    }909  | {910      kind: 'DeclareContext';911      lvalue: {912        kind:913          | InstructionKind.Let914          | InstructionKind.HoistedConst915          | InstructionKind.HoistedLet916          | InstructionKind.HoistedFunction;917        place: Place;918      };919      loc: SourceLocation;920    }921  | StoreLocal922  | {923      kind: 'StoreContext';924      /**925       * StoreContext kinds:926       * Reassign: context variable reassignment in source927       * Const:    const declaration + assignment in source928       *           ('const' context vars are ones whose declarations are hoisted)929       * Let:      let declaration + assignment in source930       * Function: function declaration in source (similar to `const`)931       */932      lvalue: {933        kind:934          | InstructionKind.Reassign935          | InstructionKind.Const936          | InstructionKind.Let937          | InstructionKind.Function;938        place: Place;939      };940      value: Place;941      loc: SourceLocation;942    }943  | Destructure944  | {945      kind: 'Primitive';946      value: number | boolean | string | null | undefined;947      loc: SourceLocation;948    }949  | JSXText950  | {951      kind: 'BinaryExpression';952      operator: Exclude<t.BinaryExpression['operator'], '|>'>;953      left: Place;954      right: Place;955      loc: SourceLocation;956    }957  | NewExpression958  | CallExpression959  | MethodCall960  | {961      kind: 'UnaryExpression';962      operator: Exclude<t.UnaryExpression['operator'], 'throw' | 'delete'>;963      value: Place;964      loc: SourceLocation;965    }966  | ({967      kind: 'TypeCastExpression';968      value: Place;969      type: Type;970      loc: SourceLocation;971    } & (972      | {973          typeAnnotation: t.FlowType;974          typeAnnotationKind: 'cast';975        }976      | {977          typeAnnotation: t.TSType;978          typeAnnotationKind: 'as' | 'satisfies';979        }980    ))981  | JsxExpression982  | {983      kind: 'ObjectExpression';984      properties: Array<ObjectProperty | SpreadPattern>;985      loc: SourceLocation;986    }987  | ObjectMethod988  | ArrayExpression989  | {kind: 'JsxFragment'; children: Array<Place>; loc: SourceLocation}990  | {991      kind: 'RegExpLiteral';992      pattern: string;993      flags: string;994      loc: SourceLocation;995    }996  | {997      kind: 'MetaProperty';998      meta: string;999      property: string;1000      loc: SourceLocation;1001    }10021003  // store `object.property = value`1004  | {1005      kind: 'PropertyStore';1006      object: Place;1007      property: PropertyLiteral;1008      value: Place;1009      loc: SourceLocation;1010    }1011  // load `object.property`1012  | PropertyLoad1013  // `delete object.property`1014  | {1015      kind: 'PropertyDelete';1016      object: Place;1017      property: PropertyLiteral;1018      loc: SourceLocation;1019    }10201021  // store `object[index] = value` - like PropertyStore but with a dynamic property1022  | {1023      kind: 'ComputedStore';1024      object: Place;1025      property: Place;1026      value: Place;1027      loc: SourceLocation;1028    }1029  // load `object[index]` - like PropertyLoad but with a dynamic property1030  | {1031      kind: 'ComputedLoad';1032      object: Place;1033      property: Place;1034      loc: SourceLocation;1035    }1036  // `delete object[property]`1037  | {1038      kind: 'ComputedDelete';1039      object: Place;1040      property: Place;1041      loc: SourceLocation;1042    }1043  | LoadGlobal1044  | StoreGlobal1045  | FunctionExpression1046  | {1047      kind: 'TaggedTemplateExpression';1048      tag: Place;1049      value: {raw: string; cooked?: string};1050      loc: SourceLocation;1051    }1052  | {1053      kind: 'TemplateLiteral';1054      subexprs: Array<Place>;1055      quasis: Array<{raw: string; cooked?: string}>;1056      loc: SourceLocation;1057    }1058  | {1059      kind: 'Await';1060      value: Place;1061      loc: SourceLocation;1062    }1063  | {1064      kind: 'GetIterator';1065      collection: Place; // the collection1066      loc: SourceLocation;1067    }1068  | {1069      kind: 'IteratorNext';1070      iterator: Place; // the iterator created with GetIterator1071      collection: Place; // the collection being iterated over (which may be an iterable or iterator)1072      loc: SourceLocation;1073    }1074  | {1075      kind: 'NextPropertyOf';1076      value: Place; // the collection1077      loc: SourceLocation;1078    }1079  /*1080   * Models a prefix update expression such as --x or ++y1081   * This instructions increments or decrements the <lvalue>1082   * but evaluates to the value of <value> prior to the update.1083   */1084  | {1085      kind: 'PrefixUpdate';1086      lvalue: Place;1087      operation: t.UpdateExpression['operator'];1088      value: Place;1089      loc: SourceLocation;1090    }1091  /*1092   * Models a postfix update expression such as x-- or y++1093   * This instructions increments or decrements the <lvalue>1094   * and evaluates to the value after the update1095   */1096  | {1097      kind: 'PostfixUpdate';1098      lvalue: Place;1099      operation: t.UpdateExpression['operator'];1100      value: Place;1101      loc: SourceLocation;1102    }1103  // `debugger` statement1104  | {kind: 'Debugger'; loc: SourceLocation}1105  /*1106   * Represents semantic information from useMemo/useCallback that the developer1107   * has indicated a particular value should be memoized. This value is ignored1108   * unless the TODO flag is enabled.1109   *1110   * NOTE: the Memoize instruction is intended for side-effects only, and is pruned1111   * during codegen. It can't be pruned during DCE because we need to preserve the1112   * instruction so it can be visible in InferReferenceEffects.1113   */1114  | StartMemoize1115  | FinishMemoize1116  /*1117   * Catch-all for statements such as type imports, nested class declarations, etc1118   * which are not directly represented, but included for completeness and to allow1119   * passing through in codegen.1120   */1121  | {1122      kind: 'UnsupportedNode';1123      node: t.Node;1124      loc: SourceLocation;1125    };11261127export type JsxExpression = {1128  kind: 'JsxExpression';1129  tag: Place | BuiltinTag;1130  props: Array<JsxAttribute>;1131  children: Array<Place> | null; // null === no children1132  loc: SourceLocation;1133  openingLoc: SourceLocation;1134  closingLoc: SourceLocation;1135};11361137export type JsxAttribute =1138  | {kind: 'JsxSpreadAttribute'; argument: Place}1139  | {kind: 'JsxAttribute'; name: string; place: Place};11401141export type FunctionExpression = {1142  kind: 'FunctionExpression';1143  name: ValidIdentifierName | null;1144  nameHint: string | null;1145  loweredFunc: LoweredFunction;1146  type:1147    | 'ArrowFunctionExpression'1148    | 'FunctionExpression'1149    | 'FunctionDeclaration';1150  loc: SourceLocation;1151};11521153export type Destructure = {1154  kind: 'Destructure';1155  lvalue: LValuePattern;1156  value: Place;1157  loc: SourceLocation;1158};11591160/*1161 * A place where data may be read from / written to:1162 * - a variable (identifier)1163 * - a path into an identifier1164 */1165export type Place = {1166  kind: 'Identifier';1167  identifier: Identifier;1168  effect: Effect;1169  reactive: boolean;1170  loc: SourceLocation;1171};11721173// A primitive value with a specific (constant) value.1174export type Primitive = {1175  kind: 'Primitive';1176  value: number | boolean | string | null | undefined;1177  loc: SourceLocation;1178};11791180export type JSXText = {kind: 'JSXText'; value: string; loc: SourceLocation};11811182export type StoreLocal = {1183  kind: 'StoreLocal';1184  lvalue: LValue;1185  value: Place;1186  type: t.FlowType | t.TSType | null;1187  loc: SourceLocation;1188};1189export type PropertyLoad = {1190  kind: 'PropertyLoad';1191  object: Place;1192  property: PropertyLiteral;1193  loc: SourceLocation;1194};11951196export type LoadGlobal = {1197  kind: 'LoadGlobal';1198  binding: NonLocalBinding;1199  loc: SourceLocation;1200};12011202export type StoreGlobal = {1203  kind: 'StoreGlobal';1204  name: string;1205  value: Place;1206  loc: SourceLocation;1207};12081209export type BuiltinTag = {1210  kind: 'BuiltinTag';1211  name: string;1212  loc: SourceLocation;1213};12141215/*1216 * Range in which an identifier is mutable. Start and End refer to Instruction.id.1217 *1218 * Start is inclusive, End is exclusive (ie, end is the "first" instruction for which1219 * the value is not mutable).1220 */1221export type MutableRange = {1222  start: InstructionId;1223  end: InstructionId;1224};12251226export type VariableBinding =1227  // let, const, etc declared within the current component/hook1228  | {kind: 'Identifier'; identifier: Identifier; bindingKind: BindingKind}1229  // bindings declard outside the current component/hook1230  | NonLocalBinding;12311232// `import {bar as baz} from 'foo'`: name=baz, module=foo, imported=bar1233export type NonLocalImportSpecifier = {1234  kind: 'ImportSpecifier';1235  name: string;1236  module: string;1237  imported: string;1238};12391240export type NonLocalBinding =1241  // `import Foo from 'foo'`: name=Foo, module=foo1242  | {kind: 'ImportDefault'; name: string; module: string}1243  // `import * as Foo from 'foo'`: name=Foo, module=foo1244  | {kind: 'ImportNamespace'; name: string; module: string}1245  // `import {bar as baz} from 'foo'`1246  | NonLocalImportSpecifier1247  // let, const, function, etc declared in the module but outside the current component/hook1248  | {kind: 'ModuleLocal'; name: string}1249  // an unresolved binding1250  | {kind: 'Global'; name: string};12511252// Represents a user-defined variable (has a name) or a temporary variable (no name).1253export type Identifier = {1254  /**1255   * After EnterSSA, `id` uniquely identifies an SSA instance of a variable.1256   * Before EnterSSA, `id` matches `declarationId`.1257   */1258  id: IdentifierId;12591260  /**1261   * Uniquely identifies a given variable in the original program. If a value is1262   * reassigned in the original program each reassigned value will have a distinct1263   * `id` (after EnterSSA), but they will still have the same `declarationId`.1264   */1265  declarationId: DeclarationId;12661267  // null for temporaries. name is primarily used for debugging.1268  name: IdentifierName | null;1269  // The range for which this variable is mutable1270  mutableRange: MutableRange;1271  /*1272   * The ID of the reactive scope which will compute this value. Multiple1273   * variables may have the same scope id.1274   */1275  scope: ReactiveScope | null;1276  type: Type;1277  loc: SourceLocation;1278};12791280export type IdentifierName = ValidatedIdentifier | PromotedIdentifier;1281export type ValidatedIdentifier = {kind: 'named'; value: ValidIdentifierName};1282export type PromotedIdentifier = {kind: 'promoted'; value: string};12831284/**1285 * Simulated opaque type for identifier names to ensure values can only be created1286 * through the below helpers.1287 */1288const opaqueValidIdentifierName = Symbol();1289export type ValidIdentifierName = string & {1290  [opaqueValidIdentifierName]: 'ValidIdentifierName';1291};12921293export function makeTemporaryIdentifier(1294  id: IdentifierId,1295  loc: SourceLocation,1296): Identifier {1297  return {1298    id,1299    name: null,1300    declarationId: makeDeclarationId(id),1301    mutableRange: {start: makeInstructionId(0), end: makeInstructionId(0)},1302    scope: null,1303    type: makeType(),1304    loc,1305  };1306}13071308export function forkTemporaryIdentifier(1309  id: IdentifierId,1310  source: Identifier,1311): Identifier {1312  return {1313    ...source,1314    mutableRange: {start: makeInstructionId(0), end: makeInstructionId(0)},1315    id,1316  };1317}13181319export function validateIdentifierName(1320  name: string,1321): Result<ValidatedIdentifier, CompilerError> {1322  if (isReservedWord(name)) {1323    const error = new CompilerError();1324    error.pushDiagnostic(1325      CompilerDiagnostic.create({1326        category: ErrorCategory.Syntax,1327        reason: 'Expected a non-reserved identifier name',1328        description: `\`${name}\` is a reserved word in JavaScript and cannot be used as an identifier name`,1329        suggestions: null,1330      }).withDetails({1331        kind: 'error',1332        loc: GeneratedSource,1333        message: 'reserved word',1334      }),1335    );1336    return Err(error);1337  } else if (!t.isValidIdentifier(name)) {1338    const error = new CompilerError();1339    error.pushDiagnostic(1340      CompilerDiagnostic.create({1341        category: ErrorCategory.Syntax,1342        reason: `Expected a valid identifier name`,1343        description: `\`${name}\` is not a valid JavaScript identifier`,1344        suggestions: null,1345      }).withDetails({1346        kind: 'error',1347        loc: GeneratedSource,1348        message: 'reserved word',1349      }),1350    );1351  }1352  return Ok({1353    kind: 'named',1354    value: name as ValidIdentifierName,1355  });1356}13571358/**1359 * Creates a valid identifier name. This should *not* be used for synthesizing1360 * identifier names: only call this method for identifier names that appear in the1361 * original source code.1362 */1363export function makeIdentifierName(name: string): ValidatedIdentifier {1364  return validateIdentifierName(name).unwrap();1365}13661367/**1368 * Given an unnamed identifier, promote it to a named identifier.1369 *1370 * Note: this uses the identifier's DeclarationId to ensure that all1371 * instances of the same declaration will have the same name.1372 */1373export function promoteTemporary(identifier: Identifier): void {1374  CompilerError.invariant(identifier.name === null, {1375    reason: `Expected a temporary (unnamed) identifier`,1376    description: `Identifier already has a name, \`${identifier.name}\``,1377    loc: GeneratedSource,1378  });1379  identifier.name = {1380    kind: 'promoted',1381    value: `#t${identifier.declarationId}`,1382  };1383}13841385export function isPromotedTemporary(name: string): boolean {1386  return name.startsWith('#t');1387}13881389/**1390 * Given an unnamed identifier, promote it to a named identifier, distinguishing1391 * it as a value that needs to be capitalized since it appears in JSX element tag position1392 *1393 * Note: this uses the identifier's DeclarationId to ensure that all1394 * instances of the same declaration will have the same name.1395 */1396export function promoteTemporaryJsxTag(identifier: Identifier): void {1397  CompilerError.invariant(identifier.name === null, {1398    reason: `Expected a temporary (unnamed) identifier`,1399    description: `Identifier already has a name, \`${identifier.name}\``,1400    loc: GeneratedSource,1401  });1402  identifier.name = {1403    kind: 'promoted',1404    value: `#T${identifier.declarationId}`,1405  };1406}14071408export function isPromotedJsxTemporary(name: string): boolean {1409  return name.startsWith('#T');1410}14111412export type AbstractValue = {1413  kind: ValueKind;1414  reason: ReadonlySet<ValueReason>;1415  context: ReadonlySet<Place>;1416};14171418/**1419 * The reason for the kind of a value.1420 */1421export enum ValueReason {1422  /**1423   * Defined outside the React function.1424   */1425  Global = 'global',14261427  /**1428   * Used in a JSX expression.1429   */1430  JsxCaptured = 'jsx-captured',14311432  /**1433   * Argument to a hook1434   */1435  HookCaptured = 'hook-captured',14361437  /**1438   * Return value of a hook1439   */1440  HookReturn = 'hook-return',14411442  /**1443   * Passed to an effect1444   */1445  Effect = 'effect',14461447  /**1448   * Return value of a function with known frozen return value, e.g. `useState`.1449   */1450  KnownReturnSignature = 'known-return-signature',14511452  /**1453   * A value returned from `useContext`1454   */1455  Context = 'context',14561457  /**1458   * A value returned from `useState`1459   */1460  State = 'state',14611462  /**1463   * A value returned from `useReducer`1464   */1465  ReducerState = 'reducer-state',14661467  /**1468   * Props of a component or arguments of a hook.1469   */1470  ReactiveFunctionArgument = 'reactive-function-argument',14711472  Other = 'other',1473}14741475/*1476 * Distinguish between different kinds of values relevant to inference purposes:1477 * see the main docblock for the module for details.1478 */1479export enum ValueKind {1480  MaybeFrozen = 'maybefrozen',1481  Frozen = 'frozen',1482  Primitive = 'primitive',1483  Global = 'global',1484  Mutable = 'mutable',1485  Context = 'context',1486}14871488export const ValueKindSchema = z.enum([1489  ValueKind.MaybeFrozen,1490  ValueKind.Frozen,1491  ValueKind.Primitive,1492  ValueKind.Global,1493  ValueKind.Mutable,1494  ValueKind.Context,1495]);14961497export const ValueReasonSchema = z.enum([1498  ValueReason.Context,1499  ValueReason.Effect,1500  ValueReason.Global,1501  ValueReason.HookCaptured,1502  ValueReason.HookReturn,1503  ValueReason.JsxCaptured,1504  ValueReason.KnownReturnSignature,1505  ValueReason.Other,1506  ValueReason.ReactiveFunctionArgument,1507  ValueReason.ReducerState,1508  ValueReason.State,1509]);15101511// The effect with which a value is modified.1512export enum Effect {1513  // Default value: not allowed after lifetime inference1514  Unknown = '<unknown>',1515  // This reference freezes the value (corresponds to a place where codegen should emit a freeze instruction)1516  Freeze = 'freeze',1517  // This reference reads the value1518  Read = 'read',1519  // This reference reads and stores the value1520  Capture = 'capture',1521  ConditionallyMutateIterator = 'mutate-iterator?',1522  /*1523   * This reference *may* write to (mutate) the value. This covers two similar cases:1524   * - The compiler is being conservative and assuming that a value *may* be mutated1525   * - The effect is polymorphic: mutable values may be mutated, non-mutable values1526   *   will not be mutated.1527   * In both cases, we conservatively assume that mutable values will be mutated.1528   * But we do not error if the value is known to be immutable.1529   */1530  ConditionallyMutate = 'mutate?',15311532  /*1533   * This reference *does* write to (mutate) the value. It is an error (invalid input)1534   * if an immutable value flows into a location with this effect.1535   */1536  Mutate = 'mutate',1537  // This reference may alias to (mutate) the value1538  Store = 'store',1539}1540export const EffectSchema = z.enum([1541  Effect.Read,1542  Effect.Mutate,1543  Effect.ConditionallyMutate,1544  Effect.ConditionallyMutateIterator,1545  Effect.Capture,1546  Effect.Store,1547  Effect.Freeze,1548]);15491550export function isMutableEffect(1551  effect: Effect,1552  location: SourceLocation,1553): boolean {1554  switch (effect) {1555    case Effect.Capture:1556    case Effect.Store:1557    case Effect.ConditionallyMutate:1558    case Effect.ConditionallyMutateIterator:1559    case Effect.Mutate: {1560      return true;1561    }15621563    case Effect.Unknown: {1564      CompilerError.invariant(false, {1565        reason: 'Unexpected unknown effect',1566        loc: location,1567      });1568    }1569    case Effect.Read:1570    case Effect.Freeze: {1571      return false;1572    }1573    default: {1574      assertExhaustive(effect, `Unexpected effect \`${effect}\``);1575    }1576  }1577}15781579export type ReactiveScope = {1580  id: ScopeId;1581  range: MutableRange;15821583  /**1584   * The inputs to this reactive scope1585   */1586  dependencies: ReactiveScopeDependencies;15871588  /**1589   * The set of values produced by this scope. This may be empty1590   * for scopes that produce reassignments only.1591   */1592  declarations: Map<IdentifierId, ReactiveScopeDeclaration>;15931594  /**1595   * A mutable range may sometimes include a reassignment of some variable.1596   * This is the set of identifiers which are reassigned by this scope.1597   */1598  reassignments: Set<Identifier>;15991600  /**1601   * Reactive scopes may contain a return statement, which needs to be replayed1602   * whenever the inputs to the scope have not changed since the previous execution.1603   * If the reactive scope has an early return, this variable stores the temporary1604   * identifier to which the return value will be assigned. See PropagateEarlyReturns1605   * for more about how early returns in reactive scopes are compiled and represented.1606   *1607   * This value is null for scopes that do not contain early returns.1608   */1609  earlyReturnValue: {1610    value: Identifier;1611    loc: SourceLocation;1612    label: BlockId;1613  } | null;16141615  /*1616   * Some passes may merge scopes together. The merged set contains the1617   * ids of scopes that were merged into this one, for passes that need1618   * to track which scopes are still present (in some form) vs scopes that1619   * no longer exist due to being pruned.1620   */1621  merged: Set<ScopeId>;16221623  loc: SourceLocation;1624};16251626export type ReactiveScopeDependencies = Set<ReactiveScopeDependency>;16271628export type ReactiveScopeDeclaration = {1629  identifier: Identifier;1630  scope: ReactiveScope; // the scope in which the variable was originally declared1631};16321633const opaquePropertyLiteral = Symbol();1634export type PropertyLiteral = (string | number) & {1635  [opaquePropertyLiteral]: 'PropertyLiteral';1636};1637export function makePropertyLiteral(value: string | number): PropertyLiteral {1638  return value as PropertyLiteral;1639}1640export type DependencyPathEntry = {1641  property: PropertyLiteral;1642  optional: boolean;1643  loc: SourceLocation;1644};1645export type DependencyPath = Array<DependencyPathEntry>;1646export type ReactiveScopeDependency = {1647  identifier: Identifier;1648  /**1649   * Reflects whether the base identifier is reactive. Note that some reactive1650   * objects may have non-reactive properties, but we do not currently track1651   * this.1652   *1653   * ```js1654   * // Technically, result[0] is reactive and result[1] is not.1655   * // Currently, both dependencies would be marked as reactive.1656   * const result = useState();1657   * ```1658   */1659  reactive: boolean;1660  path: DependencyPath;1661  loc: SourceLocation;1662};16631664export function areEqualPaths(a: DependencyPath, b: DependencyPath): boolean {1665  return (1666    a.length === b.length &&1667    a.every(1668      (item, ix) =>1669        item.property === b[ix].property && item.optional === b[ix].optional,1670    )1671  );1672}1673export function isSubPath(1674  subpath: DependencyPath,1675  path: DependencyPath,1676): boolean {1677  return (1678    subpath.length <= path.length &&1679    subpath.every(1680      (item, ix) =>1681        item.property === path[ix].property &&1682        item.optional === path[ix].optional,1683    )1684  );1685}1686export function isSubPathIgnoringOptionals(1687  subpath: DependencyPath,1688  path: DependencyPath,1689): boolean {1690  return (1691    subpath.length <= path.length &&1692    subpath.every((item, ix) => item.property === path[ix].property)1693  );1694}16951696export function getPlaceScope(1697  id: InstructionId,1698  place: Place,1699): ReactiveScope | null {1700  const scope = place.identifier.scope;1701  if (scope !== null && isScopeActive(scope, id)) {1702    return scope;1703  }1704  return null;1705}17061707function isScopeActive(scope: ReactiveScope, id: InstructionId): boolean {1708  return id >= scope.range.start && id < scope.range.end;1709}17101711/*1712 * Simulated opaque type for BlockIds to prevent using normal numbers as block ids1713 * accidentally.1714 */1715const opaqueBlockId = Symbol();1716export type BlockId = number & {[opaqueBlockId]: 'BlockId'};17171718export function makeBlockId(id: number): BlockId {1719  CompilerError.invariant(id >= 0 && Number.isInteger(id), {1720    reason: 'Expected block id to be a non-negative integer',1721    loc: GeneratedSource,1722  });1723  return id as BlockId;1724}17251726/*1727 * Simulated opaque type for ScopeIds to prevent using normal numbers as scope ids1728 * accidentally.1729 */1730const opaqueScopeId = Symbol();1731export type ScopeId = number & {[opaqueScopeId]: 'ScopeId'};17321733export function makeScopeId(id: number): ScopeId {1734  CompilerError.invariant(id >= 0 && Number.isInteger(id), {1735    reason: 'Expected block id to be a non-negative integer',1736    loc: GeneratedSource,1737  });1738  return id as ScopeId;1739}17401741/*1742 * Simulated opaque type for IdentifierId to prevent using normal numbers as ids1743 * accidentally.1744 */1745const opaqueIdentifierId = Symbol();1746export type IdentifierId = number & {[opaqueIdentifierId]: 'IdentifierId'};17471748export function makeIdentifierId(id: number): IdentifierId {1749  CompilerError.invariant(id >= 0 && Number.isInteger(id), {1750    reason: 'Expected identifier id to be a non-negative integer',1751    loc: GeneratedSource,1752  });1753  return id as IdentifierId;1754}17551756/*1757 * Simulated opaque type for IdentifierId to prevent using normal numbers as ids1758 * accidentally.1759 */1760const opageDeclarationId = Symbol();1761export type DeclarationId = number & {[opageDeclarationId]: 'DeclarationId'};17621763export function makeDeclarationId(id: number): DeclarationId {1764  CompilerError.invariant(id >= 0 && Number.isInteger(id), {1765    reason: 'Expected declaration id to be a non-negative integer',1766    loc: GeneratedSource,1767  });1768  return id as DeclarationId;1769}17701771/*1772 * Simulated opaque type for InstructionId to prevent using normal numbers as ids1773 * accidentally.1774 */1775const opaqueInstructionId = Symbol();1776export type InstructionId = number & {[opaqueInstructionId]: 'IdentifierId'};17771778export function makeInstructionId(id: number): InstructionId {1779  CompilerError.invariant(id >= 0 && Number.isInteger(id), {1780    reason: 'Expected instruction id to be a non-negative integer',1781    loc: GeneratedSource,1782  });1783  return id as InstructionId;1784}17851786export function isObjectMethodType(id: Identifier): boolean {1787  return id.type.kind == 'ObjectMethod';1788}17891790export function isObjectType(id: Identifier): boolean {1791  return id.type.kind === 'Object';1792}17931794export function isPrimitiveType(id: Identifier): boolean {1795  return id.type.kind === 'Primitive';1796}17971798export function isPlainObjectType(id: Identifier): boolean {1799  return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInObject';1800}18011802export function isArrayType(id: Identifier): boolean {1803  return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInArray';1804}18051806export function isMapType(id: Identifier): boolean {1807  return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInMap';1808}18091810export function isSetType(id: Identifier): boolean {1811  return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInSet';1812}18131814export function isPropsType(id: Identifier): boolean {1815  return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInProps';1816}18171818export function isRefValueType(id: Identifier): boolean {1819  return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInRefValue';1820}18211822export function isUseRefType(id: Identifier): boolean {1823  return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseRefId';1824}18251826export function isUseStateType(id: Identifier): boolean {1827  return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseState';1828}18291830export function isJsxType(type: Type): boolean {1831  return type.kind === 'Object' && type.shapeId === 'BuiltInJsx';1832}18331834export function isRefOrRefValue(id: Identifier): boolean {1835  return isUseRefType(id) || isRefValueType(id);1836}18371838/*1839 * Returns true if the type is a Ref or a custom user type that acts like a ref when it1840 * shouldn't. For now the only other case of this is Reanimated's shared values.1841 */1842export function isRefOrRefLikeMutableType(type: Type): boolean {1843  return (1844    type.kind === 'Object' &&1845    (type.shapeId === 'BuiltInUseRefId' ||1846      type.shapeId == 'ReanimatedSharedValueId')1847  );1848}18491850export function isSetStateType(id: Identifier): boolean {1851  return id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetState';1852}18531854export function isUseActionStateType(id: Identifier): boolean {1855  return (1856    id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseActionState'1857  );1858}18591860export function isStartTransitionType(id: Identifier): boolean {1861  return (1862    id.type.kind === 'Function' && id.type.shapeId === 'BuiltInStartTransition'1863  );1864}18651866export function isUseOptimisticType(id: Identifier): boolean {1867  return (1868    id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseOptimistic'1869  );1870}18711872export function isSetOptimisticType(id: Identifier): boolean {1873  return (1874    id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetOptimistic'1875  );1876}18771878export function isSetActionStateType(id: Identifier): boolean {1879  return (1880    id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetActionState'1881  );1882}18831884export function isUseReducerType(id: Identifier): boolean {1885  return id.type.kind === 'Function' && id.type.shapeId === 'BuiltInUseReducer';1886}18871888export function isDispatcherType(id: Identifier): boolean {1889  return id.type.kind === 'Function' && id.type.shapeId === 'BuiltInDispatch';1890}18911892export function isEffectEventFunctionType(id: Identifier): boolean {1893  return (1894    id.type.kind === 'Function' &&1895    id.type.shapeId === 'BuiltInEffectEventFunction'1896  );1897}18981899export function isStableType(id: Identifier): boolean {1900  return (1901    isSetStateType(id) ||1902    isSetActionStateType(id) ||1903    isDispatcherType(id) ||1904    isUseRefType(id) ||1905    isStartTransitionType(id) ||1906    isSetOptimisticType(id)1907  );1908}19091910export function isStableTypeContainer(id: Identifier): boolean {1911  const type_ = id.type;1912  if (type_.kind !== 'Object') {1913    return false;1914  }1915  return (1916    isUseStateType(id) || // setState1917    isUseActionStateType(id) || // setActionState1918    isUseReducerType(id) || // dispatcher1919    isUseOptimisticType(id) || // setOptimistic1920    type_.shapeId === 'BuiltInUseTransition' // startTransition1921  );1922}19231924export function evaluatesToStableTypeOrContainer(1925  env: Environment,1926  {value}: Instruction,1927): boolean {1928  if (value.kind === 'CallExpression' || value.kind === 'MethodCall') {1929    const callee =1930      value.kind === 'CallExpression' ? value.callee : value.property;19311932    const calleeHookKind = getHookKind(env, callee.identifier);1933    switch (calleeHookKind) {1934      case 'useState':1935      case 'useReducer':1936      case 'useActionState':1937      case 'useRef':1938      case 'useTransition':1939      case 'useOptimistic':1940        return true;1941    }1942  }1943  return false;1944}19451946export function isUseEffectHookType(id: Identifier): boolean {1947  return (1948    id.type.kind === 'Function' && id.type.shapeId === 'BuiltInUseEffectHook'1949  );1950}1951export function isUseLayoutEffectHookType(id: Identifier): boolean {1952  return (1953    id.type.kind === 'Function' &&1954    id.type.shapeId === 'BuiltInUseLayoutEffectHook'1955  );1956}1957export function isUseInsertionEffectHookType(id: Identifier): boolean {1958  return (1959    id.type.kind === 'Function' &&1960    id.type.shapeId === 'BuiltInUseInsertionEffectHook'1961  );1962}1963export function isUseEffectEventType(id: Identifier): boolean {1964  return (1965    id.type.kind === 'Function' && id.type.shapeId === 'BuiltInUseEffectEvent'1966  );1967}19681969export function isUseContextHookType(id: Identifier): boolean {1970  return (1971    id.type.kind === 'Function' && id.type.shapeId === 'BuiltInUseContextHook'1972  );1973}19741975export function getHookKind(env: Environment, id: Identifier): HookKind | null {1976  return getHookKindForType(env, id.type);1977}19781979export function isUseOperator(id: Identifier): boolean {1980  return (1981    id.type.kind === 'Function' && id.type.shapeId === 'BuiltInUseOperator'1982  );1983}19841985export function getHookKindForType(1986  env: Environment,1987  type: Type,1988): HookKind | null {1989  if (type.kind === 'Function') {1990    const signature = env.getFunctionSignature(type);1991    return signature?.hookKind ?? null;1992  }1993  return null;1994}19951996export * from './Types';

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.