main
rs 631 lines 23.5 KB
Raw
1 // Copyright (c) Meta Platforms, Inc. and affiliates.
2 //
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the root directory of this source tree.
5
6 //! 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`.
11
12 use react_compiler_hir::environment::Environment;
13 use react_compiler_hir::print::{self, PrintFormatter};
14 use react_compiler_hir::{
15 HirFunction, ParamPattern, ReactiveBlock, ReactiveFunction, ReactiveInstruction,
16 ReactiveStatement, ReactiveTerminal, ReactiveTerminalStatement, ReactiveValue,
17 };
18
19 // =============================================================================
20 // DebugPrinter — thin wrapper around PrintFormatter for reactive-specific logic
21 // =============================================================================
22
23 pub 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 }
28
29 impl<'a> DebugPrinter<'a> {
30 pub fn new(env: &'a Environment) -> Self {
31 Self {
32 fmt: PrintFormatter::new(env),
33 hir_formatter: None,
34 }
35 }
36
37 // =========================================================================
38 // ReactiveFunction
39 // =========================================================================
40
41 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.fmt
60 .line(&format!("loc: {}", print::format_loc(&func.loc)));
61
62 // params
63 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();
79
80 // directives
81 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();
87
88 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 }
95
96 // =========================================================================
97 // ReactiveBlock
98 // =========================================================================
99
100 fn format_reactive_block(&mut self, block: &ReactiveBlock) {
101 for stmt in block.iter() {
102 self.format_reactive_statement(stmt);
103 }
104 }
105
106 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 }
142
143 // =========================================================================
144 // ReactiveInstruction
145 // =========================================================================
146
147 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 }
154
155 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.fmt
171 .line(&format!("[{}] {}", i, self.fmt.format_effect(eff)));
172 }
173 self.fmt.dedent();
174 }
175 None => self.fmt.line("effects: null"),
176 }
177 self.fmt
178 .line(&format!("loc: {}", print::format_loc(&instr.loc)));
179 }
180
181 // =========================================================================
182 // ReactiveValue
183 // =========================================================================
184
185 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_formatter
189 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_cb
200 .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 }
295
296 // =========================================================================
297 // ReactiveTerminal
298 // =========================================================================
299
300 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.implicit
306 ));
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 }
315
316 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 }
600
601 // =============================================================================
602 // Entry point
603 // =============================================================================
604
605 /// Type alias for a function formatter callback that can print HIR functions.
606 /// Used to format inner functions in FunctionExpression/ObjectMethod values.
607 pub type HirFunctionFormatter = dyn Fn(&mut PrintFormatter, &HirFunction);
608
609 pub fn debug_reactive_function(func: &ReactiveFunction, env: &Environment) -> String {
610 debug_reactive_function_with_formatter(func, env, None)
611 }
612
613 pub 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);
621
622 // TODO: Print outlined functions when they've been converted to reactive form
623
624 printer.fmt.line("");
625 printer.fmt.line("Environment:");
626 printer.fmt.indent();
627 printer.fmt.format_errors(&env.errors);
628 printer.fmt.dedent();
629
630 printer.fmt.to_string_output()
631 }