@samitouri / QOS-React / commits / 3871fdadaa

[compiler][be] Clean up compilation skipping logic in Program

ghstack-source-id: fe2c81de9d4f41a787c690b722cbcff55eb18ac3 Pull Request resolved: https://github.com/facebook/react/pull/30642

Mofei Zhang committed Aug 8, 2024 at 15:41 UTC 3871fdadaa6ff98ba3039c3976e8aac6038f69a9
1 file changed +85 -73
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts
+85 -73
@@ -13,6 +13,7 @@ import {
13 ErrorSeverity,
14 } from '../CompilerError';
15 import {
16 + EnvironmentConfig,
17 ExternalFunction,
18 ReactFunctionType,
19 parseEnvironmentConfig,
@@ -276,41 +277,27 @@ export function compileProgram(
277 program: NodePath<t.Program>,
278 pass: CompilerPass,
279 ): void {
279 - if (pass.opts.sources) {
280 - if (pass.filename === null) {
281 - const error = new CompilerError();
282 - error.pushErrorDetail(
283 - new CompilerErrorDetail({
284 - reason: `Expected a filename but found none.`,
285 - description:
286 - "When the 'sources' config options is specified, the React compiler will only compile files with a name",
287 - severity: ErrorSeverity.InvalidConfig,
288 - loc: null,
289 - }),
290 - );
291 - handleError(error, pass, null);
292 - return;
293 - }
294 -
295 - if (!isFilePartOfSources(pass.opts.sources, pass.filename)) {
296 - return;
297 - }
298 - }
299 -
300 - // Top level "use no forget", skip this file entirely
301 - if (
302 - findDirectiveDisablingMemoization(program.node.directives, pass.opts) !=
303 - null
304 - ) {
280 + if (shouldSkipCompilation(program, pass)) {
281 return;
282 }
283
308 - const environment = parseEnvironmentConfig(pass.opts.environment ?? {});
284 + /*
285 + * TODO(lauren): Remove pass.opts.environment nullcheck once PluginOptions
286 + * is validated
287 + */
288 + const environmentResult = parseEnvironmentConfig(pass.opts.environment ?? {});
289 + if (environmentResult.isErr()) {
290 + CompilerError.throwInvalidConfig({
291 + reason:
292 + 'Error in validating environment config. This is an advanced setting and not meant to be used directly',
293 + description: environmentResult.unwrapErr().toString(),
294 + suggestions: null,
295 + loc: null,
296 + });
297 + }
298 + const environment = environmentResult.unwrap();
299 const useMemoCacheIdentifier = program.scope.generateUidIdentifier('c');
300 const moduleName = pass.opts.runtimeModule ?? 'react/compiler-runtime';
311 - if (hasMemoCacheFunctionImport(program, moduleName)) {
312 - return;
313 - }
301
302 /*
303 * Record lint errors and critical errors as depending on Forget's config,
@@ -332,7 +319,7 @@ export function compileProgram(
319 const compiledFns: Array<CompileResult> = [];
320
321 const traverseFunction = (fn: BabelFn, pass: CompilerPass): void => {
335 - const fnType = getReactFunctionType(fn, pass);
322 + const fnType = getReactFunctionType(fn, pass, environment);
323 if (fnType === null || ALREADY_COMPILED.has(fn.node)) {
324 return;
325 }
@@ -403,24 +390,9 @@ export function compileProgram(
390
391 let compiledFn: CodegenFunction;
392 try {
406 - /*
407 - * TODO(lauren): Remove pass.opts.environment nullcheck once PluginOptions
408 - * is validated
409 - */
410 - if (environment.isErr()) {
411 - CompilerError.throwInvalidConfig({
412 - reason:
413 - 'Error in validating environment config. This is an advanced setting and not meant to be used directly',
414 - description: environment.unwrapErr().toString(),
415 - suggestions: null,
416 - loc: null,
417 - });
418 - }
419 - const config = environment.unwrap();
420 -
393 compiledFn = compileFn(
394 fn,
423 - config,
395 + environment,
396 fnType,
397 useMemoCacheIdentifier.name,
398 pass.opts.logger,
@@ -514,43 +486,29 @@ export function compileProgram(
486 externalFunctions.push(gating);
487 }
488
517 - const lowerContextAccess = pass.opts.environment?.lowerContextAccess;
489 + const lowerContextAccess = environment.lowerContextAccess;
490 if (lowerContextAccess && hasLoweredContextAccess) {
519 - externalFunctions.push(tryParseExternalFunction(lowerContextAccess));
491 + externalFunctions.push(lowerContextAccess);
492 }
493
522 - const enableEmitInstrumentForget =
523 - pass.opts.environment?.enableEmitInstrumentForget;
494 + const enableEmitInstrumentForget = environment.enableEmitInstrumentForget;
495 if (enableEmitInstrumentForget != null) {
525 - externalFunctions.push(
526 - tryParseExternalFunction(enableEmitInstrumentForget.fn),
527 - );
496 + externalFunctions.push(enableEmitInstrumentForget.fn);
497 if (enableEmitInstrumentForget.gating != null) {
529 - externalFunctions.push(
530 - tryParseExternalFunction(enableEmitInstrumentForget.gating),
531 - );
498 + externalFunctions.push(enableEmitInstrumentForget.gating);
499 }
500 }
501
535 - if (pass.opts.environment?.enableEmitFreeze != null) {
536 - const enableEmitFreeze = tryParseExternalFunction(
537 - pass.opts.environment.enableEmitFreeze,
538 - );
539 - externalFunctions.push(enableEmitFreeze);
502 + if (environment.enableEmitFreeze != null) {
503 + externalFunctions.push(environment.enableEmitFreeze);
504 }
505
542 - if (pass.opts.environment?.enableEmitHookGuards != null) {
543 - const enableEmitHookGuards = tryParseExternalFunction(
544 - pass.opts.environment.enableEmitHookGuards,
545 - );
546 - externalFunctions.push(enableEmitHookGuards);
506 + if (environment.enableEmitHookGuards != null) {
507 + externalFunctions.push(environment.enableEmitHookGuards);
508 }
509
549 - if (pass.opts.environment?.enableChangeDetectionForDebugging != null) {
550 - const enableChangeDetectionForDebugging = tryParseExternalFunction(
551 - pass.opts.environment.enableChangeDetectionForDebugging,
552 - );
553 - externalFunctions.push(enableChangeDetectionForDebugging);
510 + if (environment.enableChangeDetectionForDebugging != null) {
511 + externalFunctions.push(environment.enableChangeDetectionForDebugging);
512 }
513 } catch (err) {
514 handleError(err, pass, null);
@@ -593,11 +551,65 @@ export function compileProgram(
551 }
552 }
553
554 +function shouldSkipCompilation(
555 + program: NodePath<t.Program>,
556 + pass: CompilerPass,
557 +): boolean {
558 + if (pass.opts.sources) {
559 + if (pass.filename === null) {
560 + const error = new CompilerError();
561 + error.pushErrorDetail(
562 + new CompilerErrorDetail({
563 + reason: `Expected a filename but found none.`,
564 + description:
565 + "When the 'sources' config options is specified, the React compiler will only compile files with a name",
566 + severity: ErrorSeverity.InvalidConfig,
567 + loc: null,
568 + }),
569 + );
570 + handleError(error, pass, null);
571 + return true;
572 + }
573 +
574 + if (!isFilePartOfSources(pass.opts.sources, pass.filename)) {
575 + return true;
576 + }
577 + }
578 +
579 + // Top level "use no forget", skip this file entirely
580 + const useNoForget = findDirectiveDisablingMemoization(
581 + program.node.directives,
582 + pass.opts,
583 + );
584 + if (useNoForget != null) {
585 + pass.opts.logger?.logEvent(pass.filename, {
586 + kind: 'CompileError',
587 + fnLoc: null,
588 + detail: {
589 + severity: ErrorSeverity.Todo,
590 + reason: 'Skipped due to "use no forget" directive.',
591 + loc: useNoForget.loc ?? null,
592 + suggestions: null,
593 + },
594 + });
595 + return true;
596 + }
597 + const moduleName = pass.opts.runtimeModule ?? 'react/compiler-runtime';
598 + if (hasMemoCacheFunctionImport(program, moduleName)) {
599 + return true;
600 + }
601 + return false;
602 +}
603 +
604 function getReactFunctionType(
605 fn: BabelFn,
606 pass: CompilerPass,
607 + /**
608 + * TODO(mofeiZ): remove once we validate PluginOptions with Zod
609 + */
610 + environment: EnvironmentConfig,
611 ): ReactFunctionType | null {
600 - const hookPattern = pass.opts.environment?.hookPattern ?? null;
612 + const hookPattern = environment.hookPattern;
613 if (fn.node.body.type === 'BlockStatement') {
614 // Opt-outs disable compilation regardless of mode
615 const useNoForget = findDirectiveDisablingMemoization(