1use react_compiler_diagnostics::CompilerError;2use react_compiler_hir::environment::Environment;3use react_compiler_hir::print::{self, PrintFormatter};4use react_compiler_hir::{5 BasicBlock, BlockId, HirFunction, Instruction, ParamPattern, Place, Terminal,6};78// =============================================================================9// DebugPrinter struct — thin wrapper around PrintFormatter for HIR-specific logic10// =============================================================================1112struct DebugPrinter<'a> {13 fmt: PrintFormatter<'a>,14}1516impl<'a> DebugPrinter<'a> {17 fn new(env: &'a Environment) -> Self {18 Self {19 fmt: PrintFormatter::new(env),20 }21 }2223 // =========================================================================24 // Function25 // =========================================================================2627 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.fmt47 .line(&format!("loc: {}", print::format_loc(&func.loc)));4849 // params50 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();6667 // returns68 self.fmt.line("returns:");69 self.fmt.indent();70 self.fmt.format_place_field("value", &func.returns);71 self.fmt.dedent();7273 // context74 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();8081 // aliasing_effects82 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.fmt88 .line(&format!("[{}] {}", i, self.fmt.format_effect(eff)));89 }90 self.fmt.dedent();91 }92 None => self.fmt.line("aliasingEffects: null"),93 }9495 // directives96 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();102103 // return_type_annotation104 self.fmt.line(&format!(105 "returnTypeAnnotation: {}",106 match &func.return_type_annotation {107 Some(ann) => ann.clone(),108 None => "null".to_string(),109 }110 ));111112 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 }121122 // =========================================================================123 // Block124 // =========================================================================125126 fn format_block(127 &mut self,128 block_id: &BlockId,129 block: &BasicBlock,130 instructions: &[Instruction],131 ) {132 self.fmt133 .line(&format!("bb{} ({}):", block_id.0, block.kind));134 self.fmt.indent();135136 // preds137 let preds: Vec<String> = block.preds.iter().map(|p| format!("bb{}", p.0)).collect();138 self.fmt.line(&format!("preds: [{}]", preds.join(", ")));139140 // phis141 self.fmt.line("phis:");142 self.fmt.indent();143 for phi in &block.phis {144 self.format_phi(phi);145 }146 self.fmt.dedent();147148 // instructions149 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();156157 // terminal158 self.fmt.line("terminal:");159 self.fmt.indent();160 self.format_terminal(&block.terminal);161 self.fmt.dedent();162163 self.fmt.dedent();164 }165166 // =========================================================================167 // Phi168 // =========================================================================169170 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 }186187 // =========================================================================188 // Instruction189 // =========================================================================190191 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_function199 self.fmt.format_instruction_value(200 &instr.value,201 Some(&|fmt: &mut PrintFormatter, func: &HirFunction| {202 // We need to recursively format the inner function203 // Use a temporary DebugPrinter that shares the formatter state204 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 formatter215 for line in &inner.fmt.output {216 fmt.line_raw(line);217 }218 // Copy back the seen state219 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.fmt230 .line(&format!("[{}] {}", i, self.fmt.format_effect(eff)));231 }232 self.fmt.dedent();233 }234 None => self.fmt.line("effects: null"),235 }236 self.fmt237 .line(&format!("loc: {}", print::format_loc(&instr.loc)));238 self.fmt.dedent();239 self.fmt.line("}");240 }241242 // =========================================================================243 // Terminal244 // =========================================================================245246 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.fmt355 .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.fmt363 .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.fmt413 .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.fmt575 .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.fmt589 .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}658659// =============================================================================660// Entry point661// =============================================================================662663pub fn debug_hir(hir: &HirFunction, env: &Environment) -> String {664 let mut printer = DebugPrinter::new(env);665 printer.format_function(hir);666667 // 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 }672673 printer.fmt.line("");674 printer.fmt.line("Environment:");675 printer.fmt.indent();676 printer.fmt.format_errors(&env.errors);677 printer.fmt.dedent();678679 printer.fmt.to_string_output()680}681682// =============================================================================683// Error formatting (kept for backward compatibility)684// =============================================================================685686pub 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}692693/// Format an HIR function into a reactive PrintFormatter.694/// This bridges the two debug printers so inner functions in FunctionExpression/ObjectMethod695/// can be printed within the reactive function output.696pub fn format_hir_function_into(reactive_fmt: &mut PrintFormatter, func: &HirFunction) {697 // Create a temporary DebugPrinter that shares the same environment698 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);708709 // Write the output lines into the reactive formatter710 for line in &printer.fmt.output {711 reactive_fmt.line_raw(line);712 }713 // Copy back the seen state714 reactive_fmt.seen_identifiers = printer.fmt.seen_identifiers;715 reactive_fmt.seen_scopes = printer.fmt.seen_scopes;716}717718// =============================================================================719// Helpers for effect formatting (kept for backward compatibility)720// =============================================================================721722#[allow(dead_code)]723fn 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}