Skip to main content

react_compiler_hir/
print.rs

1//! Shared formatting utilities for HIR debug printing.
2//!
3//! This module provides `PrintFormatter` — a stateful formatter that both
4//! `react_compiler::debug_print` (HIR printer) and
5//! `react_compiler_reactive_scopes::print_reactive_function` (reactive printer)
6//! delegate to for shared formatting logic.
7//!
8//! It also exports standalone formatting functions (format_loc, format_primitive, etc.)
9//! that require no state.
10
11use std::collections::HashSet;
12
13use react_compiler_diagnostics::CompilerError;
14use react_compiler_diagnostics::CompilerErrorOrDiagnostic;
15use react_compiler_diagnostics::SourceLocation;
16
17use crate::AliasingEffect;
18use crate::HirFunction;
19use crate::IdentifierId;
20use crate::IdentifierName;
21use crate::InstructionValue;
22use crate::LValue;
23use crate::MutationReason;
24use crate::Pattern;
25use crate::Place;
26use crate::PlaceOrSpreadOrHole;
27use crate::ScopeId;
28use crate::Type;
29use crate::environment::Environment;
30use crate::type_config::ValueKind;
31use crate::type_config::ValueReason;
32
33// =============================================================================
34// Standalone formatting functions (no state needed)
35// =============================================================================
36
37pub fn format_loc(loc: &Option<SourceLocation>) -> String {
38    match loc {
39        Some(l) => format_loc_value(l),
40        None => "generated".to_string(),
41    }
42}
43
44pub fn format_loc_value(loc: &SourceLocation) -> String {
45    format!(
46        "{}:{}-{}:{}",
47        loc.start.line, loc.start.column, loc.end.line, loc.end.column
48    )
49}
50
51/// Format a string like JS `JSON.stringify`: escape control chars and quotes
52/// but preserve non-ASCII unicode (e.g. U+00A0 nbsp) as literal characters.
53pub fn format_js_string(s: &str) -> String {
54            let mut result = String::with_capacity(s.len() + 2);
55            result.push('"');
56            for c in s.chars() {
57                match c {
58                    '"' => result.push_str("\\\""),
59                    '\\' => result.push_str("\\\\"),
60                    '\n' => result.push_str("\\n"),
61                    '\r' => result.push_str("\\r"),
62                    '\t' => result.push_str("\\t"),
63            '\u{0008}' => result.push_str("\\b"),
64            '\u{000c}' => result.push_str("\\f"),
65                    // Only escape C0 control chars (U+0000–U+001F), matching JS JSON.stringify.
66                    // Do NOT escape C1 controls (U+0080–U+009F) — JS outputs those as literal chars.
67                    c if (c as u32) <= 0x1F => {
68                result.push_str(&format!("\\u{:04x}", c as u32));
69                    }
70                    c => result.push(c),
71                }
72            }
73            result.push('"');
74            result
75        }
76
77pub fn format_primitive(prim: &crate::PrimitiveValue) -> String {
78    match prim {
79        crate::PrimitiveValue::Null => "null".to_string(),
80        crate::PrimitiveValue::Undefined => "undefined".to_string(),
81        crate::PrimitiveValue::Boolean(b) => format!("{}", b),
82        crate::PrimitiveValue::Number(n) => crate::format_js_number(n.value()),
83        crate::PrimitiveValue::String(s) => format_js_string(s),
84    }
85}
86
87pub fn format_property_literal(prop: &crate::PropertyLiteral) -> String {
88    match prop {
89        crate::PropertyLiteral::String(s) => s.clone(),
90        crate::PropertyLiteral::Number(n) => crate::format_js_number(n.value()),
91    }
92}
93
94pub fn format_object_property_key(key: &crate::ObjectPropertyKey) -> String {
95    match key {
96        crate::ObjectPropertyKey::String { name } => format!("String(\"{}\")", name),
97        crate::ObjectPropertyKey::Identifier { name } => {
98            format!("Identifier(\"{}\")", name)
99        }
100        crate::ObjectPropertyKey::Computed { name } => {
101            format!("Computed({})", name.identifier.0)
102        }
103        crate::ObjectPropertyKey::Number { name } => {
104            format!("Number({})", crate::format_js_number(name.value()))
105        }
106    }
107}
108
109pub fn format_non_local_binding(binding: &crate::NonLocalBinding) -> String {
110    match binding {
111        crate::NonLocalBinding::Global { name } => {
112            format!("Global {{ name: \"{}\" }}", name)
113        }
114        crate::NonLocalBinding::ModuleLocal { name } => {
115            format!("ModuleLocal {{ name: \"{}\" }}", name)
116        }
117        crate::NonLocalBinding::ImportDefault { name, module } => {
118            format!(
119                "ImportDefault {{ name: \"{}\", module: \"{}\" }}",
120                name, module
121            )
122        }
123        crate::NonLocalBinding::ImportNamespace { name, module } => {
124            format!(
125                "ImportNamespace {{ name: \"{}\", module: \"{}\" }}",
126                name, module
127            )
128        }
129        crate::NonLocalBinding::ImportSpecifier {
130            name,
131            module,
132            imported,
133        } => {
134            format!(
135                "ImportSpecifier {{ name: \"{}\", module: \"{}\", imported: \"{}\" }}",
136                name, module, imported
137            )
138        }
139    }
140}
141
142pub fn format_value_kind(kind: ValueKind) -> &'static str {
143    match kind {
144        ValueKind::Mutable => "mutable",
145        ValueKind::Frozen => "frozen",
146        ValueKind::Primitive => "primitive",
147        ValueKind::MaybeFrozen => "maybe-frozen",
148        ValueKind::Global => "global",
149        ValueKind::Context => "context",
150    }
151}
152
153pub fn format_value_reason(reason: ValueReason) -> &'static str {
154    match reason {
155        ValueReason::KnownReturnSignature => "known-return-signature",
156        ValueReason::State => "state",
157        ValueReason::ReducerState => "reducer-state",
158        ValueReason::Context => "context",
159        ValueReason::Effect => "effect",
160        ValueReason::HookCaptured => "hook-captured",
161        ValueReason::HookReturn => "hook-return",
162        ValueReason::Global => "global",
163        ValueReason::JsxCaptured => "jsx-captured",
164        ValueReason::StoreLocal => "store-local",
165        ValueReason::ReactiveFunctionArgument => "reactive-function-argument",
166        ValueReason::Other => "other",
167    }
168}
169
170// =============================================================================
171// PrintFormatter — shared stateful formatter
172// =============================================================================
173
174/// Shared formatter state used by both HIR and reactive printers.
175///
176/// Both `DebugPrinter` structs delegate to this for formatting shared constructs
177/// like Places, Identifiers, Scopes, Types, InstructionValues, etc.
178pub struct PrintFormatter<'a> {
179    pub env: &'a Environment,
180    pub seen_identifiers: HashSet<IdentifierId>,
181    pub seen_scopes: HashSet<ScopeId>,
182    pub output: Vec<String>,
183    pub indent_level: usize,
184}
185
186impl<'a> PrintFormatter<'a> {
187    pub fn new(env: &'a Environment) -> Self {
188        Self {
189            env,
190            seen_identifiers: HashSet::new(),
191            seen_scopes: HashSet::new(),
192            output: Vec::new(),
193            indent_level: 0,
194        }
195    }
196
197    pub fn line(&mut self, text: &str) {
198        let indent = "  ".repeat(self.indent_level);
199        self.output.push(format!("{}{}", indent, text));
200    }
201
202    /// Write a line without adding indentation (used when copying pre-formatted output)
203    pub fn line_raw(&mut self, text: &str) {
204        self.output.push(text.to_string());
205    }
206
207    pub fn indent(&mut self) {
208        self.indent_level += 1;
209    }
210
211    pub fn dedent(&mut self) {
212        self.indent_level -= 1;
213    }
214
215    pub fn to_string_output(&self) -> String {
216        self.output.join("\n")
217    }
218
219    // =========================================================================
220    // AliasingEffect
221    // =========================================================================
222
223    pub fn format_effect(&self, effect: &AliasingEffect) -> String {
224        match effect {
225            AliasingEffect::Freeze { value, reason } => {
226                format!(
227                    "Freeze {{ value: {}, reason: {} }}",
228                    value.identifier.0,
229                    format_value_reason(*reason)
230                )
231            }
232            AliasingEffect::Mutate { value, reason } => match reason {
233                Some(MutationReason::AssignCurrentProperty) => {
234                    format!(
235                        "Mutate {{ value: {}, reason: AssignCurrentProperty }}",
236                        value.identifier.0
237                    )
238                }
239                None => format!("Mutate {{ value: {} }}", value.identifier.0),
240            },
241            AliasingEffect::MutateConditionally { value } => {
242                format!("MutateConditionally {{ value: {} }}", value.identifier.0)
243            }
244            AliasingEffect::MutateTransitive { value } => {
245                format!("MutateTransitive {{ value: {} }}", value.identifier.0)
246            }
247            AliasingEffect::MutateTransitiveConditionally { value } => {
248                format!(
249                    "MutateTransitiveConditionally {{ value: {} }}",
250                    value.identifier.0
251                )
252            }
253            AliasingEffect::Capture { from, into } => {
254                format!(
255                    "Capture {{ into: {}, from: {} }}",
256                    into.identifier.0, from.identifier.0
257                )
258            }
259            AliasingEffect::Alias { from, into } => {
260                format!(
261                    "Alias {{ into: {}, from: {} }}",
262                    into.identifier.0, from.identifier.0
263                )
264            }
265            AliasingEffect::MaybeAlias { from, into } => {
266                format!(
267                    "MaybeAlias {{ into: {}, from: {} }}",
268                    into.identifier.0, from.identifier.0
269                )
270            }
271            AliasingEffect::Assign { from, into } => {
272                format!(
273                    "Assign {{ into: {}, from: {} }}",
274                    into.identifier.0, from.identifier.0
275                )
276            }
277            AliasingEffect::Create {
278                into,
279                value,
280                reason,
281            } => {
282                format!(
283                    "Create {{ into: {}, value: {}, reason: {} }}",
284                    into.identifier.0,
285                    format_value_kind(*value),
286                    format_value_reason(*reason)
287                )
288            }
289            AliasingEffect::CreateFrom { from, into } => {
290                format!(
291                    "CreateFrom {{ into: {}, from: {} }}",
292                    into.identifier.0, from.identifier.0
293                )
294            }
295            AliasingEffect::ImmutableCapture { from, into } => {
296                format!(
297                    "ImmutableCapture {{ into: {}, from: {} }}",
298                    into.identifier.0, from.identifier.0
299                )
300            }
301            AliasingEffect::Apply {
302                receiver,
303                function,
304                mutates_function,
305                args,
306                into,
307                ..
308            } => {
309                let args_str: Vec<String> = args
310                    .iter()
311                    .map(|a| match a {
312                        PlaceOrSpreadOrHole::Hole => "hole".to_string(),
313                        PlaceOrSpreadOrHole::Place(p) => p.identifier.0.to_string(),
314                        PlaceOrSpreadOrHole::Spread(s) => format!("...{}", s.place.identifier.0),
315                    })
316                    .collect();
317                format!(
318                    "Apply {{ into: {}, receiver: {}, function: {}, mutatesFunction: {}, args: [{}] }}",
319                    into.identifier.0,
320                    receiver.identifier.0,
321                    function.identifier.0,
322                    mutates_function,
323                    args_str.join(", ")
324                )
325            }
326            AliasingEffect::CreateFunction {
327                captures,
328                function_id: _,
329                into,
330            } => {
331                let cap_str: Vec<String> = captures
332                    .iter()
333                    .map(|p| p.identifier.0.to_string())
334                    .collect();
335                format!(
336                    "CreateFunction {{ into: {}, captures: [{}] }}",
337                    into.identifier.0,
338                    cap_str.join(", ")
339                )
340            }
341            AliasingEffect::MutateFrozen { place, error } => {
342                format!(
343                    "MutateFrozen {{ place: {}, reason: {:?} }}",
344                    place.identifier.0, error.reason
345                )
346            }
347            AliasingEffect::MutateGlobal { place, error } => {
348                format!(
349                    "MutateGlobal {{ place: {}, reason: {:?} }}",
350                    place.identifier.0, error.reason
351                )
352            }
353            AliasingEffect::Impure { place, error } => {
354                format!(
355                    "Impure {{ place: {}, reason: {:?} }}",
356                    place.identifier.0, error.reason
357                )
358            }
359            AliasingEffect::Render { place } => {
360                format!("Render {{ place: {} }}", place.identifier.0)
361            }
362        }
363    }
364
365    // =========================================================================
366    // Place (with identifier deduplication)
367    // =========================================================================
368
369    pub fn format_place_field(&mut self, field_name: &str, place: &Place) {
370        let is_seen = self.seen_identifiers.contains(&place.identifier);
371        if is_seen {
372            self.line(&format!(
373                "{}: Place {{ identifier: Identifier({}), effect: {}, reactive: {}, loc: {} }}",
374                field_name,
375                place.identifier.0,
376                place.effect,
377                place.reactive,
378                format_loc(&place.loc)
379            ));
380        } else {
381            self.line(&format!("{}: Place {{", field_name));
382            self.indent();
383            self.line("identifier:");
384            self.indent();
385            self.format_identifier(place.identifier);
386            self.dedent();
387            self.line(&format!("effect: {}", place.effect));
388            self.line(&format!("reactive: {}", place.reactive));
389            self.line(&format!("loc: {}", format_loc(&place.loc)));
390            self.dedent();
391            self.line("}");
392        }
393    }
394
395    // =========================================================================
396    // Identifier (first-seen expansion)
397    // =========================================================================
398
399    pub fn format_identifier(&mut self, id: IdentifierId) {
400        self.seen_identifiers.insert(id);
401        let ident = &self.env.identifiers[id.0 as usize];
402        self.line("Identifier {");
403        self.indent();
404        self.line(&format!("id: {}", ident.id.0));
405        self.line(&format!("declarationId: {}", ident.declaration_id.0));
406        match &ident.name {
407            Some(name) => {
408                let (kind, value) = match name {
409                    IdentifierName::Named(n) => ("named", n.as_str()),
410                    IdentifierName::Promoted(n) => ("promoted", n.as_str()),
411                };
412                self.line(&format!(
413                    "name: {{ kind: \"{}\", value: \"{}\" }}",
414                    kind, value
415                ));
416            }
417            None => self.line("name: null"),
418        }
419        // Print the identifier's mutable_range directly, matching the TS
420        // DebugPrintHIR which prints `identifier.mutableRange`. In TS,
421        // InferReactiveScopeVariables sets identifier.mutableRange = scope.range
422        // (shared reference), and AlignReactiveScopesToBlockScopesHIR syncs them.
423        // After MergeOverlappingReactiveScopesHIR repoints scopes, the TS
424        // identifier.mutableRange still references the OLD scope's range (stale),
425        // so we match by using ident.mutable_range directly (which is synced
426        // at the AlignReactiveScopesToBlockScopesHIR step but not re-synced
427        // after scope repointing in merge passes).
428        self.line(&format!(
429            "mutableRange: [{}:{}]",
430            ident.mutable_range.start.0, ident.mutable_range.end.0
431        ));
432        match ident.scope {
433            Some(scope_id) => self.format_scope_field("scope", scope_id),
434            None => self.line("scope: null"),
435        }
436        self.line(&format!("type: {}", self.format_type(ident.type_)));
437        self.line(&format!("loc: {}", format_loc(&ident.loc)));
438        self.dedent();
439        self.line("}");
440    }
441
442    // =========================================================================
443    // Scope (with deduplication)
444    // =========================================================================
445
446    pub fn format_scope_field(&mut self, field_name: &str, scope_id: ScopeId) {
447        let is_seen = self.seen_scopes.contains(&scope_id);
448        if is_seen {
449            self.line(&format!("{}: Scope({})", field_name, scope_id.0));
450        } else {
451            self.seen_scopes.insert(scope_id);
452            if let Some(scope) = self.env.scopes.iter().find(|s| s.id == scope_id) {
453                let range_start = scope.range.start.0;
454                let range_end = scope.range.end.0;
455                let dependencies = scope.dependencies.clone();
456                let declarations = scope.declarations.clone();
457                let reassignments = scope.reassignments.clone();
458                let early_return_value = scope.early_return_value.clone();
459                let merged = scope.merged.clone();
460                let loc = scope.loc;
461
462                self.line(&format!("{}: Scope {{", field_name));
463                self.indent();
464                self.line(&format!("id: {}", scope_id.0));
465                self.line(&format!("range: [{}:{}]", range_start, range_end));
466
467                // dependencies
468                self.line("dependencies:");
469                self.indent();
470                for (i, dep) in dependencies.iter().enumerate() {
471                    let path_str: String = dep
472                        .path
473                        .iter()
474                        .map(|p| {
475                            let prop = match &p.property {
476                                crate::PropertyLiteral::String(s) => s.clone(),
477                                crate::PropertyLiteral::Number(n) => {
478                                    crate::format_js_number(n.value())
479                                }
480                            };
481                            format!("{}{}", if p.optional { "?." } else { "." }, prop)
482                        })
483                        .collect();
484                    self.line(&format!(
485                        "[{}] {{ identifier: {}, reactive: {}, path: \"{}\" }}",
486                        i, dep.identifier.0, dep.reactive, path_str
487                    ));
488                }
489                self.dedent();
490
491                // declarations
492                self.line("declarations:");
493                self.indent();
494                for (ident_id, decl) in &declarations {
495                    self.line(&format!(
496                        "{}: {{ identifier: {}, scope: {} }}",
497                        ident_id.0, decl.identifier.0, decl.scope.0
498                    ));
499                }
500                self.dedent();
501
502                // reassignments
503                self.line("reassignments:");
504                self.indent();
505                for ident_id in &reassignments {
506                    self.line(&format!("{}", ident_id.0));
507                }
508                self.dedent();
509
510                // earlyReturnValue
511                if let Some(early_return) = &early_return_value {
512                    self.line("earlyReturnValue:");
513                    self.indent();
514                    self.line(&format!("value: {}", early_return.value.0));
515                    self.line(&format!("loc: {}", format_loc(&early_return.loc)));
516                    self.line(&format!("label: bb{}", early_return.label.0));
517                    self.dedent();
518                } else {
519                    self.line("earlyReturnValue: null");
520                }
521
522                // merged
523                let merged_str: Vec<String> = merged.iter().map(|s| s.0.to_string()).collect();
524                self.line(&format!("merged: [{}]", merged_str.join(", ")));
525
526                // loc
527                self.line(&format!("loc: {}", format_loc(&loc)));
528
529                self.dedent();
530                self.line("}");
531            } else {
532                self.line(&format!("{}: Scope({})", field_name, scope_id.0));
533            }
534        }
535    }
536
537    // =========================================================================
538    // Type
539    // =========================================================================
540
541    pub fn format_type(&self, type_id: crate::TypeId) -> String {
542        if let Some(ty) = self.env.types.get(type_id.0 as usize) {
543            self.format_type_value(ty)
544        } else {
545            format!("Type({})", type_id.0)
546        }
547    }
548
549    pub fn format_type_value(&self, ty: &Type) -> String {
550        match ty {
551            Type::Primitive => "Primitive".to_string(),
552            Type::Function {
553                shape_id,
554                return_type,
555                is_constructor,
556            } => {
557                format!(
558                    "Function {{ shapeId: {}, return: {}, isConstructor: {} }}",
559                    match shape_id {
560                        Some(s) => format!("\"{}\"", s),
561                        None => "null".to_string(),
562                    },
563                    self.format_type_value(return_type),
564                    is_constructor
565                )
566            }
567            Type::Object { shape_id } => {
568                format!(
569                    "Object {{ shapeId: {} }}",
570                    match shape_id {
571                        Some(s) => format!("\"{}\"", s),
572                        None => "null".to_string(),
573                    }
574                )
575            }
576            Type::TypeVar { id } => format!("Type({})", id.0),
577            Type::Poly => "Poly".to_string(),
578            Type::Phi { operands } => {
579                let ops: Vec<String> = operands
580                    .iter()
581                    .map(|op| self.format_type_value(op))
582                    .collect();
583                format!("Phi {{ operands: [{}] }}", ops.join(", "))
584            }
585            Type::Property {
586                object_type,
587                object_name,
588                property_name,
589            } => {
590                let prop_str = match property_name {
591                    crate::PropertyNameKind::Literal { value } => {
592                        format!("\"{}\"", format_property_literal(value))
593                    }
594                    crate::PropertyNameKind::Computed { value } => {
595                        format!("computed({})", self.format_type_value(value))
596                    }
597                };
598                format!(
599                    "Property {{ objectType: {}, objectName: \"{}\", propertyName: {} }}",
600                    self.format_type_value(object_type),
601                    object_name,
602                    prop_str
603                )
604            }
605            Type::ObjectMethod => "ObjectMethod".to_string(),
606        }
607    }
608
609    // =========================================================================
610    // LValue
611    // =========================================================================
612
613    pub fn format_lvalue(&mut self, field_name: &str, lv: &LValue) {
614        self.line(&format!("{}:", field_name));
615        self.indent();
616        self.line(&format!("kind: {:?}", lv.kind));
617        self.format_place_field("place", &lv.place);
618        self.dedent();
619    }
620
621    // =========================================================================
622    // Pattern
623    // =========================================================================
624
625    pub fn format_pattern(&mut self, pattern: &Pattern) {
626        match pattern {
627            Pattern::Array(arr) => {
628                self.line("pattern: ArrayPattern {");
629                self.indent();
630                self.line("items:");
631                self.indent();
632                for (i, item) in arr.items.iter().enumerate() {
633                    match item {
634                        crate::ArrayPatternElement::Hole => {
635                            self.line(&format!("[{}] Hole", i));
636                        }
637                        crate::ArrayPatternElement::Place(p) => {
638                            self.format_place_field(&format!("[{}]", i), p);
639                        }
640                        crate::ArrayPatternElement::Spread(s) => {
641                            self.line(&format!("[{}] Spread:", i));
642                            self.indent();
643                            self.format_place_field("place", &s.place);
644                            self.dedent();
645                        }
646                    }
647                }
648                self.dedent();
649                self.line(&format!("loc: {}", format_loc(&arr.loc)));
650                self.dedent();
651                self.line("}");
652            }
653            Pattern::Object(obj) => {
654                self.line("pattern: ObjectPattern {");
655                self.indent();
656                self.line("properties:");
657                self.indent();
658                for (i, prop) in obj.properties.iter().enumerate() {
659                    match prop {
660                        crate::ObjectPropertyOrSpread::Property(p) => {
661                            self.line(&format!("[{}] ObjectProperty {{", i));
662                            self.indent();
663                            self.line(&format!("key: {}", format_object_property_key(&p.key)));
664                            self.line(&format!("type: \"{}\"", p.property_type));
665                            self.format_place_field("place", &p.place);
666                            self.dedent();
667                            self.line("}");
668                        }
669                        crate::ObjectPropertyOrSpread::Spread(s) => {
670                            self.line(&format!("[{}] Spread:", i));
671                            self.indent();
672                            self.format_place_field("place", &s.place);
673                            self.dedent();
674                        }
675                    }
676                }
677                self.dedent();
678                self.line(&format!("loc: {}", format_loc(&obj.loc)));
679                self.dedent();
680                self.line("}");
681            }
682        }
683    }
684
685    // =========================================================================
686    // Arguments
687    // =========================================================================
688
689    pub fn format_argument(&mut self, arg: &crate::PlaceOrSpread, index: usize) {
690        match arg {
691            crate::PlaceOrSpread::Place(p) => {
692                self.format_place_field(&format!("[{}]", index), p);
693            }
694            crate::PlaceOrSpread::Spread(s) => {
695                self.line(&format!("[{}] Spread:", index));
696                self.indent();
697                self.format_place_field("place", &s.place);
698                self.dedent();
699            }
700        }
701    }
702
703    // =========================================================================
704    // InstructionValue
705    // =========================================================================
706
707    /// Format an InstructionValue. The `inner_func_formatter` callback is invoked
708    /// for FunctionExpression/ObjectMethod to format the inner HirFunction. If None,
709    /// a placeholder is printed instead.
710    pub fn format_instruction_value(
711        &mut self,
712        value: &InstructionValue,
713        inner_func_formatter: Option<&dyn Fn(&mut PrintFormatter, &HirFunction)>,
714    ) {
715        match value {
716            InstructionValue::ArrayExpression { elements, loc } => {
717                self.line("ArrayExpression {");
718                self.indent();
719                self.line("elements:");
720                self.indent();
721                for (i, elem) in elements.iter().enumerate() {
722                    match elem {
723                        crate::ArrayElement::Place(p) => {
724                            self.format_place_field(&format!("[{}]", i), p);
725                        }
726                        crate::ArrayElement::Hole => {
727                            self.line(&format!("[{}] Hole", i));
728                        }
729                        crate::ArrayElement::Spread(s) => {
730                            self.line(&format!("[{}] Spread:", i));
731                            self.indent();
732                            self.format_place_field("place", &s.place);
733                            self.dedent();
734                        }
735                    }
736                }
737                self.dedent();
738                self.line(&format!("loc: {}", format_loc(loc)));
739                self.dedent();
740                self.line("}");
741            }
742            InstructionValue::ObjectExpression { properties, loc } => {
743                self.line("ObjectExpression {");
744                self.indent();
745                self.line("properties:");
746                self.indent();
747                for (i, prop) in properties.iter().enumerate() {
748                    match prop {
749                        crate::ObjectPropertyOrSpread::Property(p) => {
750                            self.line(&format!("[{}] ObjectProperty {{", i));
751                            self.indent();
752                            self.line(&format!("key: {}", format_object_property_key(&p.key)));
753                            self.line(&format!("type: \"{}\"", p.property_type));
754                            self.format_place_field("place", &p.place);
755                            self.dedent();
756                            self.line("}");
757                        }
758                        crate::ObjectPropertyOrSpread::Spread(s) => {
759                            self.line(&format!("[{}] Spread:", i));
760                            self.indent();
761                            self.format_place_field("place", &s.place);
762                            self.dedent();
763                        }
764                    }
765                }
766                self.dedent();
767                self.line(&format!("loc: {}", format_loc(loc)));
768                self.dedent();
769                self.line("}");
770            }
771            InstructionValue::UnaryExpression {
772                operator,
773                value: val,
774                loc,
775            } => {
776                self.line("UnaryExpression {");
777                self.indent();
778                self.line(&format!("operator: \"{}\"", operator));
779                self.format_place_field("value", val);
780                self.line(&format!("loc: {}", format_loc(loc)));
781                self.dedent();
782                self.line("}");
783            }
784            InstructionValue::BinaryExpression {
785                operator,
786                left,
787                right,
788                loc,
789            } => {
790                self.line("BinaryExpression {");
791                self.indent();
792                self.line(&format!("operator: \"{}\"", operator));
793                self.format_place_field("left", left);
794                self.format_place_field("right", right);
795                self.line(&format!("loc: {}", format_loc(loc)));
796                self.dedent();
797                self.line("}");
798            }
799            InstructionValue::NewExpression { callee, args, loc } => {
800                self.line("NewExpression {");
801                self.indent();
802                self.format_place_field("callee", callee);
803                self.line("args:");
804                self.indent();
805                for (i, arg) in args.iter().enumerate() {
806                    self.format_argument(arg, i);
807                }
808                self.dedent();
809                self.line(&format!("loc: {}", format_loc(loc)));
810                self.dedent();
811                self.line("}");
812            }
813            InstructionValue::CallExpression { callee, args, loc } => {
814                self.line("CallExpression {");
815                self.indent();
816                self.format_place_field("callee", callee);
817                self.line("args:");
818                self.indent();
819                for (i, arg) in args.iter().enumerate() {
820                    self.format_argument(arg, i);
821                }
822                self.dedent();
823                self.line(&format!("loc: {}", format_loc(loc)));
824                self.dedent();
825                self.line("}");
826            }
827            InstructionValue::MethodCall {
828                receiver,
829                property,
830                args,
831                loc,
832            } => {
833                self.line("MethodCall {");
834                self.indent();
835                self.format_place_field("receiver", receiver);
836                self.format_place_field("property", property);
837                self.line("args:");
838                self.indent();
839                for (i, arg) in args.iter().enumerate() {
840                    self.format_argument(arg, i);
841                }
842                self.dedent();
843                self.line(&format!("loc: {}", format_loc(loc)));
844                self.dedent();
845                self.line("}");
846            }
847            InstructionValue::JSXText { value: val, loc } => {
848                self.line(&format!(
849                    "JSXText {{ value: {}, loc: {} }}",
850                    format_js_string(val),
851                    format_loc(loc)
852                ));
853            }
854            InstructionValue::Primitive { value: prim, loc } => {
855                self.line(&format!(
856                    "Primitive {{ value: {}, loc: {} }}",
857                    format_primitive(prim),
858                    format_loc(loc)
859                ));
860            }
861            InstructionValue::TypeCastExpression {
862                value: val,
863                type_,
864                type_annotation_name,
865                type_annotation_kind,
866                type_annotation: _,
867                loc,
868            } => {
869                self.line("TypeCastExpression {");
870                self.indent();
871                self.format_place_field("value", val);
872                self.line(&format!("type: {}", self.format_type_value(type_)));
873                if let Some(annotation_name) = type_annotation_name {
874                    self.line(&format!("typeAnnotation: {}", annotation_name));
875                }
876                if let Some(annotation_kind) = type_annotation_kind {
877                    self.line(&format!("typeAnnotationKind: \"{}\"", annotation_kind));
878                }
879                self.line(&format!("loc: {}", format_loc(loc)));
880                self.dedent();
881                self.line("}");
882            }
883            InstructionValue::JsxExpression {
884                tag,
885                props,
886                children,
887                loc,
888                opening_loc,
889                closing_loc,
890            } => {
891                self.line("JsxExpression {");
892                self.indent();
893                match tag {
894                    crate::JsxTag::Place(p) => {
895                        self.format_place_field("tag", p);
896                    }
897                    crate::JsxTag::Builtin(b) => {
898                        self.line(&format!("tag: BuiltinTag(\"{}\")", b.name));
899                    }
900                }
901                self.line("props:");
902                self.indent();
903                for (i, prop) in props.iter().enumerate() {
904                    match prop {
905                        crate::JsxAttribute::Attribute { name, place } => {
906                            self.line(&format!("[{}] JsxAttribute {{", i));
907                            self.indent();
908                            self.line(&format!("name: \"{}\"", name));
909                            self.format_place_field("place", place);
910                            self.dedent();
911                            self.line("}");
912                        }
913                        crate::JsxAttribute::SpreadAttribute { argument } => {
914                            self.line(&format!("[{}] JsxSpreadAttribute:", i));
915                            self.indent();
916                            self.format_place_field("argument", argument);
917                            self.dedent();
918                        }
919                    }
920                }
921                self.dedent();
922                match children {
923                    Some(c) => {
924                        self.line("children:");
925                        self.indent();
926                        for (i, child) in c.iter().enumerate() {
927                            self.format_place_field(&format!("[{}]", i), child);
928                        }
929                        self.dedent();
930                    }
931                    None => self.line("children: null"),
932                }
933                self.line(&format!("openingLoc: {}", format_loc(opening_loc)));
934                self.line(&format!("closingLoc: {}", format_loc(closing_loc)));
935                self.line(&format!("loc: {}", format_loc(loc)));
936                self.dedent();
937                self.line("}");
938            }
939            InstructionValue::JsxFragment { children, loc } => {
940                self.line("JsxFragment {");
941                self.indent();
942                self.line("children:");
943                self.indent();
944                for (i, child) in children.iter().enumerate() {
945                    self.format_place_field(&format!("[{}]", i), child);
946                }
947                self.dedent();
948                self.line(&format!("loc: {}", format_loc(loc)));
949                self.dedent();
950                self.line("}");
951            }
952            InstructionValue::UnsupportedNode { node_type, loc, .. } => match node_type {
953                Some(t) => self.line(&format!(
954                    "UnsupportedNode {{ type: {:?}, loc: {} }}",
955                    t,
956                    format_loc(loc)
957                )),
958                None => self.line(&format!("UnsupportedNode {{ loc: {} }}", format_loc(loc))),
959            },
960            InstructionValue::LoadLocal { place, loc } => {
961                self.line("LoadLocal {");
962                self.indent();
963                self.format_place_field("place", place);
964                self.line(&format!("loc: {}", format_loc(loc)));
965                self.dedent();
966                self.line("}");
967            }
968            InstructionValue::DeclareLocal {
969                lvalue,
970                type_annotation,
971                loc,
972            } => {
973                self.line("DeclareLocal {");
974                self.indent();
975                self.format_lvalue("lvalue", lvalue);
976                self.line(&format!(
977                    "type: {}",
978                    match type_annotation {
979                        Some(t) => t.clone(),
980                        None => "null".to_string(),
981                    }
982                ));
983                self.line(&format!("loc: {}", format_loc(loc)));
984                self.dedent();
985                self.line("}");
986            }
987            InstructionValue::DeclareContext { lvalue, loc } => {
988                self.line("DeclareContext {");
989                self.indent();
990                self.line("lvalue:");
991                self.indent();
992                self.line(&format!("kind: {:?}", lvalue.kind));
993                self.format_place_field("place", &lvalue.place);
994                self.dedent();
995                self.line(&format!("loc: {}", format_loc(loc)));
996                self.dedent();
997                self.line("}");
998            }
999            InstructionValue::StoreLocal {
1000                lvalue,
1001                value: val,
1002                type_annotation,
1003                loc,
1004            } => {
1005                self.line("StoreLocal {");
1006                self.indent();
1007                self.format_lvalue("lvalue", lvalue);
1008                self.format_place_field("value", val);
1009                self.line(&format!(
1010                    "type: {}",
1011                    match type_annotation {
1012                        Some(t) => t.clone(),
1013                        None => "null".to_string(),
1014                    }
1015                ));
1016                self.line(&format!("loc: {}", format_loc(loc)));
1017                self.dedent();
1018                self.line("}");
1019            }
1020            InstructionValue::LoadContext { place, loc } => {
1021                self.line("LoadContext {");
1022                self.indent();
1023                self.format_place_field("place", place);
1024                self.line(&format!("loc: {}", format_loc(loc)));
1025                self.dedent();
1026                self.line("}");
1027            }
1028            InstructionValue::StoreContext {
1029                lvalue,
1030                value: val,
1031                loc,
1032            } => {
1033                self.line("StoreContext {");
1034                self.indent();
1035                self.line("lvalue:");
1036                self.indent();
1037                self.line(&format!("kind: {:?}", lvalue.kind));
1038                self.format_place_field("place", &lvalue.place);
1039                self.dedent();
1040                self.format_place_field("value", val);
1041                self.line(&format!("loc: {}", format_loc(loc)));
1042                self.dedent();
1043                self.line("}");
1044            }
1045            InstructionValue::Destructure {
1046                lvalue,
1047                value: val,
1048                loc,
1049            } => {
1050                self.line("Destructure {");
1051                self.indent();
1052                self.line("lvalue:");
1053                self.indent();
1054                self.line(&format!("kind: {:?}", lvalue.kind));
1055                self.format_pattern(&lvalue.pattern);
1056                self.dedent();
1057                self.format_place_field("value", val);
1058                self.line(&format!("loc: {}", format_loc(loc)));
1059                self.dedent();
1060                self.line("}");
1061            }
1062            InstructionValue::PropertyLoad {
1063                object,
1064                property,
1065                loc,
1066            } => {
1067                self.line("PropertyLoad {");
1068                self.indent();
1069                self.format_place_field("object", object);
1070                self.line(&format!(
1071                    "property: \"{}\"",
1072                    format_property_literal(property)
1073                ));
1074                self.line(&format!("loc: {}", format_loc(loc)));
1075                self.dedent();
1076                self.line("}");
1077            }
1078            InstructionValue::PropertyStore {
1079                object,
1080                property,
1081                value: val,
1082                loc,
1083            } => {
1084                self.line("PropertyStore {");
1085                self.indent();
1086                self.format_place_field("object", object);
1087                self.line(&format!(
1088                    "property: \"{}\"",
1089                    format_property_literal(property)
1090                ));
1091                self.format_place_field("value", val);
1092                self.line(&format!("loc: {}", format_loc(loc)));
1093                self.dedent();
1094                self.line("}");
1095            }
1096            InstructionValue::PropertyDelete {
1097                object,
1098                property,
1099                loc,
1100            } => {
1101                self.line("PropertyDelete {");
1102                self.indent();
1103                self.format_place_field("object", object);
1104                self.line(&format!(
1105                    "property: \"{}\"",
1106                    format_property_literal(property)
1107                ));
1108                self.line(&format!("loc: {}", format_loc(loc)));
1109                self.dedent();
1110                self.line("}");
1111            }
1112            InstructionValue::ComputedLoad {
1113                object,
1114                property,
1115                loc,
1116            } => {
1117                self.line("ComputedLoad {");
1118                self.indent();
1119                self.format_place_field("object", object);
1120                self.format_place_field("property", property);
1121                self.line(&format!("loc: {}", format_loc(loc)));
1122                self.dedent();
1123                self.line("}");
1124            }
1125            InstructionValue::ComputedStore {
1126                object,
1127                property,
1128                value: val,
1129                loc,
1130            } => {
1131                self.line("ComputedStore {");
1132                self.indent();
1133                self.format_place_field("object", object);
1134                self.format_place_field("property", property);
1135                self.format_place_field("value", val);
1136                self.line(&format!("loc: {}", format_loc(loc)));
1137                self.dedent();
1138                self.line("}");
1139            }
1140            InstructionValue::ComputedDelete {
1141                object,
1142                property,
1143                loc,
1144            } => {
1145                self.line("ComputedDelete {");
1146                self.indent();
1147                self.format_place_field("object", object);
1148                self.format_place_field("property", property);
1149                self.line(&format!("loc: {}", format_loc(loc)));
1150                self.dedent();
1151                self.line("}");
1152            }
1153            InstructionValue::LoadGlobal { binding, loc } => {
1154                self.line("LoadGlobal {");
1155                self.indent();
1156                self.line(&format!("binding: {}", format_non_local_binding(binding)));
1157                self.line(&format!("loc: {}", format_loc(loc)));
1158                self.dedent();
1159                self.line("}");
1160            }
1161            InstructionValue::StoreGlobal {
1162                name,
1163                value: val,
1164                loc,
1165            } => {
1166                self.line("StoreGlobal {");
1167                self.indent();
1168                self.line(&format!("name: \"{}\"", name));
1169                self.format_place_field("value", val);
1170                self.line(&format!("loc: {}", format_loc(loc)));
1171                self.dedent();
1172                self.line("}");
1173            }
1174            InstructionValue::FunctionExpression {
1175                name,
1176                name_hint,
1177                lowered_func,
1178                expr_type,
1179                loc,
1180            } => {
1181                self.line("FunctionExpression {");
1182                self.indent();
1183                self.line(&format!(
1184                    "name: {}",
1185                    match name {
1186                        Some(n) => format!("\"{}\"", n),
1187                        None => "null".to_string(),
1188                    }
1189                ));
1190                self.line(&format!(
1191                    "nameHint: {}",
1192                    match name_hint {
1193                        Some(h) => format!("\"{}\"", h),
1194                        None => "null".to_string(),
1195                    }
1196                ));
1197                self.line(&format!("type: \"{:?}\"", expr_type));
1198                self.line("loweredFunc:");
1199                let inner_func = &self.env.functions[lowered_func.func.0 as usize];
1200                if let Some(formatter) = inner_func_formatter {
1201                    formatter(self, inner_func);
1202                } else {
1203                    self.line(&format!("  <function {}>", lowered_func.func.0));
1204                }
1205                self.line(&format!("loc: {}", format_loc(loc)));
1206                self.dedent();
1207                self.line("}");
1208            }
1209            InstructionValue::ObjectMethod { loc, lowered_func } => {
1210                self.line("ObjectMethod {");
1211                self.indent();
1212                self.line("loweredFunc:");
1213                let inner_func = &self.env.functions[lowered_func.func.0 as usize];
1214                if let Some(formatter) = inner_func_formatter {
1215                    formatter(self, inner_func);
1216                } else {
1217                    self.line(&format!("  <function {}>", lowered_func.func.0));
1218                }
1219                self.line(&format!("loc: {}", format_loc(loc)));
1220                self.dedent();
1221                self.line("}");
1222            }
1223            InstructionValue::TaggedTemplateExpression {
1224                tag,
1225                value: val,
1226                loc,
1227            } => {
1228                self.line("TaggedTemplateExpression {");
1229                self.indent();
1230                self.format_place_field("tag", tag);
1231                self.line(&format!("raw: {}", format_js_string(&val.raw)));
1232                self.line(&format!(
1233                    "cooked: {}",
1234                    match &val.cooked {
1235                        Some(c) => format_js_string(c),
1236                        None => "undefined".to_string(),
1237                    }
1238                ));
1239                self.line(&format!("loc: {}", format_loc(loc)));
1240                self.dedent();
1241                self.line("}");
1242            }
1243            InstructionValue::TemplateLiteral {
1244                subexprs,
1245                quasis,
1246                loc,
1247            } => {
1248                self.line("TemplateLiteral {");
1249                self.indent();
1250                self.line("subexprs:");
1251                self.indent();
1252                for (i, sub) in subexprs.iter().enumerate() {
1253                    self.format_place_field(&format!("[{}]", i), sub);
1254                }
1255                self.dedent();
1256                self.line("quasis:");
1257                self.indent();
1258                for (i, q) in quasis.iter().enumerate() {
1259                    self.line(&format!(
1260                        "[{}] {{ raw: {}, cooked: {} }}",
1261                        i,
1262                        format_js_string(&q.raw),
1263                        match &q.cooked {
1264                            Some(c) => format_js_string(c),
1265                            None => "undefined".to_string(),
1266                        }
1267                    ));
1268                }
1269                self.dedent();
1270                self.line(&format!("loc: {}", format_loc(loc)));
1271                self.dedent();
1272                self.line("}");
1273            }
1274            InstructionValue::RegExpLiteral {
1275                pattern,
1276                flags,
1277                loc,
1278            } => {
1279                self.line(&format!(
1280                    "RegExpLiteral {{ pattern: \"{}\", flags: \"{}\", loc: {} }}",
1281                    pattern,
1282                    flags,
1283                    format_loc(loc)
1284                ));
1285            }
1286            InstructionValue::MetaProperty {
1287                meta,
1288                property,
1289                loc,
1290            } => {
1291                self.line(&format!(
1292                    "MetaProperty {{ meta: \"{}\", property: \"{}\", loc: {} }}",
1293                    meta,
1294                    property,
1295                    format_loc(loc)
1296                ));
1297            }
1298            InstructionValue::Await { value: val, loc } => {
1299                self.line("Await {");
1300                self.indent();
1301                self.format_place_field("value", val);
1302                self.line(&format!("loc: {}", format_loc(loc)));
1303                self.dedent();
1304                self.line("}");
1305            }
1306            InstructionValue::GetIterator { collection, loc } => {
1307                self.line("GetIterator {");
1308                self.indent();
1309                self.format_place_field("collection", collection);
1310                self.line(&format!("loc: {}", format_loc(loc)));
1311                self.dedent();
1312                self.line("}");
1313            }
1314            InstructionValue::IteratorNext {
1315                iterator,
1316                collection,
1317                loc,
1318            } => {
1319                self.line("IteratorNext {");
1320                self.indent();
1321                self.format_place_field("iterator", iterator);
1322                self.format_place_field("collection", collection);
1323                self.line(&format!("loc: {}", format_loc(loc)));
1324                self.dedent();
1325                self.line("}");
1326            }
1327            InstructionValue::NextPropertyOf { value: val, loc } => {
1328                self.line("NextPropertyOf {");
1329                self.indent();
1330                self.format_place_field("value", val);
1331                self.line(&format!("loc: {}", format_loc(loc)));
1332                self.dedent();
1333                self.line("}");
1334            }
1335            InstructionValue::Debugger { loc } => {
1336                self.line(&format!("Debugger {{ loc: {} }}", format_loc(loc)));
1337            }
1338            InstructionValue::PostfixUpdate {
1339                lvalue,
1340                operation,
1341                value: val,
1342                loc,
1343            } => {
1344                self.line("PostfixUpdate {");
1345                self.indent();
1346                self.format_place_field("lvalue", lvalue);
1347                self.line(&format!("operation: \"{}\"", operation));
1348                self.format_place_field("value", val);
1349                self.line(&format!("loc: {}", format_loc(loc)));
1350                self.dedent();
1351                self.line("}");
1352            }
1353            InstructionValue::PrefixUpdate {
1354                lvalue,
1355                operation,
1356                value: val,
1357                loc,
1358            } => {
1359                self.line("PrefixUpdate {");
1360                self.indent();
1361                self.format_place_field("lvalue", lvalue);
1362                self.line(&format!("operation: \"{}\"", operation));
1363                self.format_place_field("value", val);
1364                self.line(&format!("loc: {}", format_loc(loc)));
1365                self.dedent();
1366                self.line("}");
1367            }
1368            InstructionValue::StartMemoize {
1369                manual_memo_id,
1370                deps,
1371                deps_loc: _,
1372                has_invalid_deps: _,
1373                loc,
1374            } => {
1375                self.line("StartMemoize {");
1376                self.indent();
1377                self.line(&format!("manualMemoId: {}", manual_memo_id));
1378                match deps {
1379                    Some(d) => {
1380                        self.line("deps:");
1381                        self.indent();
1382                        for (i, dep) in d.iter().enumerate() {
1383                            let root_str = match &dep.root {
1384                                crate::ManualMemoDependencyRoot::Global { identifier_name } => {
1385                                    format!("Global(\"{}\")", identifier_name)
1386                                }
1387                                crate::ManualMemoDependencyRoot::NamedLocal {
1388                                    value: val,
1389                                    constant,
1390                                } => {
1391                                    format!(
1392                                        "NamedLocal({}, constant={})",
1393                                        val.identifier.0, constant
1394                                    )
1395                                }
1396                            };
1397                            let path_str: String = dep
1398                                .path
1399                                .iter()
1400                                .map(|p| {
1401                                    format!(
1402                                        "{}.{}",
1403                                        if p.optional { "?" } else { "" },
1404                                        format_property_literal(&p.property)
1405                                    )
1406                                })
1407                                .collect();
1408                            self.line(&format!("[{}] {}{}", i, root_str, path_str));
1409                        }
1410                        self.dedent();
1411                    }
1412                    None => self.line("deps: null"),
1413                }
1414                self.line(&format!("loc: {}", format_loc(loc)));
1415                self.dedent();
1416                self.line("}");
1417            }
1418            InstructionValue::FinishMemoize {
1419                manual_memo_id,
1420                decl,
1421                pruned,
1422                loc,
1423            } => {
1424                self.line("FinishMemoize {");
1425                self.indent();
1426                self.line(&format!("manualMemoId: {}", manual_memo_id));
1427                self.format_place_field("decl", decl);
1428                self.line(&format!("pruned: {}", pruned));
1429                self.line(&format!("loc: {}", format_loc(loc)));
1430                self.dedent();
1431                self.line("}");
1432            }
1433        }
1434    }
1435
1436    // =========================================================================
1437    // Errors
1438    // =========================================================================
1439
1440    pub fn format_errors(&mut self, error: &CompilerError) {
1441        if error.details.is_empty() {
1442            self.line("Errors: []");
1443            return;
1444        }
1445        self.line("Errors:");
1446        self.indent();
1447        for (i, detail) in error.details.iter().enumerate() {
1448            self.line(&format!("[{}] {{", i));
1449            self.indent();
1450            match detail {
1451                CompilerErrorOrDiagnostic::Diagnostic(d) => {
1452                    self.line(&format!("severity: {:?}", d.severity()));
1453                    self.line(&format!("reason: {:?}", d.reason));
1454                    self.line(&format!(
1455                        "description: {}",
1456                        match &d.description {
1457                            Some(desc) => format!("{:?}", desc),
1458                            None => "null".to_string(),
1459                        }
1460                    ));
1461                    self.line(&format!("category: {:?}", d.category));
1462                    let loc = d.primary_location();
1463                    self.line(&format!(
1464                        "loc: {}",
1465                        match loc {
1466                            Some(l) => format_loc_value(l),
1467                            None => "null".to_string(),
1468                        }
1469                    ));
1470                }
1471                CompilerErrorOrDiagnostic::ErrorDetail(d) => {
1472                    self.line(&format!("severity: {:?}", d.severity()));
1473                    self.line(&format!("reason: {:?}", d.reason));
1474                    self.line(&format!(
1475                        "description: {}",
1476                        match &d.description {
1477                            Some(desc) => format!("{:?}", desc),
1478                            None => "null".to_string(),
1479                        }
1480                    ));
1481                    self.line(&format!("category: {:?}", d.category));
1482                    self.line(&format!(
1483                        "loc: {}",
1484                        match &d.loc {
1485                            Some(l) => format_loc_value(l),
1486                            None => "null".to_string(),
1487                        }
1488                    ));
1489                }
1490            }
1491            self.dedent();
1492            self.line("}");
1493        }
1494        self.dedent();
1495    }
1496}