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.