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.