compiler/scripts/ts-compile-fixture.ts TYPESCRIPT 698 lines View on github.com → Search inside
1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 */78/**9 * TS test binary for the Rust port testing infrastructure.10 *11 * Implements the compiler pipeline independently (NOT using compile() or12 * runWithEnvironment()), calling each pass function directly in the same13 * sequence as the Rust binary. This ensures both sides have exactly matching14 * behavior.15 *16 * Takes a compiler pass name and a fixture path, finds every top-level17 * function, runs the pipeline up to the target pass for each, and prints18 * a detailed debug representation to stdout.19 *20 * Usage: npx tsx compiler/scripts/ts-compile-fixture.mjs <pass> <fixture-path>21 */2223import {parse} from '@babel/parser';24import _traverse from '@babel/traverse';25const traverse: typeof _traverse = (_traverse as any).default || _traverse;26import * as t from '@babel/types';27import {type NodePath} from '@babel/traverse';28import fs from 'fs';29import path from 'path';3031// --- Import pass functions directly from compiler source ---32import {lower} from '../packages/babel-plugin-react-compiler/src/HIR/BuildHIR';33import {34  Environment,35  type EnvironmentConfig,36  type ReactFunctionType,37} from '../packages/babel-plugin-react-compiler/src/HIR/Environment';38import {findContextIdentifiers} from '../packages/babel-plugin-react-compiler/src/HIR/FindContextIdentifiers';39import {mergeConsecutiveBlocks} from '../packages/babel-plugin-react-compiler/src/HIR/MergeConsecutiveBlocks';40import {41  assertConsistentIdentifiers,42  assertTerminalSuccessorsExist,43  assertTerminalPredsExist,44} from '../packages/babel-plugin-react-compiler/src/HIR';45import {assertValidBlockNesting} from '../packages/babel-plugin-react-compiler/src/HIR/AssertValidBlockNesting';46import {assertValidMutableRanges} from '../packages/babel-plugin-react-compiler/src/HIR/AssertValidMutableRanges';47import {pruneUnusedLabelsHIR} from '../packages/babel-plugin-react-compiler/src/HIR/PruneUnusedLabelsHIR';48import {mergeOverlappingReactiveScopesHIR} from '../packages/babel-plugin-react-compiler/src/HIR/MergeOverlappingReactiveScopesHIR';49import {buildReactiveScopeTerminalsHIR} from '../packages/babel-plugin-react-compiler/src/HIR/BuildReactiveScopeTerminalsHIR';50import {alignReactiveScopesToBlockScopesHIR} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes/AlignReactiveScopesToBlockScopesHIR';51import {flattenReactiveLoopsHIR} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes/FlattenReactiveLoopsHIR';52import {flattenScopesWithHooksOrUseHIR} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes/FlattenScopesWithHooksOrUseHIR';53import {propagateScopeDependenciesHIR} from '../packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR';5455import {56  pruneMaybeThrows,57  constantPropagation,58  deadCodeElimination,59} from '../packages/babel-plugin-react-compiler/src/Optimization';60import {optimizePropsMethodCalls} from '../packages/babel-plugin-react-compiler/src/Optimization/OptimizePropsMethodCalls';61import {outlineFunctions} from '../packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions';62import {optimizeForSSR} from '../packages/babel-plugin-react-compiler/src/Optimization/OptimizeForSSR';6364import {65  enterSSA,66  eliminateRedundantPhi,67  rewriteInstructionKindsBasedOnReassignment,68} from '../packages/babel-plugin-react-compiler/src/SSA';69import {inferTypes} from '../packages/babel-plugin-react-compiler/src/TypeInference';7071import {72  analyseFunctions,73  dropManualMemoization,74  inferReactivePlaces,75  inlineImmediatelyInvokedFunctionExpressions,76} from '../packages/babel-plugin-react-compiler/src/Inference';77import {inferMutationAliasingEffects} from '../packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects';78import {inferMutationAliasingRanges} from '../packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges';7980import {81  buildReactiveFunction,82  inferReactiveScopeVariables,83  memoizeFbtAndMacroOperandsInSameScope,84  promoteUsedTemporaries,85  propagateEarlyReturns,86  pruneHoistedContexts,87  pruneNonEscapingScopes,88  pruneNonReactiveDependencies,89  pruneUnusedLValues,90  pruneUnusedLabels,91  pruneUnusedScopes,92  mergeReactiveScopesThatInvalidateTogether,93  renameVariables,94  extractScopeDeclarationsFromDestructuring,95  codegenFunction,96  alignObjectMethodScopes,97} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes';98import {alignMethodCallScopes} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes/AlignMethodCallScopes';99import {pruneAlwaysInvalidatingScopes} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneAlwaysInvalidatingScopes';100import {stabilizeBlockIds} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes/StabilizeBlockIds';101102import {nameAnonymousFunctions} from '../packages/babel-plugin-react-compiler/src/Transform/NameAnonymousFunctions';103104import {105  validateContextVariableLValues,106  validateHooksUsage,107  validateNoCapitalizedCalls,108  validateNoRefAccessInRender,109  validateNoSetStateInRender,110  validatePreservedManualMemoization,111  validateUseMemo,112} from '../packages/babel-plugin-react-compiler/src/Validation';113import {validateLocalsNotReassignedAfterRender} from '../packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender';114import {validateNoFreezingKnownMutableFunctions} from '../packages/babel-plugin-react-compiler/src/Validation/ValidateNoFreezingKnownMutableFunctions';115116import {CompilerError} from '../packages/babel-plugin-react-compiler/src/CompilerError';117import {type HIRFunction} from '../packages/babel-plugin-react-compiler/src/HIR/HIR';118119import {parseConfigPragmaForTests} from '../packages/babel-plugin-react-compiler/src/Utils/TestUtils';120import {121  parsePluginOptions,122  ProgramContext,123} from '../packages/babel-plugin-react-compiler/src/Entrypoint';124125import {debugPrintHIR} from './debug-print-hir.mjs';126import {debugPrintReactive} from './debug-print-reactive.mjs';127import {debugPrintError} from './debug-print-error.mjs';128129// --- Arguments ---130const [passArg, fixturePath] = process.argv.slice(2);131132if (!passArg || !fixturePath) {133  console.error(134    'Usage: npx tsx compiler/scripts/ts-compile-fixture.mjs <pass> <fixture-path>',135  );136  process.exit(1);137}138139// --- Valid pass names (checkpoint names) ---140const VALID_PASSES = new Set([141  'HIR',142  'PruneMaybeThrows',143  'DropManualMemoization',144  'InlineIIFEs',145  'MergeConsecutiveBlocks',146  'SSA',147  'EliminateRedundantPhi',148  'ConstantPropagation',149  'InferTypes',150  'OptimizePropsMethodCalls',151  'AnalyseFunctions',152  'InferMutationAliasingEffects',153  'OptimizeForSSR',154  'DeadCodeElimination',155  'PruneMaybeThrows2',156  'InferMutationAliasingRanges',157  'InferReactivePlaces',158  'RewriteInstructionKinds',159  'InferReactiveScopeVariables',160  'MemoizeFbtOperands',161  'NameAnonymousFunctions',162  'OutlineFunctions',163  'AlignMethodCallScopes',164  'AlignObjectMethodScopes',165  'PruneUnusedLabelsHIR',166  'AlignReactiveScopesToBlockScopes',167  'MergeOverlappingReactiveScopes',168  'BuildReactiveScopeTerminals',169  'FlattenReactiveLoops',170  'FlattenScopesWithHooksOrUse',171  'PropagateScopeDependencies',172  'BuildReactiveFunction',173  'PruneUnusedLabels',174  'PruneNonEscapingScopes',175  'PruneNonReactiveDependencies',176  'PruneUnusedScopes',177  'MergeReactiveScopesThatInvalidateTogether',178  'PruneAlwaysInvalidatingScopes',179  'PropagateEarlyReturns',180  'PruneUnusedLValues',181  'PromoteUsedTemporaries',182  'ExtractScopeDeclarationsFromDestructuring',183  'StabilizeBlockIds',184  'RenameVariables',185  'PruneHoistedContexts',186  'Codegen',187]);188189if (!VALID_PASSES.has(passArg)) {190  console.error(`Unknown pass: ${passArg}`);191  console.error(`Valid passes: ${[...VALID_PASSES].join(', ')}`);192  process.exit(1);193}194195// --- Read fixture source ---196const source = fs.readFileSync(fixturePath, 'utf8');197const firstLine = source.substring(0, source.indexOf('\n'));198199// Determine language and source type200const language = firstLine.includes('@flow') ? 'flow' : 'typescript';201const sourceType = firstLine.includes('@script') ? 'script' : 'module';202203// --- Parse config pragmas ---204const parsedOpts = parseConfigPragmaForTests(firstLine, {205  compilationMode: 'all',206});207const envConfig: EnvironmentConfig = {208  ...parsedOpts.environment,209  assertValidMutableRanges: true,210};211212// --- Parse the fixture ---213const plugins: Array<any> =214  language === 'flow' ? ['flow', 'jsx'] : ['typescript', 'jsx'];215const inputAst = parse(source, {216  sourceFilename: path.basename(fixturePath),217  plugins,218  sourceType,219  errorRecovery: true,220});221222// --- Find ALL top-level functions ---223const functionPaths: Array<224  NodePath<225    t.FunctionDeclaration | t.FunctionExpression | t.ArrowFunctionExpression226  >227> = [];228let programPath: NodePath<t.Program> | null = null;229230traverse(inputAst, {231  Program(nodePath: NodePath<t.Program>) {232    programPath = nodePath;233  },234  'FunctionDeclaration|FunctionExpression|ArrowFunctionExpression'(235    nodePath: NodePath<236      t.FunctionDeclaration | t.FunctionExpression | t.ArrowFunctionExpression237    >,238  ) {239    if (isTopLevelFunction(nodePath)) {240      functionPaths.push(nodePath);241      nodePath.skip();242    }243  },244  ClassDeclaration(nodePath: NodePath<t.ClassDeclaration>) {245    nodePath.skip();246  },247  ClassExpression(nodePath: NodePath<t.ClassExpression>) {248    nodePath.skip();249  },250});251252function isTopLevelFunction(fnPath: NodePath): boolean {253  let current = fnPath;254  while (current.parentPath) {255    const parent = current.parentPath;256    if (parent.isProgram()) {257      return true;258    }259    if (parent.isVariableDeclarator()) {260      current = parent;261      continue;262    }263    if (parent.isVariableDeclaration()) {264      current = parent;265      continue;266    }267    if (268      parent.isExportNamedDeclaration() ||269      parent.isExportDefaultDeclaration()270    ) {271      current = parent;272      continue;273    }274    return false;275  }276  return false;277}278279if (functionPaths.length === 0) {280  console.error('No top-level functions found in fixture');281  process.exit(1);282}283284// --- Compile each function ---285const filename = '/' + path.basename(fixturePath);286const allOutputs: string[] = [];287288for (const fnPath of functionPaths) {289  const output = compileOneFunction(fnPath);290  if (output != null) {291    allOutputs.push(output);292  }293}294295// --- Write output ---296if (allOutputs.length === 0) {297  console.error('No functions produced output');298  process.exit(1);299}300const finalOutput = allOutputs.join('\n---\n');301process.stdout.write(finalOutput);302if (!finalOutput.endsWith('\n')) {303  process.stdout.write('\n');304}305306// --- Run the pipeline for a single function, mirroring Rust's run_pipeline ---307function compileOneFunction(308  fnPath: NodePath<309    t.FunctionDeclaration | t.FunctionExpression | t.ArrowFunctionExpression310  >,311): string | null {312  const contextIdentifiers = findContextIdentifiers(fnPath);313  const env = new Environment(314    fnPath.scope,315    'Other' as ReactFunctionType,316    'client', // outputMode317    envConfig,318    contextIdentifiers,319    fnPath,320    null, // logger321    filename,322    source,323    new ProgramContext({324      program: programPath!,325      opts: parsedOpts,326      filename,327      code: source,328      suppressions: [],329      hasModuleScopeOptOut: false,330    }),331  );332333  const pass = passArg;334335  function formatEnvErrors(): string {336    return debugPrintError(env.aggregateErrors());337  }338339  function printHIR(hir: HIRFunction): string {340    return debugPrintHIR(null, hir);341  }342343  function checkpointHIR(hir: HIRFunction): string {344    if (env.hasErrors()) {345      return formatEnvErrors();346    }347    return printHIR(hir);348  }349350  try {351    // --- HIR Phase ---352    const hir = lower(fnPath, env);353    if (pass === 'HIR') {354      return checkpointHIR(hir);355    }356357    pruneMaybeThrows(hir);358    if (pass === 'PruneMaybeThrows') {359      return checkpointHIR(hir);360    }361362    validateContextVariableLValues(hir);363    validateUseMemo(hir);364365    if (env.enableDropManualMemoization) {366      dropManualMemoization(hir);367    }368    if (pass === 'DropManualMemoization') {369      return checkpointHIR(hir);370    }371372    inlineImmediatelyInvokedFunctionExpressions(hir);373    if (pass === 'InlineIIFEs') {374      return checkpointHIR(hir);375    }376377    mergeConsecutiveBlocks(hir);378    if (pass === 'MergeConsecutiveBlocks') {379      return checkpointHIR(hir);380    }381382    assertConsistentIdentifiers(hir);383    assertTerminalSuccessorsExist(hir);384385    enterSSA(hir);386    if (pass === 'SSA') {387      return checkpointHIR(hir);388    }389390    eliminateRedundantPhi(hir);391    if (pass === 'EliminateRedundantPhi') {392      return checkpointHIR(hir);393    }394395    assertConsistentIdentifiers(hir);396397    constantPropagation(hir);398    if (pass === 'ConstantPropagation') {399      return checkpointHIR(hir);400    }401402    inferTypes(hir);403    if (pass === 'InferTypes') {404      return checkpointHIR(hir);405    }406407    if (env.enableValidations) {408      if (env.config.validateHooksUsage) {409        validateHooksUsage(hir);410      }411      if (env.config.validateNoCapitalizedCalls) {412        validateNoCapitalizedCalls(hir);413      }414    }415416    optimizePropsMethodCalls(hir);417    if (pass === 'OptimizePropsMethodCalls') {418      return checkpointHIR(hir);419    }420421    analyseFunctions(hir);422    if (pass === 'AnalyseFunctions') {423      return checkpointHIR(hir);424    }425426    inferMutationAliasingEffects(hir);427    if (pass === 'InferMutationAliasingEffects') {428      return checkpointHIR(hir);429    }430431    if (env.outputMode === 'ssr') {432      optimizeForSSR(hir);433    }434    if (pass === 'OptimizeForSSR') {435      return checkpointHIR(hir);436    }437438    deadCodeElimination(hir);439    if (pass === 'DeadCodeElimination') {440      return checkpointHIR(hir);441    }442443    pruneMaybeThrows(hir);444    if (pass === 'PruneMaybeThrows2') {445      return checkpointHIR(hir);446    }447448    inferMutationAliasingRanges(hir, {isFunctionExpression: false});449    if (pass === 'InferMutationAliasingRanges') {450      return checkpointHIR(hir);451    }452453    if (env.enableValidations) {454      validateLocalsNotReassignedAfterRender(hir);455456      if (env.config.assertValidMutableRanges) {457        assertValidMutableRanges(hir);458      }459460      if (env.config.validateRefAccessDuringRender) {461        validateNoRefAccessInRender(hir);462      }463464      if (env.config.validateNoSetStateInRender) {465        validateNoSetStateInRender(hir);466      }467468      validateNoFreezingKnownMutableFunctions(hir);469    }470471    inferReactivePlaces(hir);472    if (pass === 'InferReactivePlaces') {473      return checkpointHIR(hir);474    }475476    rewriteInstructionKindsBasedOnReassignment(hir);477    if (pass === 'RewriteInstructionKinds') {478      return checkpointHIR(hir);479    }480481    if (env.enableMemoization) {482      inferReactiveScopeVariables(hir);483    }484    if (pass === 'InferReactiveScopeVariables') {485      return checkpointHIR(hir);486    }487488    const fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir);489    if (pass === 'MemoizeFbtOperands') {490      return checkpointHIR(hir);491    }492493    if (env.config.enableNameAnonymousFunctions) {494      nameAnonymousFunctions(hir);495    }496    if (pass === 'NameAnonymousFunctions') {497      return checkpointHIR(hir);498    }499500    if (env.config.enableFunctionOutlining) {501      outlineFunctions(hir, fbtOperands);502    }503    if (pass === 'OutlineFunctions') {504      return checkpointHIR(hir);505    }506507    alignMethodCallScopes(hir);508    if (pass === 'AlignMethodCallScopes') {509      return checkpointHIR(hir);510    }511512    alignObjectMethodScopes(hir);513    if (pass === 'AlignObjectMethodScopes') {514      return checkpointHIR(hir);515    }516517    pruneUnusedLabelsHIR(hir);518    if (pass === 'PruneUnusedLabelsHIR') {519      return checkpointHIR(hir);520    }521522    alignReactiveScopesToBlockScopesHIR(hir);523    if (pass === 'AlignReactiveScopesToBlockScopes') {524      return checkpointHIR(hir);525    }526527    mergeOverlappingReactiveScopesHIR(hir);528    if (pass === 'MergeOverlappingReactiveScopes') {529      return checkpointHIR(hir);530    }531532    assertValidBlockNesting(hir);533534    buildReactiveScopeTerminalsHIR(hir);535    if (pass === 'BuildReactiveScopeTerminals') {536      return checkpointHIR(hir);537    }538539    assertValidBlockNesting(hir);540541    flattenReactiveLoopsHIR(hir);542    if (pass === 'FlattenReactiveLoops') {543      return checkpointHIR(hir);544    }545546    flattenScopesWithHooksOrUseHIR(hir);547    if (pass === 'FlattenScopesWithHooksOrUse') {548      return checkpointHIR(hir);549    }550551    assertTerminalSuccessorsExist(hir);552    assertTerminalPredsExist(hir);553554    propagateScopeDependenciesHIR(hir);555    if (pass === 'PropagateScopeDependencies') {556      return checkpointHIR(hir);557    }558559    // --- Reactive Phase ---560    const reactiveFunction = buildReactiveFunction(hir);561    if (pass === 'BuildReactiveFunction') {562      if (env.hasErrors()) {563        return formatEnvErrors();564      }565      return debugPrintReactive(null, reactiveFunction);566    }567568    pruneUnusedLabels(reactiveFunction);569    if (pass === 'PruneUnusedLabels') {570      if (env.hasErrors()) {571        return formatEnvErrors();572      }573      return debugPrintReactive(null, reactiveFunction);574    }575576    pruneNonEscapingScopes(reactiveFunction);577    if (pass === 'PruneNonEscapingScopes') {578      if (env.hasErrors()) {579        return formatEnvErrors();580      }581      return debugPrintReactive(null, reactiveFunction);582    }583584    pruneNonReactiveDependencies(reactiveFunction);585    if (pass === 'PruneNonReactiveDependencies') {586      if (env.hasErrors()) {587        return formatEnvErrors();588      }589      return debugPrintReactive(null, reactiveFunction);590    }591592    pruneUnusedScopes(reactiveFunction);593    if (pass === 'PruneUnusedScopes') {594      if (env.hasErrors()) {595        return formatEnvErrors();596      }597      return debugPrintReactive(null, reactiveFunction);598    }599600    mergeReactiveScopesThatInvalidateTogether(reactiveFunction);601    if (pass === 'MergeReactiveScopesThatInvalidateTogether') {602      if (env.hasErrors()) {603        return formatEnvErrors();604      }605      return debugPrintReactive(null, reactiveFunction);606    }607608    pruneAlwaysInvalidatingScopes(reactiveFunction);609    if (pass === 'PruneAlwaysInvalidatingScopes') {610      if (env.hasErrors()) {611        return formatEnvErrors();612      }613      return debugPrintReactive(null, reactiveFunction);614    }615616    propagateEarlyReturns(reactiveFunction);617    if (pass === 'PropagateEarlyReturns') {618      if (env.hasErrors()) {619        return formatEnvErrors();620      }621      return debugPrintReactive(null, reactiveFunction);622    }623624    pruneUnusedLValues(reactiveFunction);625    if (pass === 'PruneUnusedLValues') {626      if (env.hasErrors()) {627        return formatEnvErrors();628      }629      return debugPrintReactive(null, reactiveFunction);630    }631632    promoteUsedTemporaries(reactiveFunction);633    if (pass === 'PromoteUsedTemporaries') {634      if (env.hasErrors()) {635        return formatEnvErrors();636      }637      return debugPrintReactive(null, reactiveFunction);638    }639640    extractScopeDeclarationsFromDestructuring(reactiveFunction);641    if (pass === 'ExtractScopeDeclarationsFromDestructuring') {642      if (env.hasErrors()) {643        return formatEnvErrors();644      }645      return debugPrintReactive(null, reactiveFunction);646    }647648    stabilizeBlockIds(reactiveFunction);649    if (pass === 'StabilizeBlockIds') {650      if (env.hasErrors()) {651        return formatEnvErrors();652      }653      return debugPrintReactive(null, reactiveFunction);654    }655656    const uniqueIdentifiers = renameVariables(reactiveFunction);657    if (pass === 'RenameVariables') {658      if (env.hasErrors()) {659        return formatEnvErrors();660      }661      return debugPrintReactive(null, reactiveFunction);662    }663664    pruneHoistedContexts(reactiveFunction);665    if (pass === 'PruneHoistedContexts') {666      if (env.hasErrors()) {667        return formatEnvErrors();668      }669      return debugPrintReactive(null, reactiveFunction);670    }671672    if (673      env.config.enablePreserveExistingMemoizationGuarantees ||674      env.config.validatePreserveExistingMemoizationGuarantees675    ) {676      validatePreservedManualMemoization(reactiveFunction);677    }678679    const ast = codegenFunction(reactiveFunction, {680      uniqueIdentifiers,681      fbtOperands,682    });683    if (pass === 'Codegen') {684      if (env.hasErrors()) {685        return formatEnvErrors();686      }687      return '(codegen ast)';688    }689690    return null;691  } catch (e) {692    if (e instanceof CompilerError) {693      return debugPrintError(e);694    }695    throw e;696  }697}

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.