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