compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts TYPESCRIPT 4,571 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 4,571.
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 {NodePath, Scope} from '@babel/traverse';9import * as t from '@babel/types';10import invariant from 'invariant';11import {12  CompilerDiagnostic,13  CompilerError,14  CompilerErrorDetail,15  CompilerSuggestionOperation,16  ErrorCategory,17} from '../CompilerError';18import {assertExhaustive, hasNode} from '../Utils/utils';19import {Environment} from './Environment';20import {21  ArrayExpression,22  ArrayPattern,23  BlockId,24  BranchTerminal,25  BuiltinTag,26  Case,27  Effect,28  GeneratedSource,29  GotoVariant,30  HIRFunction,31  IfTerminal,32  InstructionKind,33  InstructionValue,34  JsxAttribute,35  LoweredFunction,36  ObjectPattern,37  ObjectProperty,38  ObjectPropertyKey,39  Place,40  PropertyLiteral,41  ReturnTerminal,42  SourceLocation,43  SpreadPattern,44  ThrowTerminal,45  Type,46  makeInstructionId,47  makePropertyLiteral,48  makeType,49  promoteTemporary,50  validateIdentifierName,51} from './HIR';52import HIRBuilder, {Bindings, createTemporaryPlace} from './HIRBuilder';53import {BuiltInArrayId} from './ObjectShape';5455/*56 * *******************************************************************************************57 * *******************************************************************************************58 * ************************************* Lowering to HIR *************************************59 * *******************************************************************************************60 * *******************************************************************************************61 */6263/*64 * Converts a function into a high-level intermediate form (HIR) which represents65 * the code as a control-flow graph. All normal control-flow is modeled as accurately66 * as possible to allow precise, expression-level memoization. The main exceptions are67 * try/catch statements and exceptions: we currently bail out (skip compilation) for68 * try/catch and do not attempt to model control flow of exceptions, which can occur69 * ~anywhere in JavaScript. The compiler assumes that exceptions will be handled by70 * the runtime, ie by invalidating memoization.71 */72export function lower(73  func: NodePath<t.Function>,74  env: Environment,75  // Bindings captured from the outer function, in case lower() is called recursively (for lambdas)76  bindings: Bindings | null = null,77  capturedRefs: Map<t.Identifier, SourceLocation> = new Map(),78): HIRFunction {79  const builder = new HIRBuilder(env, {80    bindings,81    context: capturedRefs,82  });83  const context: HIRFunction['context'] = [];8485  for (const [ref, loc] of capturedRefs ?? []) {86    context.push({87      kind: 'Identifier',88      identifier: builder.resolveBinding(ref),89      effect: Effect.Unknown,90      reactive: false,91      loc,92    });93  }9495  let id: string | null = null;96  if (func.isFunctionDeclaration() || func.isFunctionExpression()) {97    const idNode = (98      func as NodePath<t.FunctionDeclaration | t.FunctionExpression>99    ).get('id');100    if (hasNode(idNode)) {101      id = idNode.node.name;102    }103  }104  const params: Array<Place | SpreadPattern> = [];105  func.get('params').forEach(param => {106    if (param.isIdentifier()) {107      const binding = builder.resolveIdentifier(param);108      if (binding.kind !== 'Identifier') {109        builder.recordError(110          CompilerDiagnostic.create({111            category: ErrorCategory.Invariant,112            reason: 'Could not find binding',113            description: `[BuildHIR] Could not find binding for param \`${param.node.name}\``,114          }).withDetails({115            kind: 'error',116            loc: param.node.loc ?? null,117            message: 'Could not find binding',118          }),119        );120        return;121      }122      const place: Place = {123        kind: 'Identifier',124        identifier: binding.identifier,125        effect: Effect.Unknown,126        reactive: false,127        loc: param.node.loc ?? GeneratedSource,128      };129      params.push(place);130    } else if (131      param.isObjectPattern() ||132      param.isArrayPattern() ||133      param.isAssignmentPattern()134    ) {135      const place: Place = {136        kind: 'Identifier',137        identifier: builder.makeTemporary(param.node.loc ?? GeneratedSource),138        effect: Effect.Unknown,139        reactive: false,140        loc: param.node.loc ?? GeneratedSource,141      };142      promoteTemporary(place.identifier);143      params.push(place);144      lowerAssignment(145        builder,146        param.node.loc ?? GeneratedSource,147        InstructionKind.Let,148        param,149        place,150        'Assignment',151      );152    } else if (param.isRestElement()) {153      const place: Place = {154        kind: 'Identifier',155        identifier: builder.makeTemporary(param.node.loc ?? GeneratedSource),156        effect: Effect.Unknown,157        reactive: false,158        loc: param.node.loc ?? GeneratedSource,159      };160      params.push({161        kind: 'Spread',162        place,163      });164      lowerAssignment(165        builder,166        param.node.loc ?? GeneratedSource,167        InstructionKind.Let,168        param.get('argument'),169        place,170        'Assignment',171      );172    } else {173      builder.recordError(174        CompilerDiagnostic.create({175          category: ErrorCategory.Todo,176          reason: `Handle ${param.node.type} parameters`,177          description: `[BuildHIR] Add support for ${param.node.type} parameters`,178        }).withDetails({179          kind: 'error',180          loc: param.node.loc ?? null,181          message: 'Unsupported parameter type',182        }),183      );184    }185  });186187  let directives: Array<string> = [];188  const body = func.get('body');189  if (body.isExpression()) {190    const fallthrough = builder.reserve('block');191    const terminal: ReturnTerminal = {192      kind: 'return',193      returnVariant: 'Implicit',194      loc: GeneratedSource,195      value: lowerExpressionToTemporary(builder, body),196      id: makeInstructionId(0),197      effects: null,198    };199    builder.terminateWithContinuation(terminal, fallthrough);200  } else if (body.isBlockStatement()) {201    lowerStatement(builder, body);202    directives = body.get('directives').map(d => d.node.value.value);203  } else {204    builder.recordError(205      CompilerDiagnostic.create({206        category: ErrorCategory.Syntax,207        reason: `Unexpected function body kind`,208        description: `Expected function body to be an expression or a block statement, got \`${body.type}\``,209      }).withDetails({210        kind: 'error',211        loc: body.node.loc ?? null,212        message: 'Expected a block statement or expression',213      }),214    );215  }216217  let validatedId: HIRFunction['id'] = null;218  if (id != null) {219    const idResult = validateIdentifierName(id);220    if (idResult.isErr()) {221      for (const detail of idResult.unwrapErr().details) {222        builder.recordError(detail);223      }224    } else {225      validatedId = idResult.unwrap().value;226    }227  }228229  builder.terminate(230    {231      kind: 'return',232      returnVariant: 'Void',233      loc: GeneratedSource,234      value: lowerValueToTemporary(builder, {235        kind: 'Primitive',236        value: undefined,237        loc: GeneratedSource,238      }),239      id: makeInstructionId(0),240      effects: null,241    },242    null,243  );244245  const hirBody = builder.build();246247  return {248    id: validatedId,249    nameHint: null,250    params,251    fnType: bindings == null ? env.fnType : 'Other',252    returnTypeAnnotation: null, // TODO: extract the actual return type node if present253    returns: createTemporaryPlace(env, func.node.loc ?? GeneratedSource),254    body: hirBody,255    context,256    generator: func.node.generator === true,257    async: func.node.async === true,258    loc: func.node.loc ?? GeneratedSource,259    env,260    aliasingEffects: null,261    directives,262  };263}264265// Helper to lower a statement266function lowerStatement(267  builder: HIRBuilder,268  stmtPath: NodePath<t.Statement>,269  label: string | null = null,270): void {271  const stmtNode = stmtPath.node;272  switch (stmtNode.type) {273    case 'ThrowStatement': {274      const stmt = stmtPath as NodePath<t.ThrowStatement>;275      const value = lowerExpressionToTemporary(builder, stmt.get('argument'));276      const handler = builder.resolveThrowHandler();277      if (handler != null) {278        /*279         * NOTE: we could support this, but a `throw` inside try/catch is using exceptions280         * for control-flow and is generally considered an anti-pattern. we can likely281         * just not support this pattern, unless it really becomes necessary for some reason.282         */283        builder.recordError(284          new CompilerErrorDetail({285            reason:286              '(BuildHIR::lowerStatement) Support ThrowStatement inside of try/catch',287            category: ErrorCategory.Todo,288            loc: stmt.node.loc ?? null,289            suggestions: null,290          }),291        );292      }293      const terminal: ThrowTerminal = {294        kind: 'throw',295        value,296        id: makeInstructionId(0),297        loc: stmt.node.loc ?? GeneratedSource,298      };299      builder.terminate(terminal, 'block');300      return;301    }302    case 'ReturnStatement': {303      const stmt = stmtPath as NodePath<t.ReturnStatement>;304      const argument = stmt.get('argument');305      let value;306      if (argument.node === null) {307        value = lowerValueToTemporary(builder, {308          kind: 'Primitive',309          value: undefined,310          loc: GeneratedSource,311        });312      } else {313        value = lowerExpressionToTemporary(314          builder,315          argument as NodePath<t.Expression>,316        );317      }318      const terminal: ReturnTerminal = {319        kind: 'return',320        returnVariant: 'Explicit',321        loc: stmt.node.loc ?? GeneratedSource,322        value,323        id: makeInstructionId(0),324        effects: null,325      };326      builder.terminate(terminal, 'block');327      return;328    }329    case 'IfStatement': {330      const stmt = stmtPath as NodePath<t.IfStatement>;331      //  Block for code following the if332      const continuationBlock = builder.reserve('block');333      //  Block for the consequent (if the test is truthy)334      const consequentBlock = builder.enter('block', _blockId => {335        const consequent = stmt.get('consequent');336        lowerStatement(builder, consequent);337        return {338          kind: 'goto',339          block: continuationBlock.id,340          variant: GotoVariant.Break,341          id: makeInstructionId(0),342          loc: consequent.node.loc ?? GeneratedSource,343        };344      });345      //  Block for the alternate (if the test is not truthy)346      let alternateBlock: BlockId;347      const alternate = stmt.get('alternate');348      if (hasNode(alternate)) {349        alternateBlock = builder.enter('block', _blockId => {350          lowerStatement(builder, alternate);351          return {352            kind: 'goto',353            block: continuationBlock.id,354            variant: GotoVariant.Break,355            id: makeInstructionId(0),356            loc: alternate.node?.loc ?? GeneratedSource,357          };358        });359      } else {360        //  If there is no else clause, use the continuation directly361        alternateBlock = continuationBlock.id;362      }363      const test = lowerExpressionToTemporary(builder, stmt.get('test'));364      const terminal: IfTerminal = {365        kind: 'if',366        test,367        consequent: consequentBlock,368        alternate: alternateBlock,369        fallthrough: continuationBlock.id,370        id: makeInstructionId(0),371        loc: stmt.node.loc ?? GeneratedSource,372      };373      builder.terminateWithContinuation(terminal, continuationBlock);374      return;375    }376    case 'BlockStatement': {377      const stmt = stmtPath as NodePath<t.BlockStatement>;378      const statements = stmt.get('body');379      /**380       * Hoistable identifier bindings defined for this precise block381       * scope (excluding bindings from parent or child block scopes).382       */383      const hoistableIdentifiers: Set<t.Identifier> = new Set();384385      for (const [, binding] of Object.entries(stmt.scope.bindings)) {386        // refs to params are always valid / never need to be hoisted387        if (binding.kind !== 'param') {388          hoistableIdentifiers.add(binding.identifier);389        }390      }391392      for (const s of statements) {393        const willHoist = new Set<NodePath<t.Identifier>>();394        /*395         * If we see a hoistable identifier before its declaration, it should be hoisted just396         * before the statement that references it.397         */398        let fnDepth = s.isFunctionDeclaration() ? 1 : 0;399        const withFunctionContext = {400          enter: (): void => {401            fnDepth++;402          },403          exit: (): void => {404            fnDepth--;405          },406        };407        s.traverse({408          FunctionExpression: withFunctionContext,409          FunctionDeclaration: withFunctionContext,410          ArrowFunctionExpression: withFunctionContext,411          ObjectMethod: withFunctionContext,412          Identifier(id: NodePath<t.Identifier>) {413            const id2 = id;414            if (415              !id2.isReferencedIdentifier() &&416              // isReferencedIdentifier is broken and returns false for reassignments417              id.parent.type !== 'AssignmentExpression'418            ) {419              return;420            }421            const binding = id.scope.getBinding(id.node.name);422            /**423             * We can only hoist an identifier decl if424             * 1. the reference occurs within an inner function425             * or426             * 2. the declaration itself is hoistable427             */428            if (429              binding != null &&430              hoistableIdentifiers.has(binding.identifier) &&431              (fnDepth > 0 || binding.kind === 'hoisted')432            ) {433              willHoist.add(id);434            }435          },436        });437        /*438         * After visiting the declaration, hoisting is no longer required439         */440        s.traverse({441          Identifier(path: NodePath<t.Identifier>) {442            if (hoistableIdentifiers.has(path.node)) {443              hoistableIdentifiers.delete(path.node);444            }445          },446        });447448        // Hoist declarations that need it to the earliest point where they are needed449        for (const id of willHoist) {450          const binding = stmt.scope.getBinding(id.node.name);451          CompilerError.invariant(binding != null, {452            reason: 'Expected to find binding for hoisted identifier',453            description: `Could not find a binding for ${id.node.name}`,454            loc: id.node.loc ?? GeneratedSource,455          });456          if (builder.environment.isHoistedIdentifier(binding.identifier)) {457            // Already hoisted458            continue;459          }460461          let kind:462            | InstructionKind.Let463            | InstructionKind.HoistedConst464            | InstructionKind.HoistedLet465            | InstructionKind.HoistedFunction;466          if (binding.kind === 'const' || binding.kind === 'var') {467            kind = InstructionKind.HoistedConst;468          } else if (binding.kind === 'let') {469            kind = InstructionKind.HoistedLet;470          } else if (binding.path.isFunctionDeclaration()) {471            kind = InstructionKind.HoistedFunction;472          } else if (!binding.path.isVariableDeclarator()) {473            builder.recordError(474              new CompilerErrorDetail({475                category: ErrorCategory.Todo,476                reason: 'Unsupported declaration type for hoisting',477                description: `variable "${binding.identifier.name}" declared with ${binding.path.type}`,478                suggestions: null,479                loc: id.parentPath.node.loc ?? GeneratedSource,480              }),481            );482            continue;483          } else {484            builder.recordError(485              new CompilerErrorDetail({486                category: ErrorCategory.Todo,487                reason: 'Handle non-const declarations for hoisting',488                description: `variable "${binding.identifier.name}" declared with ${binding.kind}`,489                suggestions: null,490                loc: id.parentPath.node.loc ?? GeneratedSource,491              }),492            );493            continue;494          }495496          const identifier = builder.resolveIdentifier(id);497          CompilerError.invariant(identifier.kind === 'Identifier', {498            reason:499              'Expected hoisted binding to be a local identifier, not a global',500            loc: id.node.loc ?? GeneratedSource,501          });502          const place: Place = {503            effect: Effect.Unknown,504            identifier: identifier.identifier,505            kind: 'Identifier',506            reactive: false,507            loc: id.node.loc ?? GeneratedSource,508          };509          lowerValueToTemporary(builder, {510            kind: 'DeclareContext',511            lvalue: {512              kind,513              place,514            },515            loc: id.node.loc ?? GeneratedSource,516          });517          builder.environment.addHoistedIdentifier(binding.identifier);518        }519        lowerStatement(builder, s);520      }521522      return;523    }524    case 'BreakStatement': {525      const stmt = stmtPath as NodePath<t.BreakStatement>;526      const block = builder.lookupBreak(stmt.node.label?.name ?? null);527      builder.terminate(528        {529          kind: 'goto',530          block,531          variant: GotoVariant.Break,532          id: makeInstructionId(0),533          loc: stmt.node.loc ?? GeneratedSource,534        },535        'block',536      );537      return;538    }539    case 'ContinueStatement': {540      const stmt = stmtPath as NodePath<t.ContinueStatement>;541      const block = builder.lookupContinue(stmt.node.label?.name ?? null);542      builder.terminate(543        {544          kind: 'goto',545          block,546          variant: GotoVariant.Continue,547          id: makeInstructionId(0),548          loc: stmt.node.loc ?? GeneratedSource,549        },550        'block',551      );552      return;553    }554    case 'ForStatement': {555      const stmt = stmtPath as NodePath<t.ForStatement>;556557      const testBlock = builder.reserve('loop');558      //  Block for code following the loop559      const continuationBlock = builder.reserve('block');560561      const initBlock = builder.enter('loop', _blockId => {562        const init = stmt.get('init');563        if (init.node == null) {564          /*565           * No init expression (e.g., `for (; ...)`), add a placeholder to avoid566           * invariant about empty blocks567           */568          lowerValueToTemporary(builder, {569            kind: 'Primitive',570            value: undefined,571            loc: stmt.node.loc ?? GeneratedSource,572          });573          return {574            kind: 'goto',575            block: testBlock.id,576            variant: GotoVariant.Break,577            id: makeInstructionId(0),578            loc: stmt.node.loc ?? GeneratedSource,579          };580        }581        if (!init.isVariableDeclaration()) {582          builder.recordError(583            new CompilerErrorDetail({584              reason:585                '(BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement',586              category: ErrorCategory.Todo,587              loc: stmt.node.loc ?? null,588              suggestions: null,589            }),590          );591          // Lower the init expression as best-effort and continue592          if (init.isExpression()) {593            lowerExpressionToTemporary(builder, init as NodePath<t.Expression>);594          }595          return {596            kind: 'goto',597            block: testBlock.id,598            variant: GotoVariant.Break,599            id: makeInstructionId(0),600            loc: init.node?.loc ?? GeneratedSource,601          };602        }603        lowerStatement(builder, init);604        return {605          kind: 'goto',606          block: testBlock.id,607          variant: GotoVariant.Break,608          id: makeInstructionId(0),609          loc: init.node.loc ?? GeneratedSource,610        };611      });612613      let updateBlock: BlockId | null = null;614      const update = stmt.get('update');615      if (hasNode(update)) {616        updateBlock = builder.enter('loop', _blockId => {617          lowerExpressionToTemporary(builder, update);618          return {619            kind: 'goto',620            block: testBlock.id,621            variant: GotoVariant.Break,622            id: makeInstructionId(0),623            loc: update.node?.loc ?? GeneratedSource,624          };625        });626      }627628      const bodyBlock = builder.enter('block', _blockId => {629        return builder.loop(630          label,631          updateBlock ?? testBlock.id,632          continuationBlock.id,633          () => {634            const body = stmt.get('body');635            lowerStatement(builder, body);636            return {637              kind: 'goto',638              block: updateBlock ?? testBlock.id,639              variant: GotoVariant.Continue,640              id: makeInstructionId(0),641              loc: body.node.loc ?? GeneratedSource,642            };643          },644        );645      });646647      builder.terminateWithContinuation(648        {649          kind: 'for',650          loc: stmtNode.loc ?? GeneratedSource,651          init: initBlock,652          test: testBlock.id,653          update: updateBlock,654          loop: bodyBlock,655          fallthrough: continuationBlock.id,656          id: makeInstructionId(0),657        },658        testBlock,659      );660661      const test = stmt.get('test');662      if (test.node == null) {663        builder.recordError(664          new CompilerErrorDetail({665            reason: `(BuildHIR::lowerStatement) Handle empty test in ForStatement`,666            category: ErrorCategory.Todo,667            loc: stmt.node.loc ?? null,668            suggestions: null,669          }),670        );671        // Treat `for(;;)` as `while(true)` to keep the builder state consistent672        builder.terminateWithContinuation(673          {674            kind: 'branch',675            test: lowerValueToTemporary(builder, {676              kind: 'Primitive',677              value: true,678              loc: stmt.node.loc ?? GeneratedSource,679            }),680            consequent: bodyBlock,681            alternate: continuationBlock.id,682            fallthrough: continuationBlock.id,683            id: makeInstructionId(0),684            loc: stmt.node.loc ?? GeneratedSource,685          },686          continuationBlock,687        );688      } else {689        builder.terminateWithContinuation(690          {691            kind: 'branch',692            test: lowerExpressionToTemporary(693              builder,694              test as NodePath<t.Expression>,695            ),696            consequent: bodyBlock,697            alternate: continuationBlock.id,698            fallthrough: continuationBlock.id,699            id: makeInstructionId(0),700            loc: stmt.node.loc ?? GeneratedSource,701          },702          continuationBlock,703        );704      }705      return;706    }707    case 'WhileStatement': {708      const stmt = stmtPath as NodePath<t.WhileStatement>;709      //  Block used to evaluate whether to (re)enter or exit the loop710      const conditionalBlock = builder.reserve('loop');711      //  Block for code following the loop712      const continuationBlock = builder.reserve('block');713      //  Loop body714      const loopBlock = builder.enter('block', _blockId => {715        return builder.loop(716          label,717          conditionalBlock.id,718          continuationBlock.id,719          () => {720            const body = stmt.get('body');721            lowerStatement(builder, body);722            return {723              kind: 'goto',724              block: conditionalBlock.id,725              variant: GotoVariant.Continue,726              id: makeInstructionId(0),727              loc: body.node.loc ?? GeneratedSource,728            };729          },730        );731      });732      /*733       * The code leading up to the loop must jump to the conditional block,734       * to evaluate whether to enter the loop or bypass to the continuation.735       */736      const loc = stmt.node.loc ?? GeneratedSource;737      builder.terminateWithContinuation(738        {739          kind: 'while',740          loc,741          test: conditionalBlock.id,742          loop: loopBlock,743          fallthrough: continuationBlock.id,744          id: makeInstructionId(0),745        },746        conditionalBlock,747      );748      const test = lowerExpressionToTemporary(builder, stmt.get('test'));749      const terminal: BranchTerminal = {750        kind: 'branch',751        test,752        consequent: loopBlock,753        alternate: continuationBlock.id,754        fallthrough: conditionalBlock.id,755        id: makeInstructionId(0),756        loc: stmt.node.loc ?? GeneratedSource,757      };758      //  Complete the conditional and continue with code after the loop759      builder.terminateWithContinuation(terminal, continuationBlock);760      return;761    }762    case 'LabeledStatement': {763      const stmt = stmtPath as NodePath<t.LabeledStatement>;764      const label = stmt.node.label.name;765      const body = stmt.get('body');766      switch (body.node.type) {767        case 'ForInStatement':768        case 'ForOfStatement':769        case 'ForStatement':770        case 'WhileStatement':771        case 'DoWhileStatement': {772          /*773           * labeled loops are special because of continue, so push the label774           * down775           */776          lowerStatement(builder, stmt.get('body'), label);777          break;778        }779        default: {780          /*781           * All other statements create a continuation block to allow `break`,782           * explicitly *don't* pass the label down783           */784          const continuationBlock = builder.reserve('block');785          const block = builder.enter('block', () => {786            const body = stmt.get('body');787            builder.label(label, continuationBlock.id, () => {788              lowerStatement(builder, body);789            });790            return {791              kind: 'goto',792              block: continuationBlock.id,793              variant: GotoVariant.Break,794              id: makeInstructionId(0),795              loc: body.node.loc ?? GeneratedSource,796            };797          });798          builder.terminateWithContinuation(799            {800              kind: 'label',801              block,802              fallthrough: continuationBlock.id,803              id: makeInstructionId(0),804              loc: stmt.node.loc ?? GeneratedSource,805            },806            continuationBlock,807          );808        }809      }810      return;811    }812    case 'SwitchStatement': {813      const stmt = stmtPath as NodePath<t.SwitchStatement>;814      //  Block following the switch815      const continuationBlock = builder.reserve('block');816      /*817       * The goto target for any cases that fallthrough, which initially starts818       * as the continuation block and is then updated as we iterate through cases819       * in reverse order.820       */821      let fallthrough = continuationBlock.id;822      /*823       * Iterate through cases in reverse order, so that previous blocks can fallthrough824       * to successors825       */826      const cases: Array<Case> = [];827      let hasDefault = false;828      for (let ii = stmt.get('cases').length - 1; ii >= 0; ii--) {829        const case_: NodePath<t.SwitchCase> = stmt.get('cases')[ii];830        const testExpr = case_.get('test');831        if (testExpr.node == null) {832          if (hasDefault) {833            builder.recordError(834              new CompilerErrorDetail({835                reason: `Expected at most one \`default\` branch in a switch statement, this code should have failed to parse`,836                category: ErrorCategory.Syntax,837                loc: case_.node.loc ?? null,838                suggestions: null,839              }),840            );841            break;842          }843          hasDefault = true;844        }845        const block = builder.enter('block', _blockId => {846          return builder.switch(label, continuationBlock.id, () => {847            case_848              .get('consequent')849              .forEach(consequent => lowerStatement(builder, consequent));850            /*851             * always generate a fallthrough to the next block, this may be dead code852             * if there was an explicit break, but if so it will be pruned later.853             */854            return {855              kind: 'goto',856              block: fallthrough,857              variant: GotoVariant.Break,858              id: makeInstructionId(0),859              loc: case_.node.loc ?? GeneratedSource,860            };861          });862        });863        let test: Place | null = null;864        if (hasNode(testExpr)) {865          test = lowerReorderableExpression(builder, testExpr);866        }867        cases.push({868          test,869          block,870        });871        fallthrough = block;872      }873      /*874       * it doesn't matter for our analysis purposes, but reverse the order of the cases875       * back to the original to make it match the original code/intent.876       */877      cases.reverse();878      /*879       * If there wasn't an explicit default case, generate one to model the fact that execution880       * could bypass any of the other cases and jump directly to the continuation.881       */882      if (!hasDefault) {883        cases.push({test: null, block: continuationBlock.id});884      }885886      const test = lowerExpressionToTemporary(887        builder,888        stmt.get('discriminant'),889      );890      builder.terminateWithContinuation(891        {892          kind: 'switch',893          test,894          cases,895          fallthrough: continuationBlock.id,896          id: makeInstructionId(0),897          loc: stmt.node.loc ?? GeneratedSource,898        },899        continuationBlock,900      );901      return;902    }903    case 'VariableDeclaration': {904      const stmt = stmtPath as NodePath<t.VariableDeclaration>;905      const nodeKind: t.VariableDeclaration['kind'] = stmt.node.kind;906      if (nodeKind === 'var') {907        builder.recordError(908          new CompilerErrorDetail({909            reason: `(BuildHIR::lowerStatement) Handle ${nodeKind} kinds in VariableDeclaration`,910            category: ErrorCategory.Todo,911            loc: stmt.node.loc ?? null,912            suggestions: null,913          }),914        );915        // Treat `var` as `let` so references to the variable don't break916      }917      const kind =918        nodeKind === 'let' || nodeKind === 'var'919          ? InstructionKind.Let920          : InstructionKind.Const;921      for (const declaration of stmt.get('declarations')) {922        const id = declaration.get('id');923        const init = declaration.get('init');924        if (hasNode(init)) {925          const value = lowerExpressionToTemporary(builder, init);926          lowerAssignment(927            builder,928            stmt.node.loc ?? GeneratedSource,929            kind,930            id,931            value,932            id.isObjectPattern() || id.isArrayPattern()933              ? 'Destructure'934              : 'Assignment',935          );936        } else if (id.isIdentifier()) {937          const binding = builder.resolveIdentifier(id);938          if (binding.kind !== 'Identifier') {939            builder.recordError(940              new CompilerErrorDetail({941                reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,942                category: ErrorCategory.Invariant,943                loc: id.node.loc ?? null,944                suggestions: null,945              }),946            );947          } else {948            const place: Place = {949              effect: Effect.Unknown,950              identifier: binding.identifier,951              kind: 'Identifier',952              reactive: false,953              loc: id.node.loc ?? GeneratedSource,954            };955            if (builder.isContextIdentifier(id)) {956              if (kind === InstructionKind.Const) {957                const declRangeStart = declaration.parentPath.node.start!;958                builder.recordError(959                  new CompilerErrorDetail({960                    reason: `Expect \`const\` declaration not to be reassigned`,961                    category: ErrorCategory.Syntax,962                    loc: id.node.loc ?? null,963                    suggestions: [964                      {965                        description: 'Change to a `let` declaration',966                        op: CompilerSuggestionOperation.Replace,967                        range: [declRangeStart, declRangeStart + 5], // "const".length968                        text: 'let',969                      },970                    ],971                  }),972                );973              }974              lowerValueToTemporary(builder, {975                kind: 'DeclareContext',976                lvalue: {977                  kind: InstructionKind.Let,978                  place,979                },980                loc: id.node.loc ?? GeneratedSource,981              });982            } else {983              const typeAnnotation = id.get('typeAnnotation');984              let type: t.FlowType | t.TSType | null;985              if (typeAnnotation.isTSTypeAnnotation()) {986                const typePath = typeAnnotation.get('typeAnnotation');987                type = typePath.node;988              } else if (typeAnnotation.isTypeAnnotation()) {989                const typePath = typeAnnotation.get('typeAnnotation');990                type = typePath.node;991              } else {992                type = null;993              }994              lowerValueToTemporary(builder, {995                kind: 'DeclareLocal',996                lvalue: {997                  kind,998                  place,999                },1000                type,1001                loc: id.node.loc ?? GeneratedSource,1002              });1003            }1004          }1005        } else {1006          builder.recordError(1007            new CompilerErrorDetail({1008              reason: `Expected variable declaration to be an identifier if no initializer was provided`,1009              description: `Got a \`${id.type}\``,1010              category: ErrorCategory.Syntax,1011              loc: stmt.node.loc ?? null,1012              suggestions: null,1013            }),1014          );1015        }1016      }1017      return;1018    }1019    case 'ExpressionStatement': {1020      const stmt = stmtPath as NodePath<t.ExpressionStatement>;1021      const expression = stmt.get('expression');1022      lowerExpressionToTemporary(builder, expression);1023      return;1024    }1025    case 'DoWhileStatement': {1026      const stmt = stmtPath as NodePath<t.DoWhileStatement>;1027      //  Block used to evaluate whether to (re)enter or exit the loop1028      const conditionalBlock = builder.reserve('loop');1029      //  Block for code following the loop1030      const continuationBlock = builder.reserve('block');1031      //  Loop body, executed at least once uncondtionally prior to exit1032      const loopBlock = builder.enter('block', _loopBlockId => {1033        return builder.loop(1034          label,1035          conditionalBlock.id,1036          continuationBlock.id,1037          () => {1038            const body = stmt.get('body');1039            lowerStatement(builder, body);1040            return {1041              kind: 'goto',1042              block: conditionalBlock.id,1043              variant: GotoVariant.Continue,1044              id: makeInstructionId(0),1045              loc: body.node.loc ?? GeneratedSource,1046            };1047          },1048        );1049      });1050      /*1051       * Jump to the conditional block to evaluate whether to (re)enter the loop or exit to the1052       * continuation block.1053       */1054      const loc = stmt.node.loc ?? GeneratedSource;1055      builder.terminateWithContinuation(1056        {1057          kind: 'do-while',1058          loc,1059          test: conditionalBlock.id,1060          loop: loopBlock,1061          fallthrough: continuationBlock.id,1062          id: makeInstructionId(0),1063        },1064        conditionalBlock,1065      );1066      /*1067       * The conditional block is empty and exists solely as conditional for1068       * (re)entering or exiting the loop1069       */1070      const test = lowerExpressionToTemporary(builder, stmt.get('test'));1071      const terminal: BranchTerminal = {1072        kind: 'branch',1073        test,1074        consequent: loopBlock,1075        alternate: continuationBlock.id,1076        fallthrough: conditionalBlock.id,1077        id: makeInstructionId(0),1078        loc,1079      };1080      //  Complete the conditional and continue with code after the loop1081      builder.terminateWithContinuation(terminal, continuationBlock);1082      return;1083    }1084    case 'FunctionDeclaration': {1085      const stmt = stmtPath as NodePath<t.FunctionDeclaration>;1086      stmt.skip();1087      CompilerError.invariant(stmt.get('id').type === 'Identifier', {1088        reason: 'function declarations must have a name',1089        loc: stmt.node.loc ?? GeneratedSource,1090      });1091      const id = stmt.get('id') as NodePath<t.Identifier>;10921093      const fn = lowerValueToTemporary(1094        builder,1095        lowerFunctionToValue(builder, stmt),1096      );1097      lowerAssignment(1098        builder,1099        stmt.node.loc ?? GeneratedSource,1100        InstructionKind.Function,1101        id,1102        fn,1103        'Assignment',1104      );11051106      return;1107    }1108    case 'ForOfStatement': {1109      const stmt = stmtPath as NodePath<t.ForOfStatement>;1110      const continuationBlock = builder.reserve('block');1111      const initBlock = builder.reserve('loop');1112      const testBlock = builder.reserve('loop');11131114      if (stmt.node.await) {1115        builder.recordError(1116          new CompilerErrorDetail({1117            reason: `(BuildHIR::lowerStatement) Handle for-await loops`,1118            category: ErrorCategory.Todo,1119            loc: stmt.node.loc ?? null,1120            suggestions: null,1121          }),1122        );1123        return;1124      }11251126      const loopBlock = builder.enter('block', _blockId => {1127        return builder.loop(label, initBlock.id, continuationBlock.id, () => {1128          const body = stmt.get('body');1129          lowerStatement(builder, body);1130          return {1131            kind: 'goto',1132            block: initBlock.id,1133            variant: GotoVariant.Continue,1134            id: makeInstructionId(0),1135            loc: body.node.loc ?? GeneratedSource,1136          };1137        });1138      });11391140      const loc = stmt.node.loc ?? GeneratedSource;1141      const value = lowerExpressionToTemporary(builder, stmt.get('right'));1142      builder.terminateWithContinuation(1143        {1144          kind: 'for-of',1145          loc,1146          init: initBlock.id,1147          test: testBlock.id,1148          loop: loopBlock,1149          fallthrough: continuationBlock.id,1150          id: makeInstructionId(0),1151        },1152        initBlock,1153      );11541155      /*1156       * The init of a ForOf statement is compound over a left (VariableDeclaration | LVal) and1157       * right (Expression), so we synthesize a new InstrValue and assignment (potentially multiple1158       * instructions when we handle other syntax like Patterns)1159       */1160      const iterator = lowerValueToTemporary(builder, {1161        kind: 'GetIterator',1162        loc: value.loc,1163        collection: {...value},1164      });1165      builder.terminateWithContinuation(1166        {1167          id: makeInstructionId(0),1168          kind: 'goto',1169          block: testBlock.id,1170          variant: GotoVariant.Break,1171          loc: stmt.node.loc ?? GeneratedSource,1172        },1173        testBlock,1174      );11751176      const left = stmt.get('left');1177      const leftLoc = left.node.loc ?? GeneratedSource;1178      let test: Place;1179      const advanceIterator = lowerValueToTemporary(builder, {1180        kind: 'IteratorNext',1181        loc: leftLoc,1182        iterator: {...iterator},1183        collection: {...value},1184      });1185      if (left.isVariableDeclaration()) {1186        const declarations = left.get('declarations');1187        CompilerError.invariant(declarations.length === 1, {1188          reason: `Expected only one declaration in the init of a ForOfStatement, got ${declarations.length}`,1189          loc: left.node.loc ?? GeneratedSource,1190        });1191        const id = declarations[0].get('id');1192        const assign = lowerAssignment(1193          builder,1194          leftLoc,1195          InstructionKind.Let,1196          id,1197          advanceIterator,1198          'Assignment',1199        );1200        test = lowerValueToTemporary(builder, assign);1201      } else {1202        CompilerError.invariant(left.isLVal(), {1203          reason: 'Expected ForOf init to be a variable declaration or lval',1204          loc: leftLoc,1205        });1206        const assign = lowerAssignment(1207          builder,1208          leftLoc,1209          InstructionKind.Reassign,1210          left,1211          advanceIterator,1212          'Assignment',1213        );1214        test = lowerValueToTemporary(builder, assign);1215      }1216      builder.terminateWithContinuation(1217        {1218          id: makeInstructionId(0),1219          kind: 'branch',1220          test,1221          consequent: loopBlock,1222          alternate: continuationBlock.id,1223          loc: stmt.node.loc ?? GeneratedSource,1224          fallthrough: continuationBlock.id,1225        },1226        continuationBlock,1227      );1228      return;1229    }1230    case 'ForInStatement': {1231      const stmt = stmtPath as NodePath<t.ForInStatement>;1232      const continuationBlock = builder.reserve('block');1233      const initBlock = builder.reserve('loop');12341235      const loopBlock = builder.enter('block', _blockId => {1236        return builder.loop(label, initBlock.id, continuationBlock.id, () => {1237          const body = stmt.get('body');1238          lowerStatement(builder, body);1239          return {1240            kind: 'goto',1241            block: initBlock.id,1242            variant: GotoVariant.Continue,1243            id: makeInstructionId(0),1244            loc: body.node.loc ?? GeneratedSource,1245          };1246        });1247      });12481249      const loc = stmt.node.loc ?? GeneratedSource;1250      const value = lowerExpressionToTemporary(builder, stmt.get('right'));1251      builder.terminateWithContinuation(1252        {1253          kind: 'for-in',1254          loc,1255          init: initBlock.id,1256          loop: loopBlock,1257          fallthrough: continuationBlock.id,1258          id: makeInstructionId(0),1259        },1260        initBlock,1261      );12621263      /*1264       * The init of a ForIn statement is compound over a left (VariableDeclaration | LVal) and1265       * right (Expression), so we synthesize a new InstrValue and assignment (potentially multiple1266       * instructions when we handle other syntax like Patterns)1267       */1268      const left = stmt.get('left');1269      const leftLoc = left.node.loc ?? GeneratedSource;1270      let test: Place;1271      const nextPropertyTemp = lowerValueToTemporary(builder, {1272        kind: 'NextPropertyOf',1273        loc: leftLoc,1274        value,1275      });1276      if (left.isVariableDeclaration()) {1277        const declarations = left.get('declarations');1278        CompilerError.invariant(declarations.length === 1, {1279          reason: `Expected only one declaration in the init of a ForInStatement, got ${declarations.length}`,1280          loc: left.node.loc ?? GeneratedSource,1281        });1282        const id = declarations[0].get('id');1283        const assign = lowerAssignment(1284          builder,1285          leftLoc,1286          InstructionKind.Let,1287          id,1288          nextPropertyTemp,1289          'Assignment',1290        );1291        test = lowerValueToTemporary(builder, assign);1292      } else {1293        CompilerError.invariant(left.isLVal(), {1294          reason: 'Expected ForIn init to be a variable declaration or lval',1295          loc: leftLoc,1296        });1297        const assign = lowerAssignment(1298          builder,1299          leftLoc,1300          InstructionKind.Reassign,1301          left,1302          nextPropertyTemp,1303          'Assignment',1304        );1305        test = lowerValueToTemporary(builder, assign);1306      }1307      builder.terminateWithContinuation(1308        {1309          id: makeInstructionId(0),1310          kind: 'branch',1311          test,1312          consequent: loopBlock,1313          alternate: continuationBlock.id,1314          fallthrough: continuationBlock.id,1315          loc: stmt.node.loc ?? GeneratedSource,1316        },1317        continuationBlock,1318      );1319      return;1320    }1321    case 'DebuggerStatement': {1322      const stmt = stmtPath as NodePath<t.DebuggerStatement>;1323      const loc = stmt.node.loc ?? GeneratedSource;1324      builder.push({1325        id: makeInstructionId(0),1326        lvalue: buildTemporaryPlace(builder, loc),1327        value: {1328          kind: 'Debugger',1329          loc,1330        },1331        effects: null,1332        loc,1333      });1334      return;1335    }1336    case 'EmptyStatement': {1337      return;1338    }1339    case 'TryStatement': {1340      const stmt = stmtPath as NodePath<t.TryStatement>;1341      const continuationBlock = builder.reserve('block');13421343      const handlerPath = stmt.get('handler');1344      if (!hasNode(handlerPath)) {1345        builder.recordError(1346          new CompilerErrorDetail({1347            reason: `(BuildHIR::lowerStatement) Handle TryStatement without a catch clause`,1348            category: ErrorCategory.Todo,1349            loc: stmt.node.loc ?? null,1350            suggestions: null,1351          }),1352        );1353        return;1354      }1355      if (hasNode(stmt.get('finalizer'))) {1356        builder.recordError(1357          new CompilerErrorDetail({1358            reason: `(BuildHIR::lowerStatement) Handle TryStatement with a finalizer ('finally') clause`,1359            category: ErrorCategory.Todo,1360            loc: stmt.node.loc ?? null,1361            suggestions: null,1362          }),1363        );1364      }13651366      const handlerBindingPath = handlerPath.get('param');1367      let handlerBinding: {1368        place: Place;1369        path: NodePath<t.Identifier | t.ArrayPattern | t.ObjectPattern>;1370      } | null = null;1371      if (hasNode(handlerBindingPath)) {1372        const place: Place = {1373          kind: 'Identifier',1374          identifier: builder.makeTemporary(1375            handlerBindingPath.node.loc ?? GeneratedSource,1376          ),1377          effect: Effect.Unknown,1378          reactive: false,1379          loc: handlerBindingPath.node.loc ?? GeneratedSource,1380        };1381        promoteTemporary(place.identifier);1382        lowerValueToTemporary(builder, {1383          kind: 'DeclareLocal',1384          lvalue: {1385            kind: InstructionKind.Catch,1386            place: {...place},1387          },1388          type: null,1389          loc: handlerBindingPath.node.loc ?? GeneratedSource,1390        });13911392        handlerBinding = {1393          path: handlerBindingPath,1394          place,1395        };1396      }13971398      const handler = builder.enter('catch', _blockId => {1399        if (handlerBinding !== null) {1400          lowerAssignment(1401            builder,1402            handlerBinding.path.node.loc ?? GeneratedSource,1403            InstructionKind.Catch,1404            handlerBinding.path,1405            {...handlerBinding.place},1406            'Assignment',1407          );1408        }1409        lowerStatement(builder, handlerPath.get('body'));1410        return {1411          kind: 'goto',1412          block: continuationBlock.id,1413          variant: GotoVariant.Break,1414          id: makeInstructionId(0),1415          loc: handlerPath.node.loc ?? GeneratedSource,1416        };1417      });14181419      const block = builder.enter('block', _blockId => {1420        const block = stmt.get('block');1421        builder.enterTryCatch(handler, () => {1422          lowerStatement(builder, block);1423        });1424        return {1425          kind: 'goto',1426          block: continuationBlock.id,1427          variant: GotoVariant.Try,1428          id: makeInstructionId(0),1429          loc: block.node.loc ?? GeneratedSource,1430        };1431      });14321433      builder.terminateWithContinuation(1434        {1435          kind: 'try',1436          block,1437          handlerBinding:1438            handlerBinding !== null ? {...handlerBinding.place} : null,1439          handler,1440          fallthrough: continuationBlock.id,1441          id: makeInstructionId(0),1442          loc: stmt.node.loc ?? GeneratedSource,1443        },1444        continuationBlock,1445      );14461447      return;1448    }1449    case 'WithStatement': {1450      builder.recordError(1451        new CompilerErrorDetail({1452          reason: `JavaScript 'with' syntax is not supported`,1453          description: `'with' syntax is considered deprecated and removed from JavaScript standards, consider alternatives`,1454          category: ErrorCategory.UnsupportedSyntax,1455          loc: stmtPath.node.loc ?? null,1456          suggestions: null,1457        }),1458      );1459      lowerValueToTemporary(builder, {1460        kind: 'UnsupportedNode',1461        loc: stmtPath.node.loc ?? GeneratedSource,1462        node: stmtPath.node,1463      });1464      return;1465    }1466    case 'ClassDeclaration': {1467      /**1468       * In theory we could support inline class declarations, but this is rare enough in practice1469       * and complex enough to support that we don't anticipate supporting anytime soon. Developers1470       * are encouraged to lift classes out of component/hook declarations.1471       */1472      builder.recordError(1473        new CompilerErrorDetail({1474          reason: 'Inline `class` declarations are not supported',1475          description: `Move class declarations outside of components/hooks`,1476          category: ErrorCategory.UnsupportedSyntax,1477          loc: stmtPath.node.loc ?? null,1478          suggestions: null,1479        }),1480      );1481      lowerValueToTemporary(builder, {1482        kind: 'UnsupportedNode',1483        loc: stmtPath.node.loc ?? GeneratedSource,1484        node: stmtPath.node,1485      });1486      return;1487    }1488    case 'EnumDeclaration':1489    case 'TSEnumDeclaration': {1490      lowerValueToTemporary(builder, {1491        kind: 'UnsupportedNode',1492        loc: stmtPath.node.loc ?? GeneratedSource,1493        node: stmtPath.node,1494      });1495      return;1496    }1497    case 'ExportAllDeclaration':1498    case 'ExportDefaultDeclaration':1499    case 'ExportNamedDeclaration':1500    case 'ImportDeclaration':1501    case 'TSExportAssignment':1502    case 'TSImportEqualsDeclaration': {1503      builder.recordError(1504        new CompilerErrorDetail({1505          reason:1506            'JavaScript `import` and `export` statements may only appear at the top level of a module',1507          category: ErrorCategory.Syntax,1508          loc: stmtPath.node.loc ?? null,1509          suggestions: null,1510        }),1511      );1512      lowerValueToTemporary(builder, {1513        kind: 'UnsupportedNode',1514        loc: stmtPath.node.loc ?? GeneratedSource,1515        node: stmtPath.node,1516      });1517      return;1518    }1519    case 'TSNamespaceExportDeclaration': {1520      builder.recordError(1521        new CompilerErrorDetail({1522          reason:1523            'TypeScript `namespace` statements may only appear at the top level of a module',1524          category: ErrorCategory.Syntax,1525          loc: stmtPath.node.loc ?? null,1526          suggestions: null,1527        }),1528      );1529      lowerValueToTemporary(builder, {1530        kind: 'UnsupportedNode',1531        loc: stmtPath.node.loc ?? GeneratedSource,1532        node: stmtPath.node,1533      });1534      return;1535    }1536    case 'DeclareClass':1537    case 'DeclareExportAllDeclaration':1538    case 'DeclareExportDeclaration':1539    case 'DeclareFunction':1540    case 'DeclareInterface':1541    case 'DeclareModule':1542    case 'DeclareModuleExports':1543    case 'DeclareOpaqueType':1544    case 'DeclareTypeAlias':1545    case 'DeclareVariable':1546    case 'InterfaceDeclaration':1547    case 'OpaqueType':1548    case 'TSDeclareFunction':1549    case 'TSInterfaceDeclaration':1550    case 'TSModuleDeclaration':1551    case 'TSTypeAliasDeclaration':1552    case 'TypeAlias': {1553      // We do not preserve type annotations/syntax through transformation1554      return;1555    }1556    default: {1557      return assertExhaustive(1558        stmtNode,1559        `Unsupported statement kind '${1560          (stmtNode as any as NodePath<t.Statement>).type1561        }'`,1562      );1563    }1564  }1565}15661567function lowerObjectMethod(1568  builder: HIRBuilder,1569  property: NodePath<t.ObjectMethod>,1570): InstructionValue {1571  const loc = property.node.loc ?? GeneratedSource;1572  const loweredFunc = lowerFunction(builder, property);15731574  return {1575    kind: 'ObjectMethod',1576    loc,1577    loweredFunc,1578  };1579}15801581function lowerObjectPropertyKey(1582  builder: HIRBuilder,1583  property: NodePath<t.ObjectProperty | t.ObjectMethod>,1584): ObjectPropertyKey | null {1585  const key = property.get('key');1586  if (key.isStringLiteral()) {1587    return {1588      kind: 'string',1589      name: key.node.value,1590    };1591  } else if (property.node.computed && key.isExpression()) {1592    const place = lowerExpressionToTemporary(builder, key);1593    return {1594      kind: 'computed',1595      name: place,1596    };1597  } else if (key.isIdentifier()) {1598    return {1599      kind: 'identifier',1600      name: key.node.name,1601    };1602  } else if (key.isNumericLiteral()) {1603    return {1604      kind: 'identifier',1605      name: String(key.node.value),1606    };1607  }16081609  builder.recordError(1610    new CompilerErrorDetail({1611      reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,1612      category: ErrorCategory.Todo,1613      loc: key.node.loc ?? null,1614      suggestions: null,1615    }),1616  );1617  return null;1618}16191620function lowerExpression(1621  builder: HIRBuilder,1622  exprPath: NodePath<t.Expression>,1623): InstructionValue {1624  const exprNode = exprPath.node;1625  const exprLoc = exprNode.loc ?? GeneratedSource;1626  switch (exprNode.type) {1627    case 'Identifier': {1628      const expr = exprPath as NodePath<t.Identifier>;1629      const place = lowerIdentifier(builder, expr);1630      return {1631        kind: getLoadKind(builder, expr),1632        place,1633        loc: exprLoc,1634      };1635    }1636    case 'NullLiteral': {1637      return {1638        kind: 'Primitive',1639        value: null,1640        loc: exprLoc,1641      };1642    }1643    case 'BooleanLiteral':1644    case 'NumericLiteral':1645    case 'StringLiteral': {1646      const expr = exprPath as NodePath<1647        t.StringLiteral | t.BooleanLiteral | t.NumericLiteral1648      >;1649      const value = expr.node.value;1650      return {1651        kind: 'Primitive',1652        value,1653        loc: exprLoc,1654      };1655    }1656    case 'ObjectExpression': {1657      const expr = exprPath as NodePath<t.ObjectExpression>;1658      const propertyPaths = expr.get('properties');1659      const properties: Array<ObjectProperty | SpreadPattern> = [];1660      for (const propertyPath of propertyPaths) {1661        if (propertyPath.isObjectProperty()) {1662          const loweredKey = lowerObjectPropertyKey(builder, propertyPath);1663          if (!loweredKey) {1664            continue;1665          }1666          const valuePath = propertyPath.get('value');1667          if (!valuePath.isExpression()) {1668            builder.recordError(1669              new CompilerErrorDetail({1670                reason: `(BuildHIR::lowerExpression) Handle ${valuePath.type} values in ObjectExpression`,1671                category: ErrorCategory.Todo,1672                loc: valuePath.node.loc ?? null,1673                suggestions: null,1674              }),1675            );1676            continue;1677          }1678          const value = lowerExpressionToTemporary(builder, valuePath);1679          properties.push({1680            kind: 'ObjectProperty',1681            type: 'property',1682            place: value,1683            key: loweredKey,1684          });1685        } else if (propertyPath.isSpreadElement()) {1686          const place = lowerExpressionToTemporary(1687            builder,1688            propertyPath.get('argument'),1689          );1690          properties.push({1691            kind: 'Spread',1692            place,1693          });1694        } else if (propertyPath.isObjectMethod()) {1695          if (propertyPath.node.kind !== 'method') {1696            builder.recordError(1697              new CompilerErrorDetail({1698                reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.node.kind} functions in ObjectExpression`,1699                category: ErrorCategory.Todo,1700                loc: propertyPath.node.loc ?? null,1701                suggestions: null,1702              }),1703            );1704            continue;1705          }1706          const method = lowerObjectMethod(builder, propertyPath);1707          const place = lowerValueToTemporary(builder, method);1708          const loweredKey = lowerObjectPropertyKey(builder, propertyPath);1709          if (!loweredKey) {1710            continue;1711          }1712          properties.push({1713            kind: 'ObjectProperty',1714            type: 'method',1715            place,1716            key: loweredKey,1717          });1718        } else {1719          builder.recordError(1720            new CompilerErrorDetail({1721              reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.type} properties in ObjectExpression`,1722              category: ErrorCategory.Todo,1723              loc: propertyPath.node.loc ?? null,1724              suggestions: null,1725            }),1726          );1727          continue;1728        }1729      }1730      return {1731        kind: 'ObjectExpression',1732        properties,1733        loc: exprLoc,1734      };1735    }1736    case 'ArrayExpression': {1737      const expr = exprPath as NodePath<t.ArrayExpression>;1738      let elements: ArrayExpression['elements'] = [];1739      for (const element of expr.get('elements')) {1740        if (element.node == null) {1741          elements.push({1742            kind: 'Hole',1743          });1744          continue;1745        } else if (element.isExpression()) {1746          elements.push(lowerExpressionToTemporary(builder, element));1747        } else if (element.isSpreadElement()) {1748          const place = lowerExpressionToTemporary(1749            builder,1750            element.get('argument'),1751          );1752          elements.push({kind: 'Spread', place});1753        } else {1754          builder.recordError(1755            new CompilerErrorDetail({1756              reason: `(BuildHIR::lowerExpression) Handle ${element.type} elements in ArrayExpression`,1757              category: ErrorCategory.Todo,1758              loc: element.node.loc ?? null,1759              suggestions: null,1760            }),1761          );1762          continue;1763        }1764      }1765      return {1766        kind: 'ArrayExpression',1767        elements,1768        loc: exprLoc,1769      };1770    }1771    case 'NewExpression': {1772      const expr = exprPath as NodePath<t.NewExpression>;1773      const calleePath = expr.get('callee');1774      if (!calleePath.isExpression()) {1775        builder.recordError(1776          new CompilerErrorDetail({1777            reason: `Expected an expression as the \`new\` expression receiver (v8 intrinsics are not supported)`,1778            description: `Got a \`${calleePath.node.type}\``,1779            category: ErrorCategory.Syntax,1780            loc: calleePath.node.loc ?? null,1781            suggestions: null,1782          }),1783        );1784        return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};1785      }1786      const callee = lowerExpressionToTemporary(builder, calleePath);1787      const args = lowerArguments(builder, expr.get('arguments'));17881789      return {1790        kind: 'NewExpression',1791        callee,1792        args,1793        loc: exprLoc,1794      };1795    }1796    case 'OptionalCallExpression': {1797      const expr = exprPath as NodePath<t.OptionalCallExpression>;1798      return lowerOptionalCallExpression(builder, expr, null);1799    }1800    case 'CallExpression': {1801      const expr = exprPath as NodePath<t.CallExpression>;1802      const calleePath = expr.get('callee');1803      if (!calleePath.isExpression()) {1804        builder.recordError(1805          new CompilerErrorDetail({1806            reason: `Expected Expression, got ${calleePath.type} in CallExpression (v8 intrinsics not supported). This error is likely caused by a bug in React Compiler. Please file an issue`,1807            category: ErrorCategory.Todo,1808            loc: calleePath.node.loc ?? null,1809            suggestions: null,1810          }),1811        );1812        return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};1813      }1814      if (calleePath.isMemberExpression()) {1815        const memberExpr = lowerMemberExpression(builder, calleePath);1816        const propertyPlace = lowerValueToTemporary(builder, memberExpr.value);1817        const args = lowerArguments(builder, expr.get('arguments'));1818        return {1819          kind: 'MethodCall',1820          receiver: memberExpr.object,1821          property: {...propertyPlace},1822          args,1823          loc: exprLoc,1824        };1825      } else {1826        const callee = lowerExpressionToTemporary(builder, calleePath);1827        const args = lowerArguments(builder, expr.get('arguments'));1828        return {1829          kind: 'CallExpression',1830          callee,1831          args,1832          loc: exprLoc,1833        };1834      }1835    }1836    case 'BinaryExpression': {1837      const expr = exprPath as NodePath<t.BinaryExpression>;1838      const leftPath = expr.get('left');1839      if (!leftPath.isExpression()) {1840        builder.recordError(1841          new CompilerErrorDetail({1842            reason: `(BuildHIR::lowerExpression) Expected Expression, got ${leftPath.type} lval in BinaryExpression`,1843            category: ErrorCategory.Todo,1844            loc: leftPath.node.loc ?? null,1845            suggestions: null,1846          }),1847        );1848        return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};1849      }1850      const left = lowerExpressionToTemporary(builder, leftPath);1851      const right = lowerExpressionToTemporary(builder, expr.get('right'));1852      const operator = expr.node.operator;1853      if (operator === '|>') {1854        builder.recordError(1855          new CompilerErrorDetail({1856            reason: `(BuildHIR::lowerExpression) Pipe operator not supported`,1857            category: ErrorCategory.Todo,1858            loc: leftPath.node.loc ?? null,1859            suggestions: null,1860          }),1861        );1862        return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};1863      }1864      return {1865        kind: 'BinaryExpression',1866        operator,1867        left,1868        right,1869        loc: exprLoc,1870      };1871    }1872    case 'SequenceExpression': {1873      const expr = exprPath as NodePath<t.SequenceExpression>;1874      const exprLoc = expr.node.loc ?? GeneratedSource;18751876      const continuationBlock = builder.reserve(builder.currentBlockKind());1877      const place = buildTemporaryPlace(builder, exprLoc);18781879      const sequenceBlock = builder.enter('sequence', _ => {1880        let last: Place | null = null;1881        for (const item of expr.get('expressions')) {1882          last = lowerExpressionToTemporary(builder, item);1883        }1884        if (last === null) {1885          builder.recordError(1886            new CompilerErrorDetail({1887              reason: `Expected sequence expression to have at least one expression`,1888              category: ErrorCategory.Syntax,1889              loc: expr.node.loc ?? null,1890              suggestions: null,1891            }),1892          );1893        } else {1894          lowerValueToTemporary(builder, {1895            kind: 'StoreLocal',1896            lvalue: {kind: InstructionKind.Const, place: {...place}},1897            value: last,1898            type: null,1899            loc: exprLoc,1900          });1901        }1902        return {1903          kind: 'goto',1904          id: makeInstructionId(0),1905          block: continuationBlock.id,1906          loc: exprLoc,1907          variant: GotoVariant.Break,1908        };1909      });19101911      builder.terminateWithContinuation(1912        {1913          kind: 'sequence',1914          block: sequenceBlock,1915          fallthrough: continuationBlock.id,1916          id: makeInstructionId(0),1917          loc: exprLoc,1918        },1919        continuationBlock,1920      );1921      return {kind: 'LoadLocal', place, loc: place.loc};1922    }1923    case 'ConditionalExpression': {1924      const expr = exprPath as NodePath<t.ConditionalExpression>;1925      const exprLoc = expr.node.loc ?? GeneratedSource;19261927      //  Block for code following the if1928      const continuationBlock = builder.reserve(builder.currentBlockKind());1929      const testBlock = builder.reserve('value');1930      const place = buildTemporaryPlace(builder, exprLoc);19311932      //  Block for the consequent (if the test is truthy)1933      const consequentBlock = builder.enter('value', _blockId => {1934        const consequentPath = expr.get('consequent');1935        const consequent = lowerExpressionToTemporary(builder, consequentPath);1936        lowerValueToTemporary(builder, {1937          kind: 'StoreLocal',1938          lvalue: {kind: InstructionKind.Const, place: {...place}},1939          value: consequent,1940          type: null,1941          loc: exprLoc,1942        });1943        return {1944          kind: 'goto',1945          block: continuationBlock.id,1946          variant: GotoVariant.Break,1947          id: makeInstructionId(0),1948          loc: consequentPath.node.loc ?? GeneratedSource,1949        };1950      });1951      //  Block for the alternate (if the test is not truthy)1952      const alternateBlock = builder.enter('value', _blockId => {1953        const alternatePath = expr.get('alternate');1954        const alternate = lowerExpressionToTemporary(builder, alternatePath);1955        lowerValueToTemporary(builder, {1956          kind: 'StoreLocal',1957          lvalue: {kind: InstructionKind.Const, place: {...place}},1958          value: alternate,1959          type: null,1960          loc: exprLoc,1961        });1962        return {1963          kind: 'goto',1964          block: continuationBlock.id,1965          variant: GotoVariant.Break,1966          id: makeInstructionId(0),1967          loc: alternatePath.node.loc ?? GeneratedSource,1968        };1969      });19701971      builder.terminateWithContinuation(1972        {1973          kind: 'ternary',1974          fallthrough: continuationBlock.id,1975          id: makeInstructionId(0),1976          test: testBlock.id,1977          loc: exprLoc,1978        },1979        testBlock,1980      );1981      const testPlace = lowerExpressionToTemporary(builder, expr.get('test'));1982      builder.terminateWithContinuation(1983        {1984          kind: 'branch',1985          test: {...testPlace},1986          consequent: consequentBlock,1987          alternate: alternateBlock,1988          fallthrough: continuationBlock.id,1989          id: makeInstructionId(0),1990          loc: exprLoc,1991        },1992        continuationBlock,1993      );1994      return {kind: 'LoadLocal', place, loc: place.loc};1995    }1996    case 'LogicalExpression': {1997      const expr = exprPath as NodePath<t.LogicalExpression>;1998      const exprLoc = expr.node.loc ?? GeneratedSource;1999      const continuationBlock = builder.reserve(builder.currentBlockKind());2000      const testBlock = builder.reserve('value');

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.