main
ts 697 lines 20.1 KB
Raw
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 /**
9 * TS test binary for the Rust port testing infrastructure.
10 *
11 * Implements the compiler pipeline independently (NOT using compile() or
12 * runWithEnvironment()), calling each pass function directly in the same
13 * sequence as the Rust binary. This ensures both sides have exactly matching
14 * behavior.
15 *
16 * Takes a compiler pass name and a fixture path, finds every top-level
17 * function, runs the pipeline up to the target pass for each, and prints
18 * a detailed debug representation to stdout.
19 *
20 * Usage: npx tsx compiler/scripts/ts-compile-fixture.mjs <pass> <fixture-path>
21 */
22
23 import {parse} from '@babel/parser';
24 import _traverse from '@babel/traverse';
25 const traverse: typeof _traverse = (_traverse as any).default || _traverse;
26 import * as t from '@babel/types';
27 import {type NodePath} from '@babel/traverse';
28 import fs from 'fs';
29 import path from 'path';
30
31 // --- Import pass functions directly from compiler source ---
32 import {lower} from '../packages/babel-plugin-react-compiler/src/HIR/BuildHIR';
33 import {
34 Environment,
35 type EnvironmentConfig,
36 type ReactFunctionType,
37 } from '../packages/babel-plugin-react-compiler/src/HIR/Environment';
38 import {findContextIdentifiers} from '../packages/babel-plugin-react-compiler/src/HIR/FindContextIdentifiers';
39 import {mergeConsecutiveBlocks} from '../packages/babel-plugin-react-compiler/src/HIR/MergeConsecutiveBlocks';
40 import {
41 assertConsistentIdentifiers,
42 assertTerminalSuccessorsExist,
43 assertTerminalPredsExist,
44 } from '../packages/babel-plugin-react-compiler/src/HIR';
45 import {assertValidBlockNesting} from '../packages/babel-plugin-react-compiler/src/HIR/AssertValidBlockNesting';
46 import {assertValidMutableRanges} from '../packages/babel-plugin-react-compiler/src/HIR/AssertValidMutableRanges';
47 import {pruneUnusedLabelsHIR} from '../packages/babel-plugin-react-compiler/src/HIR/PruneUnusedLabelsHIR';
48 import {mergeOverlappingReactiveScopesHIR} from '../packages/babel-plugin-react-compiler/src/HIR/MergeOverlappingReactiveScopesHIR';
49 import {buildReactiveScopeTerminalsHIR} from '../packages/babel-plugin-react-compiler/src/HIR/BuildReactiveScopeTerminalsHIR';
50 import {alignReactiveScopesToBlockScopesHIR} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes/AlignReactiveScopesToBlockScopesHIR';
51 import {flattenReactiveLoopsHIR} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes/FlattenReactiveLoopsHIR';
52 import {flattenScopesWithHooksOrUseHIR} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes/FlattenScopesWithHooksOrUseHIR';
53 import {propagateScopeDependenciesHIR} from '../packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR';
54
55 import {
56 pruneMaybeThrows,
57 constantPropagation,
58 deadCodeElimination,
59 } from '../packages/babel-plugin-react-compiler/src/Optimization';
60 import {optimizePropsMethodCalls} from '../packages/babel-plugin-react-compiler/src/Optimization/OptimizePropsMethodCalls';
61 import {outlineFunctions} from '../packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions';
62 import {optimizeForSSR} from '../packages/babel-plugin-react-compiler/src/Optimization/OptimizeForSSR';
63
64 import {
65 enterSSA,
66 eliminateRedundantPhi,
67 rewriteInstructionKindsBasedOnReassignment,
68 } from '../packages/babel-plugin-react-compiler/src/SSA';
69 import {inferTypes} from '../packages/babel-plugin-react-compiler/src/TypeInference';
70
71 import {
72 analyseFunctions,
73 dropManualMemoization,
74 inferReactivePlaces,
75 inlineImmediatelyInvokedFunctionExpressions,
76 } from '../packages/babel-plugin-react-compiler/src/Inference';
77 import {inferMutationAliasingEffects} from '../packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects';
78 import {inferMutationAliasingRanges} from '../packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges';
79
80 import {
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';
98 import {alignMethodCallScopes} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes/AlignMethodCallScopes';
99 import {pruneAlwaysInvalidatingScopes} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneAlwaysInvalidatingScopes';
100 import {stabilizeBlockIds} from '../packages/babel-plugin-react-compiler/src/ReactiveScopes/StabilizeBlockIds';
101
102 import {nameAnonymousFunctions} from '../packages/babel-plugin-react-compiler/src/Transform/NameAnonymousFunctions';
103
104 import {
105 validateContextVariableLValues,
106 validateHooksUsage,
107 validateNoCapitalizedCalls,
108 validateNoRefAccessInRender,
109 validateNoSetStateInRender,
110 validatePreservedManualMemoization,
111 validateUseMemo,
112 } from '../packages/babel-plugin-react-compiler/src/Validation';
113 import {validateLocalsNotReassignedAfterRender} from '../packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender';
114 import {validateNoFreezingKnownMutableFunctions} from '../packages/babel-plugin-react-compiler/src/Validation/ValidateNoFreezingKnownMutableFunctions';
115
116 import {CompilerError} from '../packages/babel-plugin-react-compiler/src/CompilerError';
117 import {type HIRFunction} from '../packages/babel-plugin-react-compiler/src/HIR/HIR';
118
119 import {parseConfigPragmaForTests} from '../packages/babel-plugin-react-compiler/src/Utils/TestUtils';
120 import {
121 parsePluginOptions,
122 ProgramContext,
123 } from '../packages/babel-plugin-react-compiler/src/Entrypoint';
124
125 import {debugPrintHIR} from './debug-print-hir.mjs';
126 import {debugPrintReactive} from './debug-print-reactive.mjs';
127 import {debugPrintError} from './debug-print-error.mjs';
128
129 // --- Arguments ---
130 const [passArg, fixturePath] = process.argv.slice(2);
131
132 if (!passArg || !fixturePath) {
133 console.error(
134 'Usage: npx tsx compiler/scripts/ts-compile-fixture.mjs <pass> <fixture-path>',
135 );
136 process.exit(1);
137 }
138
139 // --- Valid pass names (checkpoint names) ---
140 const 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 ]);
188
189 if (!VALID_PASSES.has(passArg)) {
190 console.error(`Unknown pass: ${passArg}`);
191 console.error(`Valid passes: ${[...VALID_PASSES].join(', ')}`);
192 process.exit(1);
193 }
194
195 // --- Read fixture source ---
196 const source = fs.readFileSync(fixturePath, 'utf8');
197 const firstLine = source.substring(0, source.indexOf('\n'));
198
199 // Determine language and source type
200 const language = firstLine.includes('@flow') ? 'flow' : 'typescript';
201 const sourceType = firstLine.includes('@script') ? 'script' : 'module';
202
203 // --- Parse config pragmas ---
204 const parsedOpts = parseConfigPragmaForTests(firstLine, {
205 compilationMode: 'all',
206 });
207 const envConfig: EnvironmentConfig = {
208 ...parsedOpts.environment,
209 assertValidMutableRanges: true,
210 };
211
212 // --- Parse the fixture ---
213 const plugins: Array<any> =
214 language === 'flow' ? ['flow', 'jsx'] : ['typescript', 'jsx'];
215 const inputAst = parse(source, {
216 sourceFilename: path.basename(fixturePath),
217 plugins,
218 sourceType,
219 errorRecovery: true,
220 });
221
222 // --- Find ALL top-level functions ---
223 const functionPaths: Array<
224 NodePath<
225 t.FunctionDeclaration | t.FunctionExpression | t.ArrowFunctionExpression
226 >
227 > = [];
228 let programPath: NodePath<t.Program> | null = null;
229
230 traverse(inputAst, {
231 Program(nodePath: NodePath<t.Program>) {
232 programPath = nodePath;
233 },
234 'FunctionDeclaration|FunctionExpression|ArrowFunctionExpression'(
235 nodePath: NodePath<
236 t.FunctionDeclaration | t.FunctionExpression | t.ArrowFunctionExpression
237 >,
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 });
251
252 function 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 }
278
279 if (functionPaths.length === 0) {
280 console.error('No top-level functions found in fixture');
281 process.exit(1);
282 }
283
284 // --- Compile each function ---
285 const filename = '/' + path.basename(fixturePath);
286 const allOutputs: string[] = [];
287
288 for (const fnPath of functionPaths) {
289 const output = compileOneFunction(fnPath);
290 if (output != null) {
291 allOutputs.push(output);
292 }
293 }
294
295 // --- Write output ---
296 if (allOutputs.length === 0) {
297 console.error('No functions produced output');
298 process.exit(1);
299 }
300 const finalOutput = allOutputs.join('\n---\n');
301 process.stdout.write(finalOutput);
302 if (!finalOutput.endsWith('\n')) {
303 process.stdout.write('\n');
304 }
305
306 // --- Run the pipeline for a single function, mirroring Rust's run_pipeline ---
307 function compileOneFunction(
308 fnPath: NodePath<
309 t.FunctionDeclaration | t.FunctionExpression | t.ArrowFunctionExpression
310 >,
311 ): string | null {
312 const contextIdentifiers = findContextIdentifiers(fnPath);
313 const env = new Environment(
314 fnPath.scope,
315 'Other' as ReactFunctionType,
316 'client', // outputMode
317 envConfig,
318 contextIdentifiers,
319 fnPath,
320 null, // logger
321 filename,
322 source,
323 new ProgramContext({
324 program: programPath!,
325 opts: parsedOpts,
326 filename,
327 code: source,
328 suppressions: [],
329 hasModuleScopeOptOut: false,
330 }),
331 );
332
333 const pass = passArg;
334
335 function formatEnvErrors(): string {
336 return debugPrintError(env.aggregateErrors());
337 }
338
339 function printHIR(hir: HIRFunction): string {
340 return debugPrintHIR(null, hir);
341 }
342
343 function checkpointHIR(hir: HIRFunction): string {
344 if (env.hasErrors()) {
345 return formatEnvErrors();
346 }
347 return printHIR(hir);
348 }
349
350 try {
351 // --- HIR Phase ---
352 const hir = lower(fnPath, env);
353 if (pass === 'HIR') {
354 return checkpointHIR(hir);
355 }
356
357 pruneMaybeThrows(hir);
358 if (pass === 'PruneMaybeThrows') {
359 return checkpointHIR(hir);
360 }
361
362 validateContextVariableLValues(hir);
363 validateUseMemo(hir);
364
365 if (env.enableDropManualMemoization) {
366 dropManualMemoization(hir);
367 }
368 if (pass === 'DropManualMemoization') {
369 return checkpointHIR(hir);
370 }
371
372 inlineImmediatelyInvokedFunctionExpressions(hir);
373 if (pass === 'InlineIIFEs') {
374 return checkpointHIR(hir);
375 }
376
377 mergeConsecutiveBlocks(hir);
378 if (pass === 'MergeConsecutiveBlocks') {
379 return checkpointHIR(hir);
380 }
381
382 assertConsistentIdentifiers(hir);
383 assertTerminalSuccessorsExist(hir);
384
385 enterSSA(hir);
386 if (pass === 'SSA') {
387 return checkpointHIR(hir);
388 }
389
390 eliminateRedundantPhi(hir);
391 if (pass === 'EliminateRedundantPhi') {
392 return checkpointHIR(hir);
393 }
394
395 assertConsistentIdentifiers(hir);
396
397 constantPropagation(hir);
398 if (pass === 'ConstantPropagation') {
399 return checkpointHIR(hir);
400 }
401
402 inferTypes(hir);
403 if (pass === 'InferTypes') {
404 return checkpointHIR(hir);
405 }
406
407 if (env.enableValidations) {
408 if (env.config.validateHooksUsage) {
409 validateHooksUsage(hir);
410 }
411 if (env.config.validateNoCapitalizedCalls) {
412 validateNoCapitalizedCalls(hir);
413 }
414 }
415
416 optimizePropsMethodCalls(hir);
417 if (pass === 'OptimizePropsMethodCalls') {
418 return checkpointHIR(hir);
419 }
420
421 analyseFunctions(hir);
422 if (pass === 'AnalyseFunctions') {
423 return checkpointHIR(hir);
424 }
425
426 inferMutationAliasingEffects(hir);
427 if (pass === 'InferMutationAliasingEffects') {
428 return checkpointHIR(hir);
429 }
430
431 if (env.outputMode === 'ssr') {
432 optimizeForSSR(hir);
433 }
434 if (pass === 'OptimizeForSSR') {
435 return checkpointHIR(hir);
436 }
437
438 deadCodeElimination(hir);
439 if (pass === 'DeadCodeElimination') {
440 return checkpointHIR(hir);
441 }
442
443 pruneMaybeThrows(hir);
444 if (pass === 'PruneMaybeThrows2') {
445 return checkpointHIR(hir);
446 }
447
448 inferMutationAliasingRanges(hir, {isFunctionExpression: false});
449 if (pass === 'InferMutationAliasingRanges') {
450 return checkpointHIR(hir);
451 }
452
453 if (env.enableValidations) {
454 validateLocalsNotReassignedAfterRender(hir);
455
456 if (env.config.assertValidMutableRanges) {
457 assertValidMutableRanges(hir);
458 }
459
460 if (env.config.validateRefAccessDuringRender) {
461 validateNoRefAccessInRender(hir);
462 }
463
464 if (env.config.validateNoSetStateInRender) {
465 validateNoSetStateInRender(hir);
466 }
467
468 validateNoFreezingKnownMutableFunctions(hir);
469 }
470
471 inferReactivePlaces(hir);
472 if (pass === 'InferReactivePlaces') {
473 return checkpointHIR(hir);
474 }
475
476 rewriteInstructionKindsBasedOnReassignment(hir);
477 if (pass === 'RewriteInstructionKinds') {
478 return checkpointHIR(hir);
479 }
480
481 if (env.enableMemoization) {
482 inferReactiveScopeVariables(hir);
483 }
484 if (pass === 'InferReactiveScopeVariables') {
485 return checkpointHIR(hir);
486 }
487
488 const fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir);
489 if (pass === 'MemoizeFbtOperands') {
490 return checkpointHIR(hir);
491 }
492
493 if (env.config.enableNameAnonymousFunctions) {
494 nameAnonymousFunctions(hir);
495 }
496 if (pass === 'NameAnonymousFunctions') {
497 return checkpointHIR(hir);
498 }
499
500 if (env.config.enableFunctionOutlining) {
501 outlineFunctions(hir, fbtOperands);
502 }
503 if (pass === 'OutlineFunctions') {
504 return checkpointHIR(hir);
505 }
506
507 alignMethodCallScopes(hir);
508 if (pass === 'AlignMethodCallScopes') {
509 return checkpointHIR(hir);
510 }
511
512 alignObjectMethodScopes(hir);
513 if (pass === 'AlignObjectMethodScopes') {
514 return checkpointHIR(hir);
515 }
516
517 pruneUnusedLabelsHIR(hir);
518 if (pass === 'PruneUnusedLabelsHIR') {
519 return checkpointHIR(hir);
520 }
521
522 alignReactiveScopesToBlockScopesHIR(hir);
523 if (pass === 'AlignReactiveScopesToBlockScopes') {
524 return checkpointHIR(hir);
525 }
526
527 mergeOverlappingReactiveScopesHIR(hir);
528 if (pass === 'MergeOverlappingReactiveScopes') {
529 return checkpointHIR(hir);
530 }
531
532 assertValidBlockNesting(hir);
533
534 buildReactiveScopeTerminalsHIR(hir);
535 if (pass === 'BuildReactiveScopeTerminals') {
536 return checkpointHIR(hir);
537 }
538
539 assertValidBlockNesting(hir);
540
541 flattenReactiveLoopsHIR(hir);
542 if (pass === 'FlattenReactiveLoops') {
543 return checkpointHIR(hir);
544 }
545
546 flattenScopesWithHooksOrUseHIR(hir);
547 if (pass === 'FlattenScopesWithHooksOrUse') {
548 return checkpointHIR(hir);
549 }
550
551 assertTerminalSuccessorsExist(hir);
552 assertTerminalPredsExist(hir);
553
554 propagateScopeDependenciesHIR(hir);
555 if (pass === 'PropagateScopeDependencies') {
556 return checkpointHIR(hir);
557 }
558
559 // --- 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 }
567
568 pruneUnusedLabels(reactiveFunction);
569 if (pass === 'PruneUnusedLabels') {
570 if (env.hasErrors()) {
571 return formatEnvErrors();
572 }
573 return debugPrintReactive(null, reactiveFunction);
574 }
575
576 pruneNonEscapingScopes(reactiveFunction);
577 if (pass === 'PruneNonEscapingScopes') {
578 if (env.hasErrors()) {
579 return formatEnvErrors();
580 }
581 return debugPrintReactive(null, reactiveFunction);
582 }
583
584 pruneNonReactiveDependencies(reactiveFunction);
585 if (pass === 'PruneNonReactiveDependencies') {
586 if (env.hasErrors()) {
587 return formatEnvErrors();
588 }
589 return debugPrintReactive(null, reactiveFunction);
590 }
591
592 pruneUnusedScopes(reactiveFunction);
593 if (pass === 'PruneUnusedScopes') {
594 if (env.hasErrors()) {
595 return formatEnvErrors();
596 }
597 return debugPrintReactive(null, reactiveFunction);
598 }
599
600 mergeReactiveScopesThatInvalidateTogether(reactiveFunction);
601 if (pass === 'MergeReactiveScopesThatInvalidateTogether') {
602 if (env.hasErrors()) {
603 return formatEnvErrors();
604 }
605 return debugPrintReactive(null, reactiveFunction);
606 }
607
608 pruneAlwaysInvalidatingScopes(reactiveFunction);
609 if (pass === 'PruneAlwaysInvalidatingScopes') {
610 if (env.hasErrors()) {
611 return formatEnvErrors();
612 }
613 return debugPrintReactive(null, reactiveFunction);
614 }
615
616 propagateEarlyReturns(reactiveFunction);
617 if (pass === 'PropagateEarlyReturns') {
618 if (env.hasErrors()) {
619 return formatEnvErrors();
620 }
621 return debugPrintReactive(null, reactiveFunction);
622 }
623
624 pruneUnusedLValues(reactiveFunction);
625 if (pass === 'PruneUnusedLValues') {
626 if (env.hasErrors()) {
627 return formatEnvErrors();
628 }
629 return debugPrintReactive(null, reactiveFunction);
630 }
631
632 promoteUsedTemporaries(reactiveFunction);
633 if (pass === 'PromoteUsedTemporaries') {
634 if (env.hasErrors()) {
635 return formatEnvErrors();
636 }
637 return debugPrintReactive(null, reactiveFunction);
638 }
639
640 extractScopeDeclarationsFromDestructuring(reactiveFunction);
641 if (pass === 'ExtractScopeDeclarationsFromDestructuring') {
642 if (env.hasErrors()) {
643 return formatEnvErrors();
644 }
645 return debugPrintReactive(null, reactiveFunction);
646 }
647
648 stabilizeBlockIds(reactiveFunction);
649 if (pass === 'StabilizeBlockIds') {
650 if (env.hasErrors()) {
651 return formatEnvErrors();
652 }
653 return debugPrintReactive(null, reactiveFunction);
654 }
655
656 const uniqueIdentifiers = renameVariables(reactiveFunction);
657 if (pass === 'RenameVariables') {
658 if (env.hasErrors()) {
659 return formatEnvErrors();
660 }
661 return debugPrintReactive(null, reactiveFunction);
662 }
663
664 pruneHoistedContexts(reactiveFunction);
665 if (pass === 'PruneHoistedContexts') {
666 if (env.hasErrors()) {
667 return formatEnvErrors();
668 }
669 return debugPrintReactive(null, reactiveFunction);
670 }
671
672 if (
673 env.config.enablePreserveExistingMemoizationGuarantees ||
674 env.config.validatePreserveExistingMemoizationGuarantees
675 ) {
676 validatePreservedManualMemoization(reactiveFunction);
677 }
678
679 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 }
689
690 return null;
691 } catch (e) {
692 if (e instanceof CompilerError) {
693 return debugPrintError(e);
694 }
695 throw e;
696 }
697 }