main
rs 734 lines 27.1 KB
Raw
1 use react_compiler_diagnostics::CompilerError;
2 use react_compiler_hir::environment::Environment;
3 use react_compiler_hir::print::{self, PrintFormatter};
4 use react_compiler_hir::{
5 BasicBlock, BlockId, HirFunction, Instruction, ParamPattern, Place, Terminal,
6 };
7
8 // =============================================================================
9 // DebugPrinter struct — thin wrapper around PrintFormatter for HIR-specific logic
10 // =============================================================================
11
12 struct DebugPrinter<'a> {
13 fmt: PrintFormatter<'a>,
14 }
15
16 impl<'a> DebugPrinter<'a> {
17 fn new(env: &'a Environment) -> Self {
18 Self {
19 fmt: PrintFormatter::new(env),
20 }
21 }
22
23 // =========================================================================
24 // Function
25 // =========================================================================
26
27 fn format_function(&mut self, func: &HirFunction) {
28 self.fmt.indent();
29 self.fmt.line(&format!(
30 "id: {}",
31 match &func.id {
32 Some(id) => format!("\"{}\"", id),
33 None => "null".to_string(),
34 }
35 ));
36 self.fmt.line(&format!(
37 "name_hint: {}",
38 match &func.name_hint {
39 Some(h) => format!("\"{}\"", h),
40 None => "null".to_string(),
41 }
42 ));
43 self.fmt.line(&format!("fn_type: {:?}", func.fn_type));
44 self.fmt.line(&format!("generator: {}", func.generator));
45 self.fmt.line(&format!("is_async: {}", func.is_async));
46 self.fmt
47 .line(&format!("loc: {}", print::format_loc(&func.loc)));
48
49 // params
50 self.fmt.line("params:");
51 self.fmt.indent();
52 for (i, param) in func.params.iter().enumerate() {
53 match param {
54 ParamPattern::Place(place) => {
55 self.fmt.format_place_field(&format!("[{}]", i), place);
56 }
57 ParamPattern::Spread(spread) => {
58 self.fmt.line(&format!("[{}] Spread:", i));
59 self.fmt.indent();
60 self.fmt.format_place_field("place", &spread.place);
61 self.fmt.dedent();
62 }
63 }
64 }
65 self.fmt.dedent();
66
67 // returns
68 self.fmt.line("returns:");
69 self.fmt.indent();
70 self.fmt.format_place_field("value", &func.returns);
71 self.fmt.dedent();
72
73 // context
74 self.fmt.line("context:");
75 self.fmt.indent();
76 for (i, place) in func.context.iter().enumerate() {
77 self.fmt.format_place_field(&format!("[{}]", i), place);
78 }
79 self.fmt.dedent();
80
81 // aliasing_effects
82 match &func.aliasing_effects {
83 Some(effects) => {
84 self.fmt.line("aliasingEffects:");
85 self.fmt.indent();
86 for (i, eff) in effects.iter().enumerate() {
87 self.fmt
88 .line(&format!("[{}] {}", i, self.fmt.format_effect(eff)));
89 }
90 self.fmt.dedent();
91 }
92 None => self.fmt.line("aliasingEffects: null"),
93 }
94
95 // directives
96 self.fmt.line("directives:");
97 self.fmt.indent();
98 for (i, d) in func.directives.iter().enumerate() {
99 self.fmt.line(&format!("[{}] \"{}\"", i, d));
100 }
101 self.fmt.dedent();
102
103 // return_type_annotation
104 self.fmt.line(&format!(
105 "returnTypeAnnotation: {}",
106 match &func.return_type_annotation {
107 Some(ann) => ann.clone(),
108 None => "null".to_string(),
109 }
110 ));
111
112 self.fmt.line("");
113 self.fmt.line("Blocks:");
114 self.fmt.indent();
115 for (block_id, block) in &func.body.blocks {
116 self.format_block(block_id, block, &func.instructions);
117 }
118 self.fmt.dedent();
119 self.fmt.dedent();
120 }
121
122 // =========================================================================
123 // Block
124 // =========================================================================
125
126 fn format_block(
127 &mut self,
128 block_id: &BlockId,
129 block: &BasicBlock,
130 instructions: &[Instruction],
131 ) {
132 self.fmt
133 .line(&format!("bb{} ({}):", block_id.0, block.kind));
134 self.fmt.indent();
135
136 // preds
137 let preds: Vec<String> = block.preds.iter().map(|p| format!("bb{}", p.0)).collect();
138 self.fmt.line(&format!("preds: [{}]", preds.join(", ")));
139
140 // phis
141 self.fmt.line("phis:");
142 self.fmt.indent();
143 for phi in &block.phis {
144 self.format_phi(phi);
145 }
146 self.fmt.dedent();
147
148 // instructions
149 self.fmt.line("instructions:");
150 self.fmt.indent();
151 for (index, instr_id) in block.instructions.iter().enumerate() {
152 let instr = &instructions[instr_id.0 as usize];
153 self.format_instruction(instr, index);
154 }
155 self.fmt.dedent();
156
157 // terminal
158 self.fmt.line("terminal:");
159 self.fmt.indent();
160 self.format_terminal(&block.terminal);
161 self.fmt.dedent();
162
163 self.fmt.dedent();
164 }
165
166 // =========================================================================
167 // Phi
168 // =========================================================================
169
170 fn format_phi(&mut self, phi: &react_compiler_hir::Phi) {
171 self.fmt.line("Phi {");
172 self.fmt.indent();
173 self.fmt.format_place_field("place", &phi.place);
174 self.fmt.line("operands:");
175 self.fmt.indent();
176 for (block_id, place) in &phi.operands {
177 self.fmt.line(&format!("bb{}:", block_id.0));
178 self.fmt.indent();
179 self.fmt.format_place_field("value", place);
180 self.fmt.dedent();
181 }
182 self.fmt.dedent();
183 self.fmt.dedent();
184 self.fmt.line("}");
185 }
186
187 // =========================================================================
188 // Instruction
189 // =========================================================================
190
191 fn format_instruction(&mut self, instr: &Instruction, index: usize) {
192 self.fmt.line(&format!("[{}] Instruction {{", index));
193 self.fmt.indent();
194 self.fmt.line(&format!("id: {}", instr.id.0));
195 self.fmt.format_place_field("lvalue", &instr.lvalue);
196 self.fmt.line("value:");
197 self.fmt.indent();
198 // For the HIR printer, inner functions are formatted via format_function
199 self.fmt.format_instruction_value(
200 &instr.value,
201 Some(&|fmt: &mut PrintFormatter, func: &HirFunction| {
202 // We need to recursively format the inner function
203 // Use a temporary DebugPrinter that shares the formatter state
204 let mut inner = DebugPrinter {
205 fmt: PrintFormatter {
206 env: fmt.env,
207 seen_identifiers: std::mem::take(&mut fmt.seen_identifiers),
208 seen_scopes: std::mem::take(&mut fmt.seen_scopes),
209 output: Vec::new(),
210 indent_level: fmt.indent_level,
211 },
212 };
213 inner.format_function(func);
214 // Write the output lines into the parent formatter
215 for line in &inner.fmt.output {
216 fmt.line_raw(line);
217 }
218 // Copy back the seen state
219 fmt.seen_identifiers = inner.fmt.seen_identifiers;
220 fmt.seen_scopes = inner.fmt.seen_scopes;
221 }),
222 );
223 self.fmt.dedent();
224 match &instr.effects {
225 Some(effects) => {
226 self.fmt.line("effects:");
227 self.fmt.indent();
228 for (i, eff) in effects.iter().enumerate() {
229 self.fmt
230 .line(&format!("[{}] {}", i, self.fmt.format_effect(eff)));
231 }
232 self.fmt.dedent();
233 }
234 None => self.fmt.line("effects: null"),
235 }
236 self.fmt
237 .line(&format!("loc: {}", print::format_loc(&instr.loc)));
238 self.fmt.dedent();
239 self.fmt.line("}");
240 }
241
242 // =========================================================================
243 // Terminal
244 // =========================================================================
245
246 fn format_terminal(&mut self, terminal: &Terminal) {
247 match terminal {
248 Terminal::If {
249 test,
250 consequent,
251 alternate,
252 fallthrough,
253 id,
254 loc,
255 } => {
256 self.fmt.line("If {");
257 self.fmt.indent();
258 self.fmt.line(&format!("id: {}", id.0));
259 self.fmt.format_place_field("test", test);
260 self.fmt.line(&format!("consequent: bb{}", consequent.0));
261 self.fmt.line(&format!("alternate: bb{}", alternate.0));
262 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
263 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
264 self.fmt.dedent();
265 self.fmt.line("}");
266 }
267 Terminal::Branch {
268 test,
269 consequent,
270 alternate,
271 fallthrough,
272 id,
273 loc,
274 } => {
275 self.fmt.line("Branch {");
276 self.fmt.indent();
277 self.fmt.line(&format!("id: {}", id.0));
278 self.fmt.format_place_field("test", test);
279 self.fmt.line(&format!("consequent: bb{}", consequent.0));
280 self.fmt.line(&format!("alternate: bb{}", alternate.0));
281 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
282 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
283 self.fmt.dedent();
284 self.fmt.line("}");
285 }
286 Terminal::Logical {
287 operator,
288 test,
289 fallthrough,
290 id,
291 loc,
292 } => {
293 self.fmt.line("Logical {");
294 self.fmt.indent();
295 self.fmt.line(&format!("id: {}", id.0));
296 self.fmt.line(&format!("operator: \"{}\"", operator));
297 self.fmt.line(&format!("test: bb{}", test.0));
298 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
299 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
300 self.fmt.dedent();
301 self.fmt.line("}");
302 }
303 Terminal::Ternary {
304 test,
305 fallthrough,
306 id,
307 loc,
308 } => {
309 self.fmt.line("Ternary {");
310 self.fmt.indent();
311 self.fmt.line(&format!("id: {}", id.0));
312 self.fmt.line(&format!("test: bb{}", test.0));
313 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
314 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
315 self.fmt.dedent();
316 self.fmt.line("}");
317 }
318 Terminal::Optional {
319 optional,
320 test,
321 fallthrough,
322 id,
323 loc,
324 } => {
325 self.fmt.line("Optional {");
326 self.fmt.indent();
327 self.fmt.line(&format!("id: {}", id.0));
328 self.fmt.line(&format!("optional: {}", optional));
329 self.fmt.line(&format!("test: bb{}", test.0));
330 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
331 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
332 self.fmt.dedent();
333 self.fmt.line("}");
334 }
335 Terminal::Throw { value, id, loc } => {
336 self.fmt.line("Throw {");
337 self.fmt.indent();
338 self.fmt.line(&format!("id: {}", id.0));
339 self.fmt.format_place_field("value", value);
340 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
341 self.fmt.dedent();
342 self.fmt.line("}");
343 }
344 Terminal::Return {
345 value,
346 return_variant,
347 id,
348 loc,
349 effects,
350 } => {
351 self.fmt.line("Return {");
352 self.fmt.indent();
353 self.fmt.line(&format!("id: {}", id.0));
354 self.fmt
355 .line(&format!("returnVariant: {:?}", return_variant));
356 self.fmt.format_place_field("value", value);
357 match effects {
358 Some(e) => {
359 self.fmt.line("effects:");
360 self.fmt.indent();
361 for (i, eff) in e.iter().enumerate() {
362 self.fmt
363 .line(&format!("[{}] {}", i, self.fmt.format_effect(eff)));
364 }
365 self.fmt.dedent();
366 }
367 None => self.fmt.line("effects: null"),
368 }
369 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
370 self.fmt.dedent();
371 self.fmt.line("}");
372 }
373 Terminal::Goto {
374 block,
375 variant,
376 id,
377 loc,
378 } => {
379 self.fmt.line("Goto {");
380 self.fmt.indent();
381 self.fmt.line(&format!("id: {}", id.0));
382 self.fmt.line(&format!("block: bb{}", block.0));
383 self.fmt.line(&format!("variant: {:?}", variant));
384 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
385 self.fmt.dedent();
386 self.fmt.line("}");
387 }
388 Terminal::Switch {
389 test,
390 cases,
391 fallthrough,
392 id,
393 loc,
394 } => {
395 self.fmt.line("Switch {");
396 self.fmt.indent();
397 self.fmt.line(&format!("id: {}", id.0));
398 self.fmt.format_place_field("test", test);
399 self.fmt.line("cases:");
400 self.fmt.indent();
401 for (i, case) in cases.iter().enumerate() {
402 match &case.test {
403 Some(p) => {
404 self.fmt.line(&format!("[{}] Case {{", i));
405 self.fmt.indent();
406 self.fmt.format_place_field("test", p);
407 self.fmt.line(&format!("block: bb{}", case.block.0));
408 self.fmt.dedent();
409 self.fmt.line("}");
410 }
411 None => {
412 self.fmt
413 .line(&format!("[{}] Default {{ block: bb{} }}", i, case.block.0));
414 }
415 }
416 }
417 self.fmt.dedent();
418 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
419 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
420 self.fmt.dedent();
421 self.fmt.line("}");
422 }
423 Terminal::DoWhile {
424 loop_block,
425 test,
426 fallthrough,
427 id,
428 loc,
429 } => {
430 self.fmt.line("DoWhile {");
431 self.fmt.indent();
432 self.fmt.line(&format!("id: {}", id.0));
433 self.fmt.line(&format!("loop: bb{}", loop_block.0));
434 self.fmt.line(&format!("test: bb{}", test.0));
435 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
436 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
437 self.fmt.dedent();
438 self.fmt.line("}");
439 }
440 Terminal::While {
441 test,
442 loop_block,
443 fallthrough,
444 id,
445 loc,
446 } => {
447 self.fmt.line("While {");
448 self.fmt.indent();
449 self.fmt.line(&format!("id: {}", id.0));
450 self.fmt.line(&format!("test: bb{}", test.0));
451 self.fmt.line(&format!("loop: bb{}", loop_block.0));
452 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
453 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
454 self.fmt.dedent();
455 self.fmt.line("}");
456 }
457 Terminal::For {
458 init,
459 test,
460 update,
461 loop_block,
462 fallthrough,
463 id,
464 loc,
465 } => {
466 self.fmt.line("For {");
467 self.fmt.indent();
468 self.fmt.line(&format!("id: {}", id.0));
469 self.fmt.line(&format!("init: bb{}", init.0));
470 self.fmt.line(&format!("test: bb{}", test.0));
471 self.fmt.line(&format!(
472 "update: {}",
473 match update {
474 Some(u) => format!("bb{}", u.0),
475 None => "null".to_string(),
476 }
477 ));
478 self.fmt.line(&format!("loop: bb{}", loop_block.0));
479 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
480 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
481 self.fmt.dedent();
482 self.fmt.line("}");
483 }
484 Terminal::ForOf {
485 init,
486 test,
487 loop_block,
488 fallthrough,
489 id,
490 loc,
491 } => {
492 self.fmt.line("ForOf {");
493 self.fmt.indent();
494 self.fmt.line(&format!("id: {}", id.0));
495 self.fmt.line(&format!("init: bb{}", init.0));
496 self.fmt.line(&format!("test: bb{}", test.0));
497 self.fmt.line(&format!("loop: bb{}", loop_block.0));
498 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
499 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
500 self.fmt.dedent();
501 self.fmt.line("}");
502 }
503 Terminal::ForIn {
504 init,
505 loop_block,
506 fallthrough,
507 id,
508 loc,
509 } => {
510 self.fmt.line("ForIn {");
511 self.fmt.indent();
512 self.fmt.line(&format!("id: {}", id.0));
513 self.fmt.line(&format!("init: bb{}", init.0));
514 self.fmt.line(&format!("loop: bb{}", loop_block.0));
515 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
516 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
517 self.fmt.dedent();
518 self.fmt.line("}");
519 }
520 Terminal::Label {
521 block,
522 fallthrough,
523 id,
524 loc,
525 } => {
526 self.fmt.line("Label {");
527 self.fmt.indent();
528 self.fmt.line(&format!("id: {}", id.0));
529 self.fmt.line(&format!("block: bb{}", block.0));
530 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
531 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
532 self.fmt.dedent();
533 self.fmt.line("}");
534 }
535 Terminal::Sequence {
536 block,
537 fallthrough,
538 id,
539 loc,
540 } => {
541 self.fmt.line("Sequence {");
542 self.fmt.indent();
543 self.fmt.line(&format!("id: {}", id.0));
544 self.fmt.line(&format!("block: bb{}", block.0));
545 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
546 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
547 self.fmt.dedent();
548 self.fmt.line("}");
549 }
550 Terminal::Unreachable { id, loc } => {
551 self.fmt.line(&format!(
552 "Unreachable {{ id: {}, loc: {} }}",
553 id.0,
554 print::format_loc(loc)
555 ));
556 }
557 Terminal::Unsupported { id, loc } => {
558 self.fmt.line(&format!(
559 "Unsupported {{ id: {}, loc: {} }}",
560 id.0,
561 print::format_loc(loc)
562 ));
563 }
564 Terminal::MaybeThrow {
565 continuation,
566 handler,
567 id,
568 loc,
569 effects,
570 } => {
571 self.fmt.line("MaybeThrow {");
572 self.fmt.indent();
573 self.fmt.line(&format!("id: {}", id.0));
574 self.fmt
575 .line(&format!("continuation: bb{}", continuation.0));
576 self.fmt.line(&format!(
577 "handler: {}",
578 match handler {
579 Some(h) => format!("bb{}", h.0),
580 None => "null".to_string(),
581 }
582 ));
583 match effects {
584 Some(e) => {
585 self.fmt.line("effects:");
586 self.fmt.indent();
587 for (i, eff) in e.iter().enumerate() {
588 self.fmt
589 .line(&format!("[{}] {}", i, self.fmt.format_effect(eff)));
590 }
591 self.fmt.dedent();
592 }
593 None => self.fmt.line("effects: null"),
594 }
595 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
596 self.fmt.dedent();
597 self.fmt.line("}");
598 }
599 Terminal::Scope {
600 fallthrough,
601 block,
602 scope,
603 id,
604 loc,
605 } => {
606 self.fmt.line("Scope {");
607 self.fmt.indent();
608 self.fmt.line(&format!("id: {}", id.0));
609 self.fmt.format_scope_field("scope", *scope);
610 self.fmt.line(&format!("block: bb{}", block.0));
611 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
612 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
613 self.fmt.dedent();
614 self.fmt.line("}");
615 }
616 Terminal::PrunedScope {
617 fallthrough,
618 block,
619 scope,
620 id,
621 loc,
622 } => {
623 self.fmt.line("PrunedScope {");
624 self.fmt.indent();
625 self.fmt.line(&format!("id: {}", id.0));
626 self.fmt.format_scope_field("scope", *scope);
627 self.fmt.line(&format!("block: bb{}", block.0));
628 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
629 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
630 self.fmt.dedent();
631 self.fmt.line("}");
632 }
633 Terminal::Try {
634 block,
635 handler_binding,
636 handler,
637 fallthrough,
638 id,
639 loc,
640 } => {
641 self.fmt.line("Try {");
642 self.fmt.indent();
643 self.fmt.line(&format!("id: {}", id.0));
644 self.fmt.line(&format!("block: bb{}", block.0));
645 self.fmt.line(&format!("handler: bb{}", handler.0));
646 match handler_binding {
647 Some(p) => self.fmt.format_place_field("handlerBinding", p),
648 None => self.fmt.line("handlerBinding: null"),
649 }
650 self.fmt.line(&format!("fallthrough: bb{}", fallthrough.0));
651 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));
652 self.fmt.dedent();
653 self.fmt.line("}");
654 }
655 }
656 }
657 }
658
659 // =============================================================================
660 // Entry point
661 // =============================================================================
662
663 pub fn debug_hir(hir: &HirFunction, env: &Environment) -> String {
664 let mut printer = DebugPrinter::new(env);
665 printer.format_function(hir);
666
667 // Print outlined functions (matches TS DebugPrintHIR.ts: printDebugHIR)
668 for outlined in env.get_outlined_functions() {
669 printer.fmt.line("");
670 printer.format_function(&outlined.func);
671 }
672
673 printer.fmt.line("");
674 printer.fmt.line("Environment:");
675 printer.fmt.indent();
676 printer.fmt.format_errors(&env.errors);
677 printer.fmt.dedent();
678
679 printer.fmt.to_string_output()
680 }
681
682 // =============================================================================
683 // Error formatting (kept for backward compatibility)
684 // =============================================================================
685
686 pub fn format_errors(error: &CompilerError) -> String {
687 let env = Environment::new();
688 let mut fmt = PrintFormatter::new(&env);
689 fmt.format_errors(error);
690 fmt.to_string_output()
691 }
692
693 /// Format an HIR function into a reactive PrintFormatter.
694 /// This bridges the two debug printers so inner functions in FunctionExpression/ObjectMethod
695 /// can be printed within the reactive function output.
696 pub fn format_hir_function_into(reactive_fmt: &mut PrintFormatter, func: &HirFunction) {
697 // Create a temporary DebugPrinter that shares the same environment
698 let mut printer = DebugPrinter {
699 fmt: PrintFormatter {
700 env: reactive_fmt.env,
701 seen_identifiers: std::mem::take(&mut reactive_fmt.seen_identifiers),
702 seen_scopes: std::mem::take(&mut reactive_fmt.seen_scopes),
703 output: Vec::new(),
704 indent_level: reactive_fmt.indent_level,
705 },
706 };
707 printer.format_function(func);
708
709 // Write the output lines into the reactive formatter
710 for line in &printer.fmt.output {
711 reactive_fmt.line_raw(line);
712 }
713 // Copy back the seen state
714 reactive_fmt.seen_identifiers = printer.fmt.seen_identifiers;
715 reactive_fmt.seen_scopes = printer.fmt.seen_scopes;
716 }
717
718 // =============================================================================
719 // Helpers for effect formatting (kept for backward compatibility)
720 // =============================================================================
721
722 #[allow(dead_code)]
723 fn format_place_short(place: &Place, env: &Environment) -> String {
724 let ident = &env.identifiers[place.identifier.0 as usize];
725 let name = match &ident.name {
726 Some(name) => name.value().to_string(),
727 None => String::new(),
728 };
729 let scope = match ident.scope {
730 Some(scope_id) => format!(":{}", scope_id.0),
731 None => String::new(),
732 };
733 format!("{}${}{}", name, place.identifier.0, scope)
734 }