[compiler][be] Logger based debug printing in test runner (#31809)
Avoid mutable logging enabled state and writing to `process.stdout` within our babel transform.
mofeiZ committed
Dec 16, 2024 at 15:15 UTC
d325f872de658fc26127a91c965c135d8ad4e877
8 files changed
+78
-173
compiler/packages/babel-plugin-react-compiler/package.json
-2
@@ -42,9 +42,7 @@
42
"babel-jest": "^29.0.3",
43
"babel-plugin-fbt": "^1.0.0",
44
"babel-plugin-fbt-runtime": "^1.0.0",
45
- "chalk": "4",
45
"eslint": "^8.57.1",
47
- "glob": "^7.1.6",
46
"invariant": "^2.2.4",
47
"jest": "^29.0.3",
48
"jest-environment-jsdom": "^29.0.3",
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+2
-42
@@ -79,13 +79,6 @@ import {
79
rewriteInstructionKindsBasedOnReassignment,
80
} from '../SSA';
81
import {inferTypes} from '../TypeInference';
82
-import {
83
- logCodegenFunction,
84
- logDebug,
85
- logHIRFunction,
86
- logReactiveFunction,
87
-} from '../Utils/logger';
88
-import {assertExhaustive} from '../Utils/utils';
82
import {
83
validateContextVariableLValues,
84
validateHooksUsage,
@@ -139,13 +132,7 @@ function run(
132
name: 'EnvironmentConfig',
133
value: prettyFormat(env.config),
134
});
142
- printLog({
143
- kind: 'debug',
144
- name: 'EnvironmentConfig',
145
- value: prettyFormat(env.config),
146
- });
147
- const ast = runWithEnvironment(func, env);
148
- return ast;
135
+ return runWithEnvironment(func, env);
136
}
137
138
/*
@@ -158,10 +145,8 @@ function runWithEnvironment(
145
>,
146
env: Environment,
147
): CodegenFunction {
161
- const log = (value: CompilerPipelineValue): CompilerPipelineValue => {
162
- printLog(value);
148
+ const log = (value: CompilerPipelineValue): void => {
149
env.logger?.debugLogIRs?.(value);
164
- return value;
150
};
151
const hir = lower(func, env).unwrap();
152
log({kind: 'hir', name: 'HIR', value: hir});
@@ -545,28 +530,3 @@ export function compileFn(
530
code,
531
);
532
}
548
-
549
-function printLog(value: CompilerPipelineValue): CompilerPipelineValue {
550
- switch (value.kind) {
551
- case 'ast': {
552
- logCodegenFunction(value.name, value.value);
553
- break;
554
- }
555
- case 'hir': {
556
- logHIRFunction(value.name, value.value);
557
- break;
558
- }
559
- case 'reactive': {
560
- logReactiveFunction(value.name, value.value);
561
- break;
562
- }
563
- case 'debug': {
564
- logDebug(value.name, value.value);
565
- break;
566
- }
567
- default: {
568
- assertExhaustive(value, 'Unexpected compilation kind');
569
- }
570
- }
571
- return value;
572
-}
compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts
+5
-2
@@ -19,7 +19,6 @@ import {
19
import {deadCodeElimination} from '../Optimization';
20
import {inferReactiveScopeVariables} from '../ReactiveScopes';
21
import {rewriteInstructionKindsBasedOnReassignment} from '../SSA';
22
-import {logHIRFunction} from '../Utils/logger';
22
import {inferMutableContextVariables} from './InferMutableContextVariables';
23
import {inferMutableRanges} from './InferMutableRanges';
24
import inferReferenceEffects from './InferReferenceEffects';
@@ -112,7 +111,11 @@ function lower(func: HIRFunction): void {
111
rewriteInstructionKindsBasedOnReassignment(func);
112
inferReactiveScopeVariables(func);
113
inferMutableContextVariables(func);
115
- logHIRFunction('AnalyseFunction (inner)', func);
114
+ func.env.logger?.debugLogIRs?.({
115
+ kind: 'hir',
116
+ name: 'AnalyseFunction (inner)',
117
+ value: func,
118
+ });
119
}
120
121
function infer(
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts
+5
-2
@@ -25,7 +25,6 @@ import {
25
eachPatternOperand,
26
} from '../HIR/visitors';
27
import DisjointSet from '../Utils/DisjointSet';
28
-import {logHIRFunction} from '../Utils/logger';
28
import {assertExhaustive} from '../Utils/utils';
29
30
/*
@@ -156,7 +155,11 @@ export function inferReactiveScopeVariables(fn: HIRFunction): void {
155
scope.range.end > maxInstruction + 1
156
) {
157
// Make it easier to debug why the error occurred
159
- logHIRFunction('InferReactiveScopeVariables (invalid scope)', fn);
158
+ fn.env.logger?.debugLogIRs?.({
159
+ kind: 'hir',
160
+ name: 'InferReactiveScopeVariables (invalid scope)',
161
+ value: fn,
162
+ });
163
CompilerError.invariant(false, {
164
reason: `Invalid mutable range for scope`,
165
loc: GeneratedSource,
compiler/packages/babel-plugin-react-compiler/src/Utils/logger.ts
deleted
-110
@@ -1,110 +0,0 @@
1
-/**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- */
7
-
8
-import generate from '@babel/generator';
9
-import * as t from '@babel/types';
10
-import chalk from 'chalk';
11
-import {HIR, HIRFunction, ReactiveFunction} from '../HIR/HIR';
12
-import {printFunctionWithOutlined, printHIR} from '../HIR/PrintHIR';
13
-import {CodegenFunction} from '../ReactiveScopes';
14
-import {printReactiveFunctionWithOutlined} from '../ReactiveScopes/PrintReactiveFunction';
15
-
16
-let ENABLED: boolean = false;
17
-
18
-let lastLogged: string;
19
-
20
-export function toggleLogging(enabled: boolean): void {
21
- ENABLED = enabled;
22
-}
23
-
24
-export function logDebug(step: string, value: string): void {
25
- if (ENABLED) {
26
- process.stdout.write(`${chalk.green(step)}:\n${value}\n\n`);
27
- }
28
-}
29
-
30
-export function logHIR(step: string, ir: HIR): void {
31
- if (ENABLED) {
32
- const printed = printHIR(ir);
33
- if (printed !== lastLogged) {
34
- lastLogged = printed;
35
- process.stdout.write(`${chalk.green(step)}:\n${printed}\n\n`);
36
- } else {
37
- process.stdout.write(`${chalk.blue(step)}: (no change)\n\n`);
38
- }
39
- }
40
-}
41
-
42
-export function logCodegenFunction(step: string, fn: CodegenFunction): void {
43
- if (ENABLED) {
44
- let printed: string | null = null;
45
- try {
46
- const node = t.functionDeclaration(
47
- fn.id,
48
- fn.params,
49
- fn.body,
50
- fn.generator,
51
- fn.async,
52
- );
53
- const ast = generate(node);
54
- printed = ast.code;
55
- } catch (e) {
56
- let errMsg: string;
57
- if (
58
- typeof e === 'object' &&
59
- e != null &&
60
- 'message' in e &&
61
- typeof e.message === 'string'
62
- ) {
63
- errMsg = e.message.toString();
64
- } else {
65
- errMsg = '[empty]';
66
- }
67
- console.log('Error formatting AST: ' + errMsg);
68
- }
69
- if (printed === null) {
70
- return;
71
- }
72
- if (printed !== lastLogged) {
73
- lastLogged = printed;
74
- process.stdout.write(`${chalk.green(step)}:\n${printed}\n\n`);
75
- } else {
76
- process.stdout.write(`${chalk.blue(step)}: (no change)\n\n`);
77
- }
78
- }
79
-}
80
-
81
-export function logHIRFunction(step: string, fn: HIRFunction): void {
82
- if (ENABLED) {
83
- const printed = printFunctionWithOutlined(fn);
84
- if (printed !== lastLogged) {
85
- lastLogged = printed;
86
- process.stdout.write(`${chalk.green(step)}:\n${printed}\n\n`);
87
- } else {
88
- process.stdout.write(`${chalk.blue(step)}: (no change)\n\n`);
89
- }
90
- }
91
-}
92
-
93
-export function logReactiveFunction(step: string, fn: ReactiveFunction): void {
94
- if (ENABLED) {
95
- const printed = printReactiveFunctionWithOutlined(fn);
96
- if (printed !== lastLogged) {
97
- lastLogged = printed;
98
- process.stdout.write(`${chalk.green(step)}:\n${printed}\n\n`);
99
- } else {
100
- process.stdout.write(`${chalk.blue(step)}: (no change)\n\n`);
101
- }
102
- }
103
-}
104
-
105
-export function log(fn: () => string): void {
106
- if (ENABLED) {
107
- const message = fn();
108
- process.stdout.write(message.trim() + '\n\n');
109
- }
110
-}
compiler/packages/snap/src/compiler.ts
+13
-9
@@ -19,6 +19,7 @@ import type {
19
PanicThresholdOptions,
20
PluginOptions,
21
CompilerReactTarget,
22
+ CompilerPipelineValue,
23
} from 'babel-plugin-react-compiler/src/Entrypoint';
24
import type {Effect, ValueKind} from 'babel-plugin-react-compiler/src/HIR';
25
import type {
@@ -45,6 +46,7 @@ export function parseLanguage(source: string): 'flow' | 'typescript' {
46
function makePluginOptions(
47
firstLine: string,
48
parseConfigPragmaFn: typeof ParseConfigPragma,
49
+ debugIRLogger: (value: CompilerPipelineValue) => void,
50
EffectEnum: typeof Effect,
51
ValueKindEnum: typeof ValueKind,
52
): [PluginOptions, Array<{filename: string | null; event: LoggerEvent}>] {
@@ -182,15 +184,15 @@ function makePluginOptions(
184
.filter(s => s.length > 0);
185
}
186
185
- let logs: Array<{filename: string | null; event: LoggerEvent}> = [];
186
- let logger: Logger | null = null;
187
- if (firstLine.includes('@logger')) {
188
- logger = {
189
- logEvent(filename: string | null, event: LoggerEvent): void {
190
- logs.push({filename, event});
191
- },
192
- };
193
- }
187
+ const logs: Array<{filename: string | null; event: LoggerEvent}> = [];
188
+ const logger: Logger = {
189
+ logEvent: firstLine.includes('@logger')
190
+ ? (filename, event) => {
191
+ logs.push({filename, event});
192
+ }
193
+ : () => {},
194
+ debugLogIRs: debugIRLogger,
195
+ };
196
197
const config = parseConfigPragmaFn(firstLine);
198
const options = {
@@ -338,6 +340,7 @@ export async function transformFixtureInput(
340
parseConfigPragmaFn: typeof ParseConfigPragma,
341
plugin: BabelCore.PluginObj,
342
includeEvaluator: boolean,
343
+ debugIRLogger: (value: CompilerPipelineValue) => void,
344
EffectEnum: typeof Effect,
345
ValueKindEnum: typeof ValueKind,
346
): Promise<{kind: 'ok'; value: TransformResult} | {kind: 'err'; msg: string}> {
@@ -365,6 +368,7 @@ export async function transformFixtureInput(
368
const [options, logs] = makePluginOptions(
369
firstLine,
370
parseConfigPragmaFn,
371
+ debugIRLogger,
372
EffectEnum,
373
ValueKindEnum,
374
);
compiler/packages/snap/src/constants.ts
+9
-3
@@ -18,11 +18,17 @@ export const COMPILER_PATH = path.join(
18
'BabelPlugin.js',
19
);
20
export const COMPILER_INDEX_PATH = path.join(process.cwd(), 'dist', 'index');
21
-export const LOGGER_PATH = path.join(
21
+export const PRINT_HIR_PATH = path.join(
22
process.cwd(),
23
'dist',
24
- 'Utils',
25
- 'logger.js',
24
+ 'HIR',
25
+ 'PrintHIR.js',
26
+);
27
+export const PRINT_REACTIVE_IR_PATH = path.join(
28
+ process.cwd(),
29
+ 'dist',
30
+ 'ReactiveScopes',
31
+ 'PrintReactiveFunction.js',
32
);
33
export const PARSE_CONFIG_PRAGMA_PATH = path.join(
34
process.cwd(),
compiler/packages/snap/src/runner-worker.ts
+44
-3
@@ -8,16 +8,21 @@
8
import {codeFrameColumns} from '@babel/code-frame';
9
import type {PluginObj} from '@babel/core';
10
import type {parseConfigPragmaForTests as ParseConfigPragma} from 'babel-plugin-react-compiler/src/HIR/Environment';
11
+import type {printFunctionWithOutlined as PrintFunctionWithOutlined} from 'babel-plugin-react-compiler/src/HIR/PrintHIR';
12
+import type {printReactiveFunctionWithOutlined as PrintReactiveFunctionWithOutlined} from 'babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction';
13
import {TransformResult, transformFixtureInput} from './compiler';
14
import {
15
COMPILER_PATH,
16
COMPILER_INDEX_PATH,
15
- LOGGER_PATH,
17
PARSE_CONFIG_PRAGMA_PATH,
18
+ PRINT_HIR_PATH,
19
+ PRINT_REACTIVE_IR_PATH,
20
} from './constants';
21
import {TestFixture, getBasename, isExpectError} from './fixture-utils';
22
import {TestResult, writeOutputToString} from './reporter';
23
import {runSprout} from './sprout';
24
+import {CompilerPipelineValue} from 'babel-plugin-react-compiler/src';
25
+import chalk from 'chalk';
26
27
const originalConsoleError = console.error;
28
@@ -64,20 +69,56 @@ async function compile(
69
const {Effect: EffectEnum, ValueKind: ValueKindEnum} = require(
70
COMPILER_INDEX_PATH,
71
);
67
- const {toggleLogging} = require(LOGGER_PATH);
72
+ const {printFunctionWithOutlined} = require(PRINT_HIR_PATH) as {
73
+ printFunctionWithOutlined: typeof PrintFunctionWithOutlined;
74
+ };
75
+ const {printReactiveFunctionWithOutlined} = require(
76
+ PRINT_REACTIVE_IR_PATH,
77
+ ) as {
78
+ printReactiveFunctionWithOutlined: typeof PrintReactiveFunctionWithOutlined;
79
+ };
80
+
81
+ let lastLogged: string | null = null;
82
+ const debugIRLogger = shouldLog
83
+ ? (value: CompilerPipelineValue) => {
84
+ let printed: string;
85
+ switch (value.kind) {
86
+ case 'hir':
87
+ printed = printFunctionWithOutlined(value.value);
88
+ break;
89
+ case 'reactive':
90
+ printed = printReactiveFunctionWithOutlined(value.value);
91
+ break;
92
+ case 'debug':
93
+ printed = value.value;
94
+ break;
95
+ case 'ast':
96
+ // skip printing ast as we already write fixture output JS
97
+ printed = '(ast)';
98
+ break;
99
+ }
100
+
101
+ if (printed !== lastLogged) {
102
+ lastLogged = printed;
103
+ console.log(`${chalk.green(value.name)}:\n ${printed}\n`);
104
+ } else {
105
+ console.log(`${chalk.blue(value.name)}: (no change)\n`);
106
+ }
107
+ }
108
+ : () => {};
109
const {parseConfigPragmaForTests} = require(PARSE_CONFIG_PRAGMA_PATH) as {
110
parseConfigPragmaForTests: typeof ParseConfigPragma;
111
};
112
113
// only try logging if we filtered out all but one fixture,
114
// since console log order is non-deterministic
74
- toggleLogging(shouldLog);
115
const result = await transformFixtureInput(
116
input,
117
fixturePath,
118
parseConfigPragmaForTests,
119
BabelPluginReactCompiler,
120
includeEvaluator,
121
+ debugIRLogger,
122
EffectEnum,
123
ValueKindEnum,
124
);