From 5ab4362bfdef90115502abd64b4fe14b2c0b07b6 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Mon, 15 Jul 2024 11:25:53 +0900 Subject: [PATCH 1/2] [compiler] Refactor Program to use queue of functions to compile Refactors Program.ts to first traverse the `Program` node and build up a queue of functions to visit, then iterate that queue and compile the functions. This doesn't change behavior, but allows the next diff to add additional items to the queue during compilation (for function outlining). [ghstack-poisoned] --- .../src/Entrypoint/Program.ts | 100 +++++++++++------- 1 file changed, 64 insertions(+), 36 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index adee97238b2b7..4c5155fe57751 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -31,6 +31,7 @@ import { findProgramSuppressions, suppressionsToCompilerError, } from "./Suppression"; +import { GeneratedSource } from "../HIR"; export type CompilerPass = { opts: PluginOptions; @@ -265,6 +266,10 @@ export function compileProgram( ); const lintError = suppressionsToCompilerError(suppressions); let hasCriticalError = lintError != null; + const queue: Array<{ + fn: BabelFn; + fnType: ReactFunctionType; + }> = []; const compiledFns: Array = []; const traverseFunction = (fn: BabelFn, pass: CompilerPass): void => { @@ -281,6 +286,47 @@ export function compileProgram( ALREADY_COMPILED.add(fn.node); fn.skip(); + queue.push({ fn, fnType }); + }; + + // Main traversal to compile with Forget + program.traverse( + { + ClassDeclaration(node: NodePath) { + /* + * Don't visit functions defined inside classes, because they + * can reference `this` which is unsafe for compilation + */ + node.skip(); + return; + }, + + ClassExpression(node: NodePath) { + /* + * Don't visit functions defined inside classes, because they + * can reference `this` which is unsafe for compilation + */ + node.skip(); + return; + }, + + FunctionDeclaration: traverseFunction, + + FunctionExpression: traverseFunction, + + ArrowFunctionExpression: traverseFunction, + }, + { + ...pass, + opts: { ...pass.opts, ...pass.opts }, + filename: pass.filename ?? null, + } + ); + + const processFn = ( + fn: BabelFn, + fnType: ReactFunctionType + ): null | CodegenFunction => { if (lintError != null) { /** * Note that Babel does not attach comment nodes to nodes; they are dangling off of the @@ -335,52 +381,33 @@ export function compileProgram( } catch (err) { hasCriticalError ||= isCriticalError(err); handleError(err, pass, fn.node.loc ?? null); - return; + return null; } if (!pass.opts.noEmit && !hasCriticalError) { - compiledFns.push({ originalFn: fn, compiledFn }); + return compiledFn; } + return null; }; - // Main traversal to compile with Forget - program.traverse( - { - ClassDeclaration(node: NodePath) { - /* - * Don't visit functions defined inside classes, because they - * can reference `this` which is unsafe for compilation - */ - node.skip(); - return; - }, - - ClassExpression(node: NodePath) { - /* - * Don't visit functions defined inside classes, because they - * can reference `this` which is unsafe for compilation - */ - node.skip(); - return; - }, - - FunctionDeclaration: traverseFunction, - - FunctionExpression: traverseFunction, - - ArrowFunctionExpression: traverseFunction, - }, - { - ...pass, - opts: { ...pass.opts, ...pass.opts }, - filename: pass.filename ?? null, + while (queue.length !== 0) { + const current = queue.shift()!; + const compiled = processFn(current.fn, current.fnType); + if (compiled === null) { + continue; } - ); + compiledFns.push({ + compiledFn: compiled, + originalFn: current.fn, + }); + } if (pass.opts.gating != null) { const error = checkFunctionReferencedBeforeDeclarationAtTopLevel( program, - compiledFns.map(({ originalFn }) => originalFn) + compiledFns.map((result) => { + return result.originalFn; + }) ); if (error) { handleError(error, pass, null); @@ -439,7 +466,8 @@ export function compileProgram( * Only insert Forget-ified functions if we have not encountered a critical * error elsewhere in the file, regardless of bailout mode. */ - for (const { originalFn, compiledFn } of compiledFns) { + for (const result of compiledFns) { + const { originalFn, compiledFn } = result; const transformedFn = createNewFunctionNode(originalFn, compiledFn); if (gating != null) { From 492ef1e9fdb238004749986b6b211d74243f3c66 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Mon, 15 Jul 2024 12:01:36 +0900 Subject: [PATCH 2/2] Update on "[compiler] Refactor Program to use queue of functions to compile" Refactors Program.ts to first traverse the `Program` node and build up a queue of functions to visit, then iterate that queue and compile the functions. This doesn't change behavior, but allows the next diff to add additional items to the queue during compilation (for function outlining). [ghstack-poisoned] --- .../babel-plugin-react-compiler/src/Entrypoint/Program.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index 4c5155fe57751..ed11763eee881 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -31,7 +31,6 @@ import { findProgramSuppressions, suppressionsToCompilerError, } from "./Suppression"; -import { GeneratedSource } from "../HIR"; export type CompilerPass = { opts: PluginOptions;