1// Copyright (c) Meta Platforms, Inc. and affiliates.2//3// This source code is licensed under the MIT license found in the4// LICENSE file in the root directory of this source tree.56//! Verbose debug printer for ReactiveFunction.7//!8//! Produces output identical to the TS `printDebugReactiveFunction`.9//! Delegates shared formatting (Places, Identifiers, Scopes, Types,10//! InstructionValues, Effects, Errors) to `react_compiler_hir::print::PrintFormatter`.1112use react_compiler_hir::environment::Environment;13use react_compiler_hir::print::{self, PrintFormatter};14use react_compiler_hir::{15 HirFunction, ParamPattern, ReactiveBlock, ReactiveFunction, ReactiveInstruction,16 ReactiveStatement, ReactiveTerminal, ReactiveTerminalStatement, ReactiveValue,17};1819// =============================================================================20// DebugPrinter — thin wrapper around PrintFormatter for reactive-specific logic21// =============================================================================2223pub struct DebugPrinter<'a> {24 pub fmt: PrintFormatter<'a>,25 /// Optional formatter for HIR functions (used for inner functions in FunctionExpression/ObjectMethod)26 pub hir_formatter: Option<&'a HirFunctionFormatter>,27}2829impl<'a> DebugPrinter<'a> {30 pub fn new(env: &'a Environment) -> Self {31 Self {32 fmt: PrintFormatter::new(env),33 hir_formatter: None,34 }35 }3637 // =========================================================================38 // ReactiveFunction39 // =========================================================================4041 pub fn format_reactive_function(&mut self, func: &ReactiveFunction) {42 self.fmt.indent();43 self.fmt.line(&format!(44 "id: {}",45 match &func.id {46 Some(id) => format!("\"{}\"", id),47 None => "null".to_string(),48 }49 ));50 self.fmt.line(&format!(51 "name_hint: {}",52 match &func.name_hint {53 Some(h) => format!("\"{}\"", h),54 None => "null".to_string(),55 }56 ));57 self.fmt.line(&format!("generator: {}", func.generator));58 self.fmt.line(&format!("is_async: {}", func.is_async));59 self.fmt60 .line(&format!("loc: {}", print::format_loc(&func.loc)));6162 // params63 self.fmt.line("params:");64 self.fmt.indent();65 for (i, param) in func.params.iter().enumerate() {66 match param {67 ParamPattern::Place(place) => {68 self.fmt.format_place_field(&format!("[{}]", i), place);69 }70 ParamPattern::Spread(spread) => {71 self.fmt.line(&format!("[{}] Spread:", i));72 self.fmt.indent();73 self.fmt.format_place_field("place", &spread.place);74 self.fmt.dedent();75 }76 }77 }78 self.fmt.dedent();7980 // directives81 self.fmt.line("directives:");82 self.fmt.indent();83 for (i, d) in func.directives.iter().enumerate() {84 self.fmt.line(&format!("[{}] \"{}\"", i, d));85 }86 self.fmt.dedent();8788 self.fmt.line("");89 self.fmt.line("Body:");90 self.fmt.indent();91 self.format_reactive_block(&func.body);92 self.fmt.dedent();93 self.fmt.dedent();94 }9596 // =========================================================================97 // ReactiveBlock98 // =========================================================================99100 fn format_reactive_block(&mut self, block: &ReactiveBlock) {101 for stmt in block.iter() {102 self.format_reactive_statement(stmt);103 }104 }105106 fn format_reactive_statement(&mut self, stmt: &ReactiveStatement) {107 match stmt {108 ReactiveStatement::Instruction(instr) => {109 self.format_reactive_instruction_block(instr);110 }111 ReactiveStatement::Terminal(term) => {112 self.fmt.line("ReactiveTerminalStatement {");113 self.fmt.indent();114 self.format_terminal_statement(term);115 self.fmt.dedent();116 self.fmt.line("}");117 }118 ReactiveStatement::Scope(scope) => {119 self.fmt.line("ReactiveScopeBlock {");120 self.fmt.indent();121 self.fmt.format_scope_field("scope", scope.scope);122 self.fmt.line("instructions:");123 self.fmt.indent();124 self.format_reactive_block(&scope.instructions);125 self.fmt.dedent();126 self.fmt.dedent();127 self.fmt.line("}");128 }129 ReactiveStatement::PrunedScope(scope) => {130 self.fmt.line("PrunedReactiveScopeBlock {");131 self.fmt.indent();132 self.fmt.format_scope_field("scope", scope.scope);133 self.fmt.line("instructions:");134 self.fmt.indent();135 self.format_reactive_block(&scope.instructions);136 self.fmt.dedent();137 self.fmt.dedent();138 self.fmt.line("}");139 }140 }141 }142143 // =========================================================================144 // ReactiveInstruction145 // =========================================================================146147 fn format_reactive_instruction_block(&mut self, instr: &ReactiveInstruction) {148 self.fmt.line("ReactiveInstruction {");149 self.fmt.indent();150 self.format_reactive_instruction(instr);151 self.fmt.dedent();152 self.fmt.line("}");153 }154155 fn format_reactive_instruction(&mut self, instr: &ReactiveInstruction) {156 self.fmt.line(&format!("id: {}", instr.id.0));157 match &instr.lvalue {158 Some(place) => self.fmt.format_place_field("lvalue", place),159 None => self.fmt.line("lvalue: null"),160 }161 self.fmt.line("value:");162 self.fmt.indent();163 self.format_reactive_value(&instr.value);164 self.fmt.dedent();165 match &instr.effects {166 Some(effects) => {167 self.fmt.line("effects:");168 self.fmt.indent();169 for (i, eff) in effects.iter().enumerate() {170 self.fmt171 .line(&format!("[{}] {}", i, self.fmt.format_effect(eff)));172 }173 self.fmt.dedent();174 }175 None => self.fmt.line("effects: null"),176 }177 self.fmt178 .line(&format!("loc: {}", print::format_loc(&instr.loc)));179 }180181 // =========================================================================182 // ReactiveValue183 // =========================================================================184185 fn format_reactive_value(&mut self, value: &ReactiveValue) {186 match value {187 ReactiveValue::Instruction(iv) => {188 // Build the inner function formatter callback if we have an hir_formatter189 let hir_formatter = self.hir_formatter;190 let inner_func_cb: Option<Box<dyn Fn(&mut PrintFormatter, &HirFunction) + '_>> =191 hir_formatter.map(|hf| {192 Box::new(move |fmt: &mut PrintFormatter, func: &HirFunction| {193 hf(fmt, func);194 })195 as Box<dyn Fn(&mut PrintFormatter, &HirFunction) + '_>196 });197 self.fmt.format_instruction_value(198 iv,199 inner_func_cb200 .as_ref()201 .map(|cb| cb.as_ref() as &dyn Fn(&mut PrintFormatter, &HirFunction)),202 );203 }204 ReactiveValue::LogicalExpression {205 operator,206 left,207 right,208 loc,209 } => {210 self.fmt.line("LogicalExpression {");211 self.fmt.indent();212 self.fmt.line(&format!("operator: \"{}\"", operator));213 self.fmt.line("left:");214 self.fmt.indent();215 self.format_reactive_value(left);216 self.fmt.dedent();217 self.fmt.line("right:");218 self.fmt.indent();219 self.format_reactive_value(right);220 self.fmt.dedent();221 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));222 self.fmt.dedent();223 self.fmt.line("}");224 }225 ReactiveValue::ConditionalExpression {226 test,227 consequent,228 alternate,229 loc,230 } => {231 self.fmt.line("ConditionalExpression {");232 self.fmt.indent();233 self.fmt.line("test:");234 self.fmt.indent();235 self.format_reactive_value(test);236 self.fmt.dedent();237 self.fmt.line("consequent:");238 self.fmt.indent();239 self.format_reactive_value(consequent);240 self.fmt.dedent();241 self.fmt.line("alternate:");242 self.fmt.indent();243 self.format_reactive_value(alternate);244 self.fmt.dedent();245 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));246 self.fmt.dedent();247 self.fmt.line("}");248 }249 ReactiveValue::SequenceExpression {250 instructions,251 id,252 value,253 loc,254 } => {255 self.fmt.line("SequenceExpression {");256 self.fmt.indent();257 self.fmt.line("instructions:");258 self.fmt.indent();259 for (i, instr) in instructions.iter().enumerate() {260 self.fmt.line(&format!("[{}]:", i));261 self.fmt.indent();262 self.format_reactive_instruction_block(instr);263 self.fmt.dedent();264 }265 self.fmt.dedent();266 self.fmt.line(&format!("id: {}", id.0));267 self.fmt.line("value:");268 self.fmt.indent();269 self.format_reactive_value(value);270 self.fmt.dedent();271 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));272 self.fmt.dedent();273 self.fmt.line("}");274 }275 ReactiveValue::OptionalExpression {276 id,277 value,278 optional,279 loc,280 } => {281 self.fmt.line("OptionalExpression {");282 self.fmt.indent();283 self.fmt.line(&format!("id: {}", id.0));284 self.fmt.line("value:");285 self.fmt.indent();286 self.format_reactive_value(value);287 self.fmt.dedent();288 self.fmt.line(&format!("optional: {}", optional));289 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));290 self.fmt.dedent();291 self.fmt.line("}");292 }293 }294 }295296 // =========================================================================297 // ReactiveTerminal298 // =========================================================================299300 fn format_terminal_statement(&mut self, stmt: &ReactiveTerminalStatement) {301 match &stmt.label {302 Some(label) => {303 self.fmt.line(&format!(304 "label: {{ id: bb{}, implicit: {} }}",305 label.id.0, label.implicit306 ));307 }308 None => self.fmt.line("label: null"),309 }310 self.fmt.line("terminal:");311 self.fmt.indent();312 self.format_reactive_terminal(&stmt.terminal);313 self.fmt.dedent();314 }315316 fn format_reactive_terminal(&mut self, terminal: &ReactiveTerminal) {317 match terminal {318 ReactiveTerminal::Break {319 target,320 id,321 target_kind,322 loc,323 } => {324 self.fmt.line("Break {");325 self.fmt.indent();326 self.fmt.line(&format!("target: bb{}", target.0));327 self.fmt.line(&format!("id: {}", id.0));328 self.fmt.line(&format!("targetKind: \"{}\"", target_kind));329 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));330 self.fmt.dedent();331 self.fmt.line("}");332 }333 ReactiveTerminal::Continue {334 target,335 id,336 target_kind,337 loc,338 } => {339 self.fmt.line("Continue {");340 self.fmt.indent();341 self.fmt.line(&format!("target: bb{}", target.0));342 self.fmt.line(&format!("id: {}", id.0));343 self.fmt.line(&format!("targetKind: \"{}\"", target_kind));344 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));345 self.fmt.dedent();346 self.fmt.line("}");347 }348 ReactiveTerminal::Return { value, id, loc } => {349 self.fmt.line("Return {");350 self.fmt.indent();351 self.fmt.format_place_field("value", value);352 self.fmt.line(&format!("id: {}", id.0));353 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));354 self.fmt.dedent();355 self.fmt.line("}");356 }357 ReactiveTerminal::Throw { value, id, loc } => {358 self.fmt.line("Throw {");359 self.fmt.indent();360 self.fmt.format_place_field("value", value);361 self.fmt.line(&format!("id: {}", id.0));362 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));363 self.fmt.dedent();364 self.fmt.line("}");365 }366 ReactiveTerminal::Switch {367 test,368 cases,369 id,370 loc,371 } => {372 self.fmt.line("Switch {");373 self.fmt.indent();374 self.fmt.format_place_field("test", test);375 self.fmt.line("cases:");376 self.fmt.indent();377 for (i, case) in cases.iter().enumerate() {378 self.fmt.line(&format!("[{}] {{", i));379 self.fmt.indent();380 match &case.test {381 Some(p) => {382 self.fmt.format_place_field("test", p);383 }384 None => {385 self.fmt.line("test: null");386 }387 }388 match &case.block {389 Some(block) => {390 self.fmt.line("block:");391 self.fmt.indent();392 self.format_reactive_block(block);393 self.fmt.dedent();394 }395 None => self.fmt.line("block: undefined"),396 }397 self.fmt.dedent();398 self.fmt.line("}");399 }400 self.fmt.dedent();401 self.fmt.line(&format!("id: {}", id.0));402 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));403 self.fmt.dedent();404 self.fmt.line("}");405 }406 ReactiveTerminal::DoWhile {407 loop_block,408 test,409 id,410 loc,411 } => {412 self.fmt.line("DoWhile {");413 self.fmt.indent();414 self.fmt.line("loop:");415 self.fmt.indent();416 self.format_reactive_block(loop_block);417 self.fmt.dedent();418 self.fmt.line("test:");419 self.fmt.indent();420 self.format_reactive_value(test);421 self.fmt.dedent();422 self.fmt.line(&format!("id: {}", id.0));423 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));424 self.fmt.dedent();425 self.fmt.line("}");426 }427 ReactiveTerminal::While {428 test,429 loop_block,430 id,431 loc,432 } => {433 self.fmt.line("While {");434 self.fmt.indent();435 self.fmt.line("test:");436 self.fmt.indent();437 self.format_reactive_value(test);438 self.fmt.dedent();439 self.fmt.line("loop:");440 self.fmt.indent();441 self.format_reactive_block(loop_block);442 self.fmt.dedent();443 self.fmt.line(&format!("id: {}", id.0));444 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));445 self.fmt.dedent();446 self.fmt.line("}");447 }448 ReactiveTerminal::For {449 init,450 test,451 update,452 loop_block,453 id,454 loc,455 } => {456 self.fmt.line("For {");457 self.fmt.indent();458 self.fmt.line("init:");459 self.fmt.indent();460 self.format_reactive_value(init);461 self.fmt.dedent();462 self.fmt.line("test:");463 self.fmt.indent();464 self.format_reactive_value(test);465 self.fmt.dedent();466 match update {467 Some(u) => {468 self.fmt.line("update:");469 self.fmt.indent();470 self.format_reactive_value(u);471 self.fmt.dedent();472 }473 None => self.fmt.line("update: null"),474 }475 self.fmt.line("loop:");476 self.fmt.indent();477 self.format_reactive_block(loop_block);478 self.fmt.dedent();479 self.fmt.line(&format!("id: {}", id.0));480 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));481 self.fmt.dedent();482 self.fmt.line("}");483 }484 ReactiveTerminal::ForOf {485 init,486 test,487 loop_block,488 id,489 loc,490 } => {491 self.fmt.line("ForOf {");492 self.fmt.indent();493 self.fmt.line("init:");494 self.fmt.indent();495 self.format_reactive_value(init);496 self.fmt.dedent();497 self.fmt.line("test:");498 self.fmt.indent();499 self.format_reactive_value(test);500 self.fmt.dedent();501 self.fmt.line("loop:");502 self.fmt.indent();503 self.format_reactive_block(loop_block);504 self.fmt.dedent();505 self.fmt.line(&format!("id: {}", id.0));506 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));507 self.fmt.dedent();508 self.fmt.line("}");509 }510 ReactiveTerminal::ForIn {511 init,512 loop_block,513 id,514 loc,515 } => {516 self.fmt.line("ForIn {");517 self.fmt.indent();518 self.fmt.line("init:");519 self.fmt.indent();520 self.format_reactive_value(init);521 self.fmt.dedent();522 self.fmt.line("loop:");523 self.fmt.indent();524 self.format_reactive_block(loop_block);525 self.fmt.dedent();526 self.fmt.line(&format!("id: {}", id.0));527 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));528 self.fmt.dedent();529 self.fmt.line("}");530 }531 ReactiveTerminal::If {532 test,533 consequent,534 alternate,535 id,536 loc,537 } => {538 self.fmt.line("If {");539 self.fmt.indent();540 self.fmt.format_place_field("test", test);541 self.fmt.line("consequent:");542 self.fmt.indent();543 self.format_reactive_block(consequent);544 self.fmt.dedent();545 match alternate {546 Some(alt) => {547 self.fmt.line("alternate:");548 self.fmt.indent();549 self.format_reactive_block(alt);550 self.fmt.dedent();551 }552 None => self.fmt.line("alternate: null"),553 }554 self.fmt.line(&format!("id: {}", id.0));555 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));556 self.fmt.dedent();557 self.fmt.line("}");558 }559 ReactiveTerminal::Label { block, id, loc } => {560 self.fmt.line("Label {");561 self.fmt.indent();562 self.fmt.line("block:");563 self.fmt.indent();564 self.format_reactive_block(block);565 self.fmt.dedent();566 self.fmt.line(&format!("id: {}", id.0));567 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));568 self.fmt.dedent();569 self.fmt.line("}");570 }571 ReactiveTerminal::Try {572 block,573 handler_binding,574 handler,575 id,576 loc,577 } => {578 self.fmt.line("Try {");579 self.fmt.indent();580 self.fmt.line("block:");581 self.fmt.indent();582 self.format_reactive_block(block);583 self.fmt.dedent();584 match handler_binding {585 Some(p) => self.fmt.format_place_field("handlerBinding", p),586 None => self.fmt.line("handlerBinding: null"),587 }588 self.fmt.line("handler:");589 self.fmt.indent();590 self.format_reactive_block(handler);591 self.fmt.dedent();592 self.fmt.line(&format!("id: {}", id.0));593 self.fmt.line(&format!("loc: {}", print::format_loc(loc)));594 self.fmt.dedent();595 self.fmt.line("}");596 }597 }598 }599}600601// =============================================================================602// Entry point603// =============================================================================604605/// Type alias for a function formatter callback that can print HIR functions.606/// Used to format inner functions in FunctionExpression/ObjectMethod values.607pub type HirFunctionFormatter = dyn Fn(&mut PrintFormatter, &HirFunction);608609pub fn debug_reactive_function(func: &ReactiveFunction, env: &Environment) -> String {610 debug_reactive_function_with_formatter(func, env, None)611}612613pub fn debug_reactive_function_with_formatter(614 func: &ReactiveFunction,615 env: &Environment,616 hir_formatter: Option<&HirFunctionFormatter>,617) -> String {618 let mut printer = DebugPrinter::new(env);619 printer.hir_formatter = hir_formatter;620 printer.format_reactive_function(func);621622 // TODO: Print outlined functions when they've been converted to reactive form623624 printer.fmt.line("");625 printer.fmt.line("Environment:");626 printer.fmt.indent();627 printer.fmt.format_errors(&env.errors);628 printer.fmt.dedent();629630 printer.fmt.to_string_output()631}
Findings
✓ No findings reported for this file.