@samitouri / QOS-React-2 / commits / 13f20044f3

[compiler] Prepare HIRBuilder to be used by later passes (#32286)

--- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32286). * #33326 * #33325 * __->__ #32286

mofeiZ committed May 22, 2025 at 16:13 UTC 13f20044f3a5a9433eb4c6ef4c6577b8f0d13350
4 files changed +34 -25
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+1
@@ -130,6 +130,7 @@ function run(
130 mode,
131 config,
132 contextIdentifiers,
133 + func,
134 logger,
135 filename,
136 code,
compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
+12 -13
@@ -70,12 +70,14 @@ import {BuiltInArrayId} from './ObjectShape';
70 export function lower(
71 func: NodePath<t.Function>,
72 env: Environment,
73 + // Bindings captured from the outer function, in case lower() is called recursively (for lambdas)
74 bindings: Bindings | null = null,
75 capturedRefs: Array<t.Identifier> = [],
75 - // the outermost function being compiled, in case lower() is called recursively (for lambdas)
76 - parent: NodePath<t.Function> | null = null,
76 ): Result<HIRFunction, CompilerError> {
78 - const builder = new HIRBuilder(env, parent ?? func, bindings, capturedRefs);
77 + const builder = new HIRBuilder(env, {
78 + bindings,
79 + context: capturedRefs,
80 + });
81 const context: HIRFunction['context'] = [];
82
83 for (const ref of capturedRefs ?? []) {
@@ -215,7 +217,7 @@ export function lower(
217 return Ok({
218 id,
219 params,
218 - fnType: parent == null ? env.fnType : 'Other',
220 + fnType: bindings == null ? env.fnType : 'Other',
221 returnTypeAnnotation: null, // TODO: extract the actual return type node if present
222 returnType: makeType(),
223 body: builder.build(),
@@ -3417,7 +3419,7 @@ function lowerFunction(
3419 | t.ObjectMethod
3420 >,
3421 ): LoweredFunction | null {
3420 - const componentScope: Scope = builder.parentFunction.scope;
3422 + const componentScope: Scope = builder.environment.parentFunction.scope;
3423 const capturedContext = gatherCapturedContext(expr, componentScope);
3424
3425 /*
@@ -3428,13 +3430,10 @@ function lowerFunction(
3430 * This isn't a problem in practice because use Babel's scope analysis to
3431 * identify the correct references.
3432 */
3431 - const lowering = lower(
3432 - expr,
3433 - builder.environment,
3434 - builder.bindings,
3435 - [...builder.context, ...capturedContext],
3436 - builder.parentFunction,
3437 - );
3433 + const lowering = lower(expr, builder.environment, builder.bindings, [
3434 + ...builder.context,
3435 + ...capturedContext,
3436 + ]);
3437 let loweredFunc: HIRFunction;
3438 if (lowering.isErr()) {
3439 lowering
@@ -3456,7 +3455,7 @@ function lowerExpressionToTemporary(
3455 return lowerValueToTemporary(builder, value);
3456 }
3457
3459 -function lowerValueToTemporary(
3458 +export function lowerValueToTemporary(
3459 builder: HIRBuilder,
3460 value: InstructionValue,
3461 ): Place {
compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
+4 -1
@@ -47,7 +47,7 @@ import {
47 ShapeRegistry,
48 addHook,
49 } from './ObjectShape';
50 -import {Scope as BabelScope} from '@babel/traverse';
50 +import {Scope as BabelScope, NodePath} from '@babel/traverse';
51 import {TypeSchema} from './TypeSchema';
52
53 export const ReactElementSymbolSchema = z.object({
@@ -675,6 +675,7 @@ export class Environment {
675
676 #contextIdentifiers: Set<t.Identifier>;
677 #hoistedIdentifiers: Set<t.Identifier>;
678 + parentFunction: NodePath<t.Function>;
679
680 constructor(
681 scope: BabelScope,
@@ -682,6 +683,7 @@ export class Environment {
683 compilerMode: CompilerMode,
684 config: EnvironmentConfig,
685 contextIdentifiers: Set<t.Identifier>,
686 + parentFunction: NodePath<t.Function>, // the outermost function being compiled
687 logger: Logger | null,
688 filename: string | null,
689 code: string | null,
@@ -740,6 +742,7 @@ export class Environment {
742 this.#moduleTypes.set(REANIMATED_MODULE_NAME, reanimatedModuleType);
743 }
744
745 + this.parentFunction = parentFunction;
746 this.#contextIdentifiers = contextIdentifiers;
747 this.#hoistedIdentifiers = new Set();
748 }
compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts
+17 -11
@@ -110,7 +110,6 @@ export default class HIRBuilder {
110 #bindings: Bindings;
111 #env: Environment;
112 #exceptionHandlerStack: Array<BlockId> = [];
113 - parentFunction: NodePath<t.Function>;
113 errors: CompilerError = new CompilerError();
114 /**
115 * Traversal context: counts the number of `fbt` tag parents
@@ -136,16 +135,17 @@ export default class HIRBuilder {
135
136 constructor(
137 env: Environment,
139 - parentFunction: NodePath<t.Function>, // the outermost function being compiled
140 - bindings: Bindings | null = null,
141 - context: Array<t.Identifier> | null = null,
138 + options?: {
139 + bindings?: Bindings | null;
140 + context?: Array<t.Identifier>;
141 + entryBlockKind?: BlockKind;
142 + },
143 ) {
144 this.#env = env;
144 - this.#bindings = bindings ?? new Map();
145 - this.parentFunction = parentFunction;
146 - this.#context = context ?? [];
145 + this.#bindings = options?.bindings ?? new Map();
146 + this.#context = options?.context ?? [];
147 this.#entry = makeBlockId(env.nextBlockId);
148 - this.#current = newBlock(this.#entry, 'block');
148 + this.#current = newBlock(this.#entry, options?.entryBlockKind ?? 'block');
149 }
150
151 currentBlockKind(): BlockKind {
@@ -239,7 +239,7 @@ export default class HIRBuilder {
239
240 // Check if the binding is from module scope
241 const outerBinding =
242 - this.parentFunction.scope.parent.getBinding(originalName);
242 + this.#env.parentFunction.scope.parent.getBinding(originalName);
243 if (babelBinding === outerBinding) {
244 const path = babelBinding.path;
245 if (path.isImportDefaultSpecifier()) {
@@ -293,7 +293,7 @@ export default class HIRBuilder {
293 const binding = this.#resolveBabelBinding(path);
294 if (binding) {
295 // Check if the binding is from module scope, if so return null
296 - const outerBinding = this.parentFunction.scope.parent.getBinding(
296 + const outerBinding = this.#env.parentFunction.scope.parent.getBinding(
297 path.node.name,
298 );
299 if (binding === outerBinding) {
@@ -376,7 +376,7 @@ export default class HIRBuilder {
376 }
377
378 // Terminate the current block w the given terminal, and start a new block
379 - terminate(terminal: Terminal, nextBlockKind: BlockKind | null): void {
379 + terminate(terminal: Terminal, nextBlockKind: BlockKind | null): BlockId {
380 const {id: blockId, kind, instructions} = this.#current;
381 this.#completed.set(blockId, {
382 kind,
@@ -390,6 +390,7 @@ export default class HIRBuilder {
390 const nextId = this.#env.nextBlockId;
391 this.#current = newBlock(nextId, nextBlockKind);
392 }
393 + return blockId;
394 }
395
396 /*
@@ -746,6 +747,11 @@ function getReversePostorderedBlocks(func: HIR): HIR['blocks'] {
747 * (eg bb2 then bb1), we ensure that they get reversed back to the correct order.
748 */
749 const block = func.blocks.get(blockId)!;
750 + CompilerError.invariant(block != null, {
751 + reason: '[HIRBuilder] Unexpected null block',
752 + description: `expected block ${blockId} to exist`,
753 + loc: GeneratedSource,
754 + });
755 const successors = [...eachTerminalSuccessor(block.terminal)].reverse();
756 const fallthrough = terminalFallthrough(block.terminal);
757