Skip to main content

react_compiler_hir/
lib.rs

1pub mod default_module_type_provider;
2pub mod dominator;
3pub mod environment;
4pub mod environment_config;
5pub mod globals;
6pub mod object_shape;
7pub mod print;
8pub mod reactive;
9pub mod type_config;
10pub mod visitors;
11
12use indexmap::IndexMap;
13use indexmap::IndexSet;
14pub use react_compiler_diagnostics::CompilerDiagnostic;
15pub use react_compiler_diagnostics::ErrorCategory;
16pub use react_compiler_diagnostics::GENERATED_SOURCE;
17pub use react_compiler_diagnostics::Position;
18pub use react_compiler_diagnostics::SourceLocation;
19pub use reactive::*;
20
21// =============================================================================
22// ID newtypes
23// =============================================================================
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
26pub struct BlockId(pub u32);
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
29pub struct IdentifierId(pub u32);
30
31/// Index into the flat instruction table on HirFunction.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
33pub struct InstructionId(pub u32);
34
35/// Evaluation order assigned to instructions and terminals during numbering.
36/// This was previously called InstructionId in the TypeScript compiler.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
38pub struct EvaluationOrder(pub u32);
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
41pub struct DeclarationId(pub u32);
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
44pub struct ScopeId(pub u32);
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
47pub struct TypeId(pub u32);
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
50pub struct FunctionId(pub u32);
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53pub struct MutableRangeId(pub u32);
54
55// =============================================================================
56// FloatValue wrapper
57// =============================================================================
58
59/// Wrapper around f64 that stores raw bytes for deterministic equality and hashing.
60/// This allows use in HashMap keys and ensures NaN == NaN (bitwise comparison).
61#[derive(Debug, Clone, Copy)]
62pub struct FloatValue(u64);
63
64impl FloatValue {
65    pub fn new(value: f64) -> Self {
66        FloatValue(value.to_bits())
67    }
68
69    pub fn value(self) -> f64 {
70        f64::from_bits(self.0)
71    }
72}
73
74impl From<f64> for FloatValue {
75    fn from(value: f64) -> Self {
76        FloatValue::new(value)
77    }
78}
79
80impl From<FloatValue> for f64 {
81    fn from(value: FloatValue) -> Self {
82        value.value()
83    }
84}
85
86impl PartialEq for FloatValue {
87    fn eq(&self, other: &Self) -> bool {
88        self.0 == other.0
89    }
90}
91
92impl Eq for FloatValue {}
93
94impl std::hash::Hash for FloatValue {
95    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
96        self.0.hash(state);
97    }
98}
99
100impl std::fmt::Display for FloatValue {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        write!(f, "{}", format_js_number(self.value()))
103    }
104}
105
106/// Format an f64 the way JavaScript's `Number.prototype.toString()` does.
107///
108/// Key differences from Rust's default `Display`:
109/// - Uses scientific notation for |x| >= 1e21 (e.g. `1e+21`, `2.18739127891275e+22`)
110/// - Uses scientific notation for 0 < |x| < 1e-6 (e.g. `1e-7`, `1.5e-8`)
111/// - Uses minimal significant digits that round-trip to the same f64
112/// - Formats -0 as "0"
113pub fn format_js_number(n: f64) -> String {
114    if n.is_nan() {
115        return "NaN".to_string();
116    }
117    if n.is_infinite() {
118        return if n > 0.0 {
119            "Infinity".to_string()
120        } else {
121            "-Infinity".to_string()
122        };
123    }
124    if n == 0.0 {
125        return "0".to_string();
126    }
127
128    let abs = n.abs();
129    let sign = if n < 0.0 { "-" } else { "" };
130
131    if abs >= 1e21 || (abs > 0.0 && abs < 1e-6) {
132        // Use scientific notation matching JS format: coefficient + "e+" or "e-" + exponent
133        // Rust's {:e} uses "e" (lowercase) like JS, but formats as e.g. "1.5e21" not "1.5e+21"
134        let formatted = format!("{:e}", abs);
135        // Split into coefficient and exponent parts
136        let (coeff, exp_str) = formatted.split_once('e').unwrap();
137        let exp: i32 = exp_str.parse().unwrap();
138        // JS uses e+N for positive exponents, e-N for negative
139        if exp >= 0 {
140            format!("{}{}e+{}", sign, coeff, exp)
141        } else {
142            format!("{}{}e-{}", sign, coeff, exp.unsigned_abs())
143        }
144    } else if abs.fract() == 0.0 && abs < (i64::MAX as f64) {
145        // Integer that fits in i64 — format without decimal point
146        format!("{}{}", sign, abs as i64)
147    } else {
148        // Regular float: Rust's default Display gives us the right digits
149        format!("{}", n)
150    }
151}
152
153// =============================================================================
154// Core HIR types
155// =============================================================================
156
157/// A function lowered to HIR form
158#[derive(Debug, Clone)]
159pub struct HirFunction {
160    pub loc: Option<SourceLocation>,
161    pub id: Option<String>,
162    pub name_hint: Option<String>,
163    pub fn_type: ReactFunctionType,
164    pub params: Vec<ParamPattern>,
165    pub return_type_annotation: Option<String>,
166    pub returns: Place,
167    pub context: Vec<Place>,
168    pub body: HIR,
169    pub instructions: Vec<Instruction>,
170    pub generator: bool,
171    pub is_async: bool,
172    pub directives: Vec<String>,
173    pub aliasing_effects: Option<Vec<AliasingEffect>>,
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq)]
177pub enum ReactFunctionType {
178    Component,
179    Hook,
180    Other,
181}
182
183#[derive(Debug, Clone)]
184pub enum ParamPattern {
185    Place(Place),
186    Spread(SpreadPattern),
187}
188
189/// The HIR control-flow graph
190#[derive(Debug, Clone)]
191pub struct HIR {
192    pub entry: BlockId,
193    pub blocks: IndexMap<BlockId, BasicBlock>,
194}
195
196/// Block kinds
197#[derive(Debug, Clone, Copy, PartialEq, Eq)]
198pub enum BlockKind {
199    Block,
200    Value,
201    Loop,
202    Sequence,
203    Catch,
204}
205
206impl std::fmt::Display for BlockKind {
207    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208        match self {
209            BlockKind::Block => write!(f, "block"),
210            BlockKind::Value => write!(f, "value"),
211            BlockKind::Loop => write!(f, "loop"),
212            BlockKind::Sequence => write!(f, "sequence"),
213            BlockKind::Catch => write!(f, "catch"),
214        }
215    }
216}
217
218/// A basic block in the CFG
219#[derive(Debug, Clone)]
220pub struct BasicBlock {
221    pub kind: BlockKind,
222    pub id: BlockId,
223    pub instructions: Vec<InstructionId>,
224    pub terminal: Terminal,
225    pub preds: IndexSet<BlockId>,
226    pub phis: Vec<Phi>,
227}
228
229/// Phi node for SSA
230#[derive(Debug, Clone)]
231pub struct Phi {
232    pub place: Place,
233    pub operands: IndexMap<BlockId, Place>,
234}
235
236// =============================================================================
237// Terminal enum
238// =============================================================================
239
240#[derive(Debug, Clone)]
241pub enum Terminal {
242    Unsupported {
243        id: EvaluationOrder,
244        loc: Option<SourceLocation>,
245    },
246    Unreachable {
247        id: EvaluationOrder,
248        loc: Option<SourceLocation>,
249    },
250    Throw {
251        value: Place,
252        id: EvaluationOrder,
253        loc: Option<SourceLocation>,
254    },
255    Return {
256        value: Place,
257        return_variant: ReturnVariant,
258        id: EvaluationOrder,
259        loc: Option<SourceLocation>,
260        effects: Option<Vec<AliasingEffect>>,
261    },
262    Goto {
263        block: BlockId,
264        variant: GotoVariant,
265        id: EvaluationOrder,
266        loc: Option<SourceLocation>,
267    },
268    If {
269        test: Place,
270        consequent: BlockId,
271        alternate: BlockId,
272        fallthrough: BlockId,
273        id: EvaluationOrder,
274        loc: Option<SourceLocation>,
275    },
276    Branch {
277        test: Place,
278        consequent: BlockId,
279        alternate: BlockId,
280        fallthrough: BlockId,
281        id: EvaluationOrder,
282        loc: Option<SourceLocation>,
283    },
284    Switch {
285        test: Place,
286        cases: Vec<Case>,
287        fallthrough: BlockId,
288        id: EvaluationOrder,
289        loc: Option<SourceLocation>,
290    },
291    DoWhile {
292        loop_block: BlockId,
293        test: BlockId,
294        fallthrough: BlockId,
295        id: EvaluationOrder,
296        loc: Option<SourceLocation>,
297    },
298    While {
299        test: BlockId,
300        loop_block: BlockId,
301        fallthrough: BlockId,
302        id: EvaluationOrder,
303        loc: Option<SourceLocation>,
304    },
305    For {
306        init: BlockId,
307        test: BlockId,
308        update: Option<BlockId>,
309        loop_block: BlockId,
310        fallthrough: BlockId,
311        id: EvaluationOrder,
312        loc: Option<SourceLocation>,
313    },
314    ForOf {
315        init: BlockId,
316        test: BlockId,
317        loop_block: BlockId,
318        fallthrough: BlockId,
319        id: EvaluationOrder,
320        loc: Option<SourceLocation>,
321    },
322    ForIn {
323        init: BlockId,
324        loop_block: BlockId,
325        fallthrough: BlockId,
326        id: EvaluationOrder,
327        loc: Option<SourceLocation>,
328    },
329    Logical {
330        operator: LogicalOperator,
331        test: BlockId,
332        fallthrough: BlockId,
333        id: EvaluationOrder,
334        loc: Option<SourceLocation>,
335    },
336    Ternary {
337        test: BlockId,
338        fallthrough: BlockId,
339        id: EvaluationOrder,
340        loc: Option<SourceLocation>,
341    },
342    Optional {
343        optional: bool,
344        test: BlockId,
345        fallthrough: BlockId,
346        id: EvaluationOrder,
347        loc: Option<SourceLocation>,
348    },
349    Label {
350        block: BlockId,
351        fallthrough: BlockId,
352        id: EvaluationOrder,
353        loc: Option<SourceLocation>,
354    },
355    Sequence {
356        block: BlockId,
357        fallthrough: BlockId,
358        id: EvaluationOrder,
359        loc: Option<SourceLocation>,
360    },
361    MaybeThrow {
362        continuation: BlockId,
363        handler: Option<BlockId>,
364        id: EvaluationOrder,
365        loc: Option<SourceLocation>,
366        effects: Option<Vec<AliasingEffect>>,
367    },
368    Try {
369        block: BlockId,
370        handler_binding: Option<Place>,
371        handler: BlockId,
372        fallthrough: BlockId,
373        id: EvaluationOrder,
374        loc: Option<SourceLocation>,
375    },
376    Scope {
377        fallthrough: BlockId,
378        block: BlockId,
379        scope: ScopeId,
380        id: EvaluationOrder,
381        loc: Option<SourceLocation>,
382    },
383    PrunedScope {
384        fallthrough: BlockId,
385        block: BlockId,
386        scope: ScopeId,
387        id: EvaluationOrder,
388        loc: Option<SourceLocation>,
389    },
390}
391
392impl Terminal {
393    /// Get the evaluation order of this terminal
394    pub fn evaluation_order(&self) -> EvaluationOrder {
395        match self {
396            Terminal::Unsupported { id, .. }
397            | Terminal::Unreachable { id, .. }
398            | Terminal::Throw { id, .. }
399            | Terminal::Return { id, .. }
400            | Terminal::Goto { id, .. }
401            | Terminal::If { id, .. }
402            | Terminal::Branch { id, .. }
403            | Terminal::Switch { id, .. }
404            | Terminal::DoWhile { id, .. }
405            | Terminal::While { id, .. }
406            | Terminal::For { id, .. }
407            | Terminal::ForOf { id, .. }
408            | Terminal::ForIn { id, .. }
409            | Terminal::Logical { id, .. }
410            | Terminal::Ternary { id, .. }
411            | Terminal::Optional { id, .. }
412            | Terminal::Label { id, .. }
413            | Terminal::Sequence { id, .. }
414            | Terminal::MaybeThrow { id, .. }
415            | Terminal::Try { id, .. }
416            | Terminal::Scope { id, .. }
417            | Terminal::PrunedScope { id, .. } => *id,
418        }
419    }
420
421    /// Get the source location of this terminal
422    pub fn loc(&self) -> Option<&SourceLocation> {
423        match self {
424            Terminal::Unsupported { loc, .. }
425            | Terminal::Unreachable { loc, .. }
426            | Terminal::Throw { loc, .. }
427            | Terminal::Return { loc, .. }
428            | Terminal::Goto { loc, .. }
429            | Terminal::If { loc, .. }
430            | Terminal::Branch { loc, .. }
431            | Terminal::Switch { loc, .. }
432            | Terminal::DoWhile { loc, .. }
433            | Terminal::While { loc, .. }
434            | Terminal::For { loc, .. }
435            | Terminal::ForOf { loc, .. }
436            | Terminal::ForIn { loc, .. }
437            | Terminal::Logical { loc, .. }
438            | Terminal::Ternary { loc, .. }
439            | Terminal::Optional { loc, .. }
440            | Terminal::Label { loc, .. }
441            | Terminal::Sequence { loc, .. }
442            | Terminal::MaybeThrow { loc, .. }
443            | Terminal::Try { loc, .. }
444            | Terminal::Scope { loc, .. }
445            | Terminal::PrunedScope { loc, .. } => loc.as_ref(),
446        }
447    }
448
449    /// Set the evaluation order of this terminal
450    pub fn set_evaluation_order(&mut self, new_id: EvaluationOrder) {
451        match self {
452            Terminal::Unsupported { id, .. }
453            | Terminal::Unreachable { id, .. }
454            | Terminal::Throw { id, .. }
455            | Terminal::Return { id, .. }
456            | Terminal::Goto { id, .. }
457            | Terminal::If { id, .. }
458            | Terminal::Branch { id, .. }
459            | Terminal::Switch { id, .. }
460            | Terminal::DoWhile { id, .. }
461            | Terminal::While { id, .. }
462            | Terminal::For { id, .. }
463            | Terminal::ForOf { id, .. }
464            | Terminal::ForIn { id, .. }
465            | Terminal::Logical { id, .. }
466            | Terminal::Ternary { id, .. }
467            | Terminal::Optional { id, .. }
468            | Terminal::Label { id, .. }
469            | Terminal::Sequence { id, .. }
470            | Terminal::MaybeThrow { id, .. }
471            | Terminal::Try { id, .. }
472            | Terminal::Scope { id, .. }
473            | Terminal::PrunedScope { id, .. } => *id = new_id,
474        }
475    }
476}
477
478#[derive(Debug, Clone, Copy, PartialEq, Eq)]
479pub enum ReturnVariant {
480    Void,
481    Implicit,
482    Explicit,
483}
484
485#[derive(Debug, Clone, Copy, PartialEq, Eq)]
486pub enum GotoVariant {
487    Break,
488    Continue,
489    Try,
490}
491
492#[derive(Debug, Clone)]
493pub struct Case {
494    pub test: Option<Place>,
495    pub block: BlockId,
496}
497
498#[derive(Debug, Clone, Copy, PartialEq, Eq)]
499pub enum LogicalOperator {
500    And,
501    Or,
502    NullishCoalescing,
503}
504
505impl std::fmt::Display for LogicalOperator {
506    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
507        match self {
508            LogicalOperator::And => write!(f, "&&"),
509            LogicalOperator::Or => write!(f, "||"),
510            LogicalOperator::NullishCoalescing => write!(f, "??"),
511        }
512    }
513}
514
515// =============================================================================
516// Instruction types
517// =============================================================================
518
519#[derive(Debug, Clone)]
520pub struct Instruction {
521    pub id: EvaluationOrder,
522    pub lvalue: Place,
523    pub value: InstructionValue,
524    pub loc: Option<SourceLocation>,
525    pub effects: Option<Vec<AliasingEffect>>,
526}
527
528#[derive(Debug, Clone, Copy, PartialEq, Eq)]
529pub enum InstructionKind {
530    Const,
531    Let,
532    Reassign,
533    Catch,
534    HoistedConst,
535    HoistedLet,
536    HoistedFunction,
537    Function,
538}
539
540#[derive(Debug, Clone)]
541pub struct LValue {
542    pub place: Place,
543    pub kind: InstructionKind,
544}
545
546#[derive(Debug, Clone)]
547pub struct LValuePattern {
548    pub pattern: Pattern,
549    pub kind: InstructionKind,
550}
551
552#[derive(Debug, Clone)]
553pub enum Pattern {
554    Array(ArrayPattern),
555    Object(ObjectPattern),
556}
557
558// =============================================================================
559// InstructionValue enum
560// =============================================================================
561
562#[derive(Debug, Clone)]
563pub enum InstructionValue {
564    LoadLocal {
565        place: Place,
566        loc: Option<SourceLocation>,
567    },
568    LoadContext {
569        place: Place,
570        loc: Option<SourceLocation>,
571    },
572    DeclareLocal {
573        lvalue: LValue,
574        type_annotation: Option<String>,
575        loc: Option<SourceLocation>,
576    },
577    DeclareContext {
578        lvalue: LValue,
579        loc: Option<SourceLocation>,
580    },
581    StoreLocal {
582        lvalue: LValue,
583        value: Place,
584        type_annotation: Option<String>,
585        loc: Option<SourceLocation>,
586    },
587    StoreContext {
588        lvalue: LValue,
589        value: Place,
590        loc: Option<SourceLocation>,
591    },
592    Destructure {
593        lvalue: LValuePattern,
594        value: Place,
595        loc: Option<SourceLocation>,
596    },
597    Primitive {
598        value: PrimitiveValue,
599        loc: Option<SourceLocation>,
600    },
601    JSXText {
602        value: String,
603        loc: Option<SourceLocation>,
604    },
605    BinaryExpression {
606        operator: BinaryOperator,
607        left: Place,
608        right: Place,
609        loc: Option<SourceLocation>,
610    },
611    NewExpression {
612        callee: Place,
613        args: Vec<PlaceOrSpread>,
614        loc: Option<SourceLocation>,
615    },
616    CallExpression {
617        callee: Place,
618        args: Vec<PlaceOrSpread>,
619        loc: Option<SourceLocation>,
620    },
621    MethodCall {
622        receiver: Place,
623        property: Place,
624        args: Vec<PlaceOrSpread>,
625        loc: Option<SourceLocation>,
626    },
627    UnaryExpression {
628        operator: UnaryOperator,
629        value: Place,
630        loc: Option<SourceLocation>,
631    },
632    TypeCastExpression {
633        value: Place,
634        type_: Type,
635        type_annotation_name: Option<String>,
636        type_annotation_kind: Option<String>,
637        /// The original AST type annotation node, preserved for codegen.
638        /// For Flow: the inner type from TypeAnnotation.typeAnnotation
639        /// For TS: the TSType node from TSAsExpression/TSSatisfiesExpression
640        type_annotation: Option<Box<serde_json::Value>>,
641        loc: Option<SourceLocation>,
642    },
643    JsxExpression {
644        tag: JsxTag,
645        props: Vec<JsxAttribute>,
646        children: Option<Vec<Place>>,
647        loc: Option<SourceLocation>,
648        opening_loc: Option<SourceLocation>,
649        closing_loc: Option<SourceLocation>,
650    },
651    ObjectExpression {
652        properties: Vec<ObjectPropertyOrSpread>,
653        loc: Option<SourceLocation>,
654    },
655    ObjectMethod {
656        loc: Option<SourceLocation>,
657        lowered_func: LoweredFunction,
658    },
659    ArrayExpression {
660        elements: Vec<ArrayElement>,
661        loc: Option<SourceLocation>,
662    },
663    JsxFragment {
664        children: Vec<Place>,
665        loc: Option<SourceLocation>,
666    },
667    RegExpLiteral {
668        pattern: String,
669        flags: String,
670        loc: Option<SourceLocation>,
671    },
672    MetaProperty {
673        meta: String,
674        property: String,
675        loc: Option<SourceLocation>,
676    },
677    PropertyStore {
678        object: Place,
679        property: PropertyLiteral,
680        value: Place,
681        loc: Option<SourceLocation>,
682    },
683    PropertyLoad {
684        object: Place,
685        property: PropertyLiteral,
686        loc: Option<SourceLocation>,
687    },
688    PropertyDelete {
689        object: Place,
690        property: PropertyLiteral,
691        loc: Option<SourceLocation>,
692    },
693    ComputedStore {
694        object: Place,
695        property: Place,
696        value: Place,
697        loc: Option<SourceLocation>,
698    },
699    ComputedLoad {
700        object: Place,
701        property: Place,
702        loc: Option<SourceLocation>,
703    },
704    ComputedDelete {
705        object: Place,
706        property: Place,
707        loc: Option<SourceLocation>,
708    },
709    LoadGlobal {
710        binding: NonLocalBinding,
711        loc: Option<SourceLocation>,
712    },
713    StoreGlobal {
714        name: String,
715        value: Place,
716        loc: Option<SourceLocation>,
717    },
718    FunctionExpression {
719        name: Option<String>,
720        name_hint: Option<String>,
721        lowered_func: LoweredFunction,
722        expr_type: FunctionExpressionType,
723        loc: Option<SourceLocation>,
724    },
725    TaggedTemplateExpression {
726        tag: Place,
727        value: TemplateQuasi,
728        loc: Option<SourceLocation>,
729    },
730    TemplateLiteral {
731        subexprs: Vec<Place>,
732        quasis: Vec<TemplateQuasi>,
733        loc: Option<SourceLocation>,
734    },
735    Await {
736        value: Place,
737        loc: Option<SourceLocation>,
738    },
739    GetIterator {
740        collection: Place,
741        loc: Option<SourceLocation>,
742    },
743    IteratorNext {
744        iterator: Place,
745        collection: Place,
746        loc: Option<SourceLocation>,
747    },
748    NextPropertyOf {
749        value: Place,
750        loc: Option<SourceLocation>,
751    },
752    PrefixUpdate {
753        lvalue: Place,
754        operation: UpdateOperator,
755        value: Place,
756        loc: Option<SourceLocation>,
757    },
758    PostfixUpdate {
759        lvalue: Place,
760        operation: UpdateOperator,
761        value: Place,
762        loc: Option<SourceLocation>,
763    },
764    Debugger {
765        loc: Option<SourceLocation>,
766    },
767    StartMemoize {
768        manual_memo_id: u32,
769        deps: Option<Vec<ManualMemoDependency>>,
770        deps_loc: Option<Option<SourceLocation>>,
771        has_invalid_deps: bool,
772        loc: Option<SourceLocation>,
773    },
774    FinishMemoize {
775        manual_memo_id: u32,
776        decl: Place,
777        pruned: bool,
778        loc: Option<SourceLocation>,
779    },
780    UnsupportedNode {
781        node_type: Option<String>,
782        /// The original AST node serialized as JSON, so codegen can emit it verbatim.
783        original_node: Option<serde_json::Value>,
784        loc: Option<SourceLocation>,
785    },
786}
787
788impl InstructionValue {
789    pub fn loc(&self) -> Option<&SourceLocation> {
790        match self {
791            InstructionValue::LoadLocal { loc, .. }
792            | InstructionValue::LoadContext { loc, .. }
793            | InstructionValue::DeclareLocal { loc, .. }
794            | InstructionValue::DeclareContext { loc, .. }
795            | InstructionValue::StoreLocal { loc, .. }
796            | InstructionValue::StoreContext { loc, .. }
797            | InstructionValue::Destructure { loc, .. }
798            | InstructionValue::Primitive { loc, .. }
799            | InstructionValue::JSXText { loc, .. }
800            | InstructionValue::BinaryExpression { loc, .. }
801            | InstructionValue::NewExpression { loc, .. }
802            | InstructionValue::CallExpression { loc, .. }
803            | InstructionValue::MethodCall { loc, .. }
804            | InstructionValue::UnaryExpression { loc, .. }
805            | InstructionValue::TypeCastExpression { loc, .. }
806            | InstructionValue::JsxExpression { loc, .. }
807            | InstructionValue::ObjectExpression { loc, .. }
808            | InstructionValue::ObjectMethod { loc, .. }
809            | InstructionValue::ArrayExpression { loc, .. }
810            | InstructionValue::JsxFragment { loc, .. }
811            | InstructionValue::RegExpLiteral { loc, .. }
812            | InstructionValue::MetaProperty { loc, .. }
813            | InstructionValue::PropertyStore { loc, .. }
814            | InstructionValue::PropertyLoad { loc, .. }
815            | InstructionValue::PropertyDelete { loc, .. }
816            | InstructionValue::ComputedStore { loc, .. }
817            | InstructionValue::ComputedLoad { loc, .. }
818            | InstructionValue::ComputedDelete { loc, .. }
819            | InstructionValue::LoadGlobal { loc, .. }
820            | InstructionValue::StoreGlobal { loc, .. }
821            | InstructionValue::FunctionExpression { loc, .. }
822            | InstructionValue::TaggedTemplateExpression { loc, .. }
823            | InstructionValue::TemplateLiteral { loc, .. }
824            | InstructionValue::Await { loc, .. }
825            | InstructionValue::GetIterator { loc, .. }
826            | InstructionValue::IteratorNext { loc, .. }
827            | InstructionValue::NextPropertyOf { loc, .. }
828            | InstructionValue::PrefixUpdate { loc, .. }
829            | InstructionValue::PostfixUpdate { loc, .. }
830            | InstructionValue::Debugger { loc, .. }
831            | InstructionValue::StartMemoize { loc, .. }
832            | InstructionValue::FinishMemoize { loc, .. }
833            | InstructionValue::UnsupportedNode { loc, .. } => loc.as_ref(),
834        }
835    }
836}
837
838// =============================================================================
839// Supporting types
840// =============================================================================
841
842#[derive(Debug, Clone, PartialEq, Eq, Hash)]
843pub enum PrimitiveValue {
844    Null,
845    Undefined,
846    Boolean(bool),
847    Number(FloatValue),
848    String(String),
849}
850
851#[derive(Debug, Clone, Copy, PartialEq, Eq)]
852pub enum BinaryOperator {
853    Equal,
854    NotEqual,
855    StrictEqual,
856    StrictNotEqual,
857    LessThan,
858    LessEqual,
859    GreaterThan,
860    GreaterEqual,
861    ShiftLeft,
862    ShiftRight,
863    UnsignedShiftRight,
864    Add,
865    Subtract,
866    Multiply,
867    Divide,
868    Modulo,
869    Exponent,
870    BitwiseOr,
871    BitwiseXor,
872    BitwiseAnd,
873    In,
874    InstanceOf,
875}
876
877impl std::fmt::Display for BinaryOperator {
878    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
879        match self {
880            BinaryOperator::Equal => write!(f, "=="),
881            BinaryOperator::NotEqual => write!(f, "!="),
882            BinaryOperator::StrictEqual => write!(f, "==="),
883            BinaryOperator::StrictNotEqual => write!(f, "!=="),
884            BinaryOperator::LessThan => write!(f, "<"),
885            BinaryOperator::LessEqual => write!(f, "<="),
886            BinaryOperator::GreaterThan => write!(f, ">"),
887            BinaryOperator::GreaterEqual => write!(f, ">="),
888            BinaryOperator::ShiftLeft => write!(f, "<<"),
889            BinaryOperator::ShiftRight => write!(f, ">>"),
890            BinaryOperator::UnsignedShiftRight => write!(f, ">>>"),
891            BinaryOperator::Add => write!(f, "+"),
892            BinaryOperator::Subtract => write!(f, "-"),
893            BinaryOperator::Multiply => write!(f, "*"),
894            BinaryOperator::Divide => write!(f, "/"),
895            BinaryOperator::Modulo => write!(f, "%"),
896            BinaryOperator::Exponent => write!(f, "**"),
897            BinaryOperator::BitwiseOr => write!(f, "|"),
898            BinaryOperator::BitwiseXor => write!(f, "^"),
899            BinaryOperator::BitwiseAnd => write!(f, "&"),
900            BinaryOperator::In => write!(f, "in"),
901            BinaryOperator::InstanceOf => write!(f, "instanceof"),
902        }
903    }
904}
905
906#[derive(Debug, Clone, Copy, PartialEq, Eq)]
907pub enum UnaryOperator {
908    Minus,
909    Plus,
910    Not,
911    BitwiseNot,
912    TypeOf,
913    Void,
914}
915
916impl std::fmt::Display for UnaryOperator {
917    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
918        match self {
919            UnaryOperator::Minus => write!(f, "-"),
920            UnaryOperator::Plus => write!(f, "+"),
921            UnaryOperator::Not => write!(f, "!"),
922            UnaryOperator::BitwiseNot => write!(f, "~"),
923            UnaryOperator::TypeOf => write!(f, "typeof"),
924            UnaryOperator::Void => write!(f, "void"),
925        }
926    }
927}
928
929#[derive(Debug, Clone, Copy, PartialEq, Eq)]
930pub enum UpdateOperator {
931    Increment,
932    Decrement,
933}
934
935impl std::fmt::Display for UpdateOperator {
936    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
937        match self {
938            UpdateOperator::Increment => write!(f, "++"),
939            UpdateOperator::Decrement => write!(f, "--"),
940        }
941    }
942}
943
944#[derive(Debug, Clone, Copy, PartialEq, Eq)]
945pub enum FunctionExpressionType {
946    ArrowFunctionExpression,
947    FunctionExpression,
948    FunctionDeclaration,
949}
950
951#[derive(Debug, Clone)]
952pub struct TemplateQuasi {
953    pub raw: String,
954    pub cooked: Option<String>,
955}
956
957#[derive(Debug, Clone)]
958pub struct ManualMemoDependency {
959    pub root: ManualMemoDependencyRoot,
960    pub path: Vec<DependencyPathEntry>,
961    pub loc: Option<SourceLocation>,
962}
963
964#[derive(Debug, Clone)]
965pub enum ManualMemoDependencyRoot {
966    NamedLocal { value: Place, constant: bool },
967    Global { identifier_name: String },
968}
969
970#[derive(Debug, Clone, PartialEq, Eq)]
971pub struct DependencyPathEntry {
972    pub property: PropertyLiteral,
973    pub optional: bool,
974    pub loc: Option<SourceLocation>,
975}
976
977// =============================================================================
978// Place, Identifier, and related types
979// =============================================================================
980
981#[derive(Debug, Clone)]
982pub struct Place {
983    pub identifier: IdentifierId,
984    pub effect: Effect,
985    pub reactive: bool,
986    pub loc: Option<SourceLocation>,
987}
988
989#[derive(Debug, Clone)]
990pub struct Identifier {
991    pub id: IdentifierId,
992    pub declaration_id: DeclarationId,
993    pub name: Option<IdentifierName>,
994    pub mutable_range: MutableRange,
995    pub scope: Option<ScopeId>,
996    pub type_: TypeId,
997    pub loc: Option<SourceLocation>,
998}
999
1000#[derive(Debug, Clone)]
1001pub struct MutableRange {
1002    /// Unique identity for this logical range. Cloning preserves the ID
1003    /// (same logical range); use `Environment::new_mutable_range()` to create
1004    /// a range with a fresh ID.
1005    pub id: MutableRangeId,
1006    pub start: EvaluationOrder,
1007    pub end: EvaluationOrder,
1008}
1009
1010impl MutableRange {
1011    /// Returns true if the given evaluation order falls within this mutable range.
1012    /// Corresponds to TS `inRange({id}, range)` / `isMutable(instr, place)`.
1013    pub fn contains(&self, eval_order: EvaluationOrder) -> bool {
1014        eval_order >= self.start && eval_order < self.end
1015    }
1016
1017    /// Returns true if this range has the same identity as `other`.
1018    /// In the TS compiler, this corresponds to checking whether two mutableRange
1019    /// references point to the same JS object (=== identity).
1020    pub fn same_range(&self, other: &MutableRange) -> bool {
1021        self.id == other.id
1022    }
1023}
1024
1025#[derive(Debug, Clone)]
1026pub enum IdentifierName {
1027    Named(String),
1028    Promoted(String),
1029}
1030
1031impl IdentifierName {
1032    pub fn value(&self) -> &str {
1033        match self {
1034            IdentifierName::Named(v) | IdentifierName::Promoted(v) => v,
1035        }
1036    }
1037}
1038
1039#[derive(
1040    Debug,
1041    Clone,
1042    Copy,
1043    PartialEq,
1044    Eq,
1045    serde::Serialize,
1046    serde::Deserialize
1047)]
1048pub enum Effect {
1049    #[serde(rename = "<unknown>")]
1050    Unknown,
1051    #[serde(rename = "freeze")]
1052    Freeze,
1053    #[serde(rename = "read")]
1054    Read,
1055    #[serde(rename = "capture")]
1056    Capture,
1057    #[serde(rename = "mutate-iterator?")]
1058    ConditionallyMutateIterator,
1059    #[serde(rename = "mutate?")]
1060    ConditionallyMutate,
1061    #[serde(rename = "mutate")]
1062    Mutate,
1063    #[serde(rename = "store")]
1064    Store,
1065}
1066
1067impl Effect {
1068    /// Returns true if this effect represents a mutable operation.
1069    /// Mutable effects are: Capture, Store, ConditionallyMutate,
1070    /// ConditionallyMutateIterator, and Mutate.
1071    pub fn is_mutable(&self) -> bool {
1072        matches!(
1073            self,
1074            Effect::Capture
1075                | Effect::Store
1076                | Effect::ConditionallyMutate
1077                | Effect::ConditionallyMutateIterator
1078                | Effect::Mutate
1079        )
1080    }
1081}
1082
1083impl std::fmt::Display for Effect {
1084    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1085        match self {
1086            Effect::Unknown => write!(f, "<unknown>"),
1087            Effect::Freeze => write!(f, "freeze"),
1088            Effect::Read => write!(f, "read"),
1089            Effect::Capture => write!(f, "capture"),
1090            Effect::ConditionallyMutateIterator => write!(f, "mutate-iterator?"),
1091            Effect::ConditionallyMutate => write!(f, "mutate?"),
1092            Effect::Mutate => write!(f, "mutate"),
1093            Effect::Store => write!(f, "store"),
1094        }
1095    }
1096}
1097
1098#[derive(Debug, Clone)]
1099pub struct SpreadPattern {
1100    pub place: Place,
1101}
1102
1103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1104pub enum Hole {
1105    Hole,
1106}
1107
1108#[derive(Debug, Clone)]
1109pub struct ArrayPattern {
1110    pub items: Vec<ArrayPatternElement>,
1111    pub loc: Option<SourceLocation>,
1112}
1113
1114#[derive(Debug, Clone)]
1115pub enum ArrayPatternElement {
1116    Place(Place),
1117    Spread(SpreadPattern),
1118    Hole,
1119}
1120
1121#[derive(Debug, Clone)]
1122pub struct ObjectPattern {
1123    pub properties: Vec<ObjectPropertyOrSpread>,
1124    pub loc: Option<SourceLocation>,
1125}
1126
1127#[derive(Debug, Clone)]
1128pub enum ObjectPropertyOrSpread {
1129    Property(ObjectProperty),
1130    Spread(SpreadPattern),
1131}
1132
1133#[derive(Debug, Clone)]
1134pub struct ObjectProperty {
1135    pub key: ObjectPropertyKey,
1136    pub property_type: ObjectPropertyType,
1137    pub place: Place,
1138}
1139
1140#[derive(Debug, Clone)]
1141pub enum ObjectPropertyKey {
1142    String { name: String },
1143    Identifier { name: String },
1144    Computed { name: Place },
1145    Number { name: FloatValue },
1146}
1147
1148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1149pub enum ObjectPropertyType {
1150    Property,
1151    Method,
1152}
1153
1154impl std::fmt::Display for ObjectPropertyType {
1155    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1156        match self {
1157            ObjectPropertyType::Property => write!(f, "property"),
1158            ObjectPropertyType::Method => write!(f, "method"),
1159        }
1160    }
1161}
1162
1163#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1164pub enum PropertyLiteral {
1165    String(String),
1166    Number(FloatValue),
1167}
1168
1169impl std::fmt::Display for PropertyLiteral {
1170    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1171        match self {
1172            PropertyLiteral::String(s) => write!(f, "{}", s),
1173            PropertyLiteral::Number(n) => write!(f, "{}", n),
1174        }
1175    }
1176}
1177
1178#[derive(Debug, Clone)]
1179pub enum PlaceOrSpread {
1180    Place(Place),
1181    Spread(SpreadPattern),
1182}
1183
1184#[derive(Debug, Clone)]
1185pub enum ArrayElement {
1186    Place(Place),
1187    Spread(SpreadPattern),
1188    Hole,
1189}
1190
1191#[derive(Debug, Clone)]
1192pub struct LoweredFunction {
1193    pub func: FunctionId,
1194}
1195
1196#[derive(Debug, Clone)]
1197pub struct BuiltinTag {
1198    pub name: String,
1199    pub loc: Option<SourceLocation>,
1200}
1201
1202#[derive(Debug, Clone)]
1203pub enum JsxTag {
1204    Place(Place),
1205    Builtin(BuiltinTag),
1206}
1207
1208#[derive(Debug, Clone)]
1209pub enum JsxAttribute {
1210    SpreadAttribute { argument: Place },
1211    Attribute { name: String, place: Place },
1212}
1213
1214// =============================================================================
1215// Variable Binding types
1216// =============================================================================
1217
1218#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1219pub enum BindingKind {
1220    Var,
1221    Let,
1222    Const,
1223    Param,
1224    Module,
1225    Hoisted,
1226    Local,
1227    Unknown,
1228}
1229
1230#[derive(Debug, Clone)]
1231pub enum VariableBinding {
1232    Identifier {
1233        identifier: IdentifierId,
1234        binding_kind: BindingKind,
1235    },
1236    Global {
1237        name: String,
1238    },
1239    ImportDefault {
1240        name: String,
1241        module: String,
1242    },
1243    ImportSpecifier {
1244        name: String,
1245        module: String,
1246        imported: String,
1247    },
1248    ImportNamespace {
1249        name: String,
1250        module: String,
1251    },
1252    ModuleLocal {
1253        name: String,
1254    },
1255}
1256
1257#[derive(Debug, Clone)]
1258pub enum NonLocalBinding {
1259    ImportDefault {
1260        name: String,
1261        module: String,
1262    },
1263    ImportSpecifier {
1264        name: String,
1265        module: String,
1266        imported: String,
1267    },
1268    ImportNamespace {
1269        name: String,
1270        module: String,
1271    },
1272    ModuleLocal {
1273        name: String,
1274    },
1275    Global {
1276        name: String,
1277    },
1278}
1279
1280impl NonLocalBinding {
1281    /// Returns the `name` field common to all variants.
1282    pub fn name(&self) -> &str {
1283        match self {
1284            NonLocalBinding::ImportDefault { name, .. }
1285            | NonLocalBinding::ImportSpecifier { name, .. }
1286            | NonLocalBinding::ImportNamespace { name, .. }
1287            | NonLocalBinding::ModuleLocal { name, .. }
1288            | NonLocalBinding::Global { name, .. } => name,
1289        }
1290    }
1291}
1292
1293// =============================================================================
1294// Type system (from Types.ts)
1295// =============================================================================
1296
1297#[derive(Debug, Clone)]
1298pub enum Type {
1299    Primitive,
1300    Function {
1301        shape_id: Option<String>,
1302        return_type: Box<Type>,
1303        is_constructor: bool,
1304    },
1305    Object {
1306        shape_id: Option<String>,
1307    },
1308    TypeVar {
1309        id: TypeId,
1310    },
1311    Poly,
1312    Phi {
1313        operands: Vec<Type>,
1314    },
1315    Property {
1316        object_type: Box<Type>,
1317        object_name: String,
1318        property_name: PropertyNameKind,
1319    },
1320    ObjectMethod,
1321}
1322
1323#[derive(Debug, Clone)]
1324pub enum PropertyNameKind {
1325    Literal { value: PropertyLiteral },
1326    Computed { value: Box<Type> },
1327}
1328
1329// =============================================================================
1330// ReactiveScope
1331// =============================================================================
1332
1333#[derive(Debug, Clone)]
1334pub struct ReactiveScope {
1335    pub id: ScopeId,
1336    pub range: MutableRange,
1337
1338    /// The inputs to this reactive scope (populated by later passes)
1339    pub dependencies: Vec<ReactiveScopeDependency>,
1340
1341    /// The set of values produced by this scope (populated by later passes)
1342    pub declarations: Vec<(IdentifierId, ReactiveScopeDeclaration)>,
1343
1344    /// Identifiers which are reassigned by this scope (populated by later passes)
1345    pub reassignments: Vec<IdentifierId>,
1346
1347    /// If the scope contains an early return, this stores info about it (populated by later passes)
1348    pub early_return_value: Option<ReactiveScopeEarlyReturn>,
1349
1350    /// Scopes that were merged into this one (populated by later passes)
1351    pub merged: Vec<ScopeId>,
1352
1353    /// Source location spanning the scope
1354    pub loc: Option<SourceLocation>,
1355}
1356
1357/// A dependency of a reactive scope.
1358#[derive(Debug, Clone)]
1359pub struct ReactiveScopeDependency {
1360    pub identifier: IdentifierId,
1361    pub reactive: bool,
1362    pub path: Vec<DependencyPathEntry>,
1363    pub loc: Option<SourceLocation>,
1364}
1365
1366/// A declaration produced by a reactive scope.
1367#[derive(Debug, Clone)]
1368pub struct ReactiveScopeDeclaration {
1369    pub identifier: IdentifierId,
1370    pub scope: ScopeId,
1371}
1372
1373/// Early return value info for a reactive scope.
1374#[derive(Debug, Clone)]
1375pub struct ReactiveScopeEarlyReturn {
1376    pub value: IdentifierId,
1377    pub loc: Option<SourceLocation>,
1378    pub label: BlockId,
1379}
1380
1381// =============================================================================
1382// Aliasing effects (runtime types, from AliasingEffects.ts)
1383// =============================================================================
1384
1385use crate::object_shape::FunctionSignature;
1386use crate::type_config::ValueKind;
1387use crate::type_config::ValueReason;
1388
1389/// Reason for a mutation, used for generating hints (e.g. rename to "Ref").
1390#[derive(Debug, Clone, PartialEq, Eq)]
1391pub enum MutationReason {
1392    AssignCurrentProperty,
1393}
1394
1395/// Describes the aliasing/mutation/data-flow effects of an instruction or terminal.
1396/// Ported from TS `AliasingEffect` in `AliasingEffects.ts`.
1397#[derive(Debug, Clone)]
1398pub enum AliasingEffect {
1399    /// Marks the given value and its direct aliases as frozen.
1400    Freeze { value: Place, reason: ValueReason },
1401    /// Mutate the value and any direct aliases.
1402    Mutate {
1403        value: Place,
1404        reason: Option<MutationReason>,
1405    },
1406    /// Mutate the value conditionally (only if mutable).
1407    MutateConditionally { value: Place },
1408    /// Mutate the value and transitive captures.
1409    MutateTransitive { value: Place },
1410    /// Mutate the value and transitive captures conditionally.
1411    MutateTransitiveConditionally { value: Place },
1412    /// Information flow from `from` to `into` (non-aliasing capture).
1413    Capture { from: Place, into: Place },
1414    /// Direct aliasing: mutation of `into` implies mutation of `from`.
1415    Alias { from: Place, into: Place },
1416    /// Potential aliasing relationship.
1417    MaybeAlias { from: Place, into: Place },
1418    /// Direct assignment: `into = from`.
1419    Assign { from: Place, into: Place },
1420    /// Creates a value of the given kind at the given place.
1421    Create {
1422        into: Place,
1423        value: ValueKind,
1424        reason: ValueReason,
1425    },
1426    /// Creates a new value with the same kind as the source.
1427    CreateFrom { from: Place, into: Place },
1428    /// Immutable data flow (escape analysis only, no mutable range influence).
1429    ImmutableCapture { from: Place, into: Place },
1430    /// Function call application.
1431    Apply {
1432        receiver: Place,
1433        function: Place,
1434        mutates_function: bool,
1435        args: Vec<PlaceOrSpreadOrHole>,
1436        into: Place,
1437        signature: Option<FunctionSignature>,
1438        loc: Option<SourceLocation>,
1439    },
1440    /// Function expression creation with captures.
1441    CreateFunction {
1442        captures: Vec<Place>,
1443        function_id: FunctionId,
1444        into: Place,
1445    },
1446    /// Mutation of a value known to be frozen (error).
1447    MutateFrozen {
1448        place: Place,
1449        error: CompilerDiagnostic,
1450    },
1451    /// Mutation of a global value (error).
1452    MutateGlobal {
1453        place: Place,
1454        error: CompilerDiagnostic,
1455    },
1456    /// Side-effect not safe during render.
1457    Impure {
1458        place: Place,
1459        error: CompilerDiagnostic,
1460    },
1461    /// Value is accessed during render.
1462    Render { place: Place },
1463}
1464
1465/// Combined Place/Spread/Hole for Apply args.
1466#[derive(Debug, Clone)]
1467pub enum PlaceOrSpreadOrHole {
1468    Place(Place),
1469    Spread(SpreadPattern),
1470    Hole,
1471}
1472
1473/// Aliasing signature for function calls.
1474/// Ported from TS `AliasingSignature` in `AliasingEffects.ts`.
1475#[derive(Debug, Clone)]
1476pub struct AliasingSignature {
1477    pub receiver: IdentifierId,
1478    pub params: Vec<IdentifierId>,
1479    pub rest: Option<IdentifierId>,
1480    pub returns: IdentifierId,
1481    pub effects: Vec<AliasingEffect>,
1482    pub temporaries: Vec<Place>,
1483}
1484
1485// =============================================================================
1486// Type helper functions (ported from HIR.ts)
1487// =============================================================================
1488
1489use crate::object_shape::BUILT_IN_ARRAY_ID;
1490use crate::object_shape::BUILT_IN_JSX_ID;
1491use crate::object_shape::BUILT_IN_MAP_ID;
1492use crate::object_shape::BUILT_IN_PROPS_ID;
1493use crate::object_shape::BUILT_IN_REF_VALUE_ID;
1494use crate::object_shape::BUILT_IN_SET_ID;
1495use crate::object_shape::BUILT_IN_USE_OPERATOR_ID;
1496use crate::object_shape::BUILT_IN_USE_REF_ID;
1497
1498/// Returns true if the type (looked up via identifier) is primitive.
1499pub fn is_primitive_type(ty: &Type) -> bool {
1500    matches!(ty, Type::Primitive)
1501}
1502
1503/// Returns true if the type is the props object.
1504pub fn is_props_type(ty: &Type) -> bool {
1505    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_PROPS_ID)
1506}
1507
1508/// Returns true if the type is an array.
1509pub fn is_array_type(ty: &Type) -> bool {
1510    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_ARRAY_ID)
1511}
1512
1513/// Returns true if the type is a Set.
1514pub fn is_set_type(ty: &Type) -> bool {
1515    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_SET_ID)
1516}
1517
1518/// Returns true if the type is a Map.
1519pub fn is_map_type(ty: &Type) -> bool {
1520    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_MAP_ID)
1521}
1522
1523/// Returns true if the type is JSX.
1524pub fn is_jsx_type(ty: &Type) -> bool {
1525    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_JSX_ID)
1526}
1527
1528/// Returns true if the identifier type is a ref value.
1529pub fn is_ref_value_type(ty: &Type) -> bool {
1530    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_REF_VALUE_ID)
1531}
1532
1533/// Returns true if the identifier type is useRef.
1534pub fn is_use_ref_type(ty: &Type) -> bool {
1535    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_USE_REF_ID)
1536}
1537
1538/// Returns true if the type is a ref or ref value.
1539pub fn is_ref_or_ref_value(ty: &Type) -> bool {
1540    is_use_ref_type(ty) || is_ref_value_type(ty)
1541}
1542
1543/// Returns true if the type is a useState result (BuiltInUseState).
1544pub fn is_use_state_type(ty: &Type) -> bool {
1545    matches!(ty, Type::Object { shape_id: Some(id) } if id == object_shape::BUILT_IN_USE_STATE_ID)
1546}
1547
1548/// Returns true if the type is a setState function (BuiltInSetState).
1549pub fn is_set_state_type(ty: &Type) -> bool {
1550    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_SET_STATE_ID)
1551}
1552
1553/// Returns true if the type is a useEffect hook.
1554pub fn is_use_effect_hook_type(ty: &Type) -> bool {
1555    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_USE_EFFECT_HOOK_ID)
1556}
1557
1558/// Returns true if the type is a useLayoutEffect hook.
1559pub fn is_use_layout_effect_hook_type(ty: &Type) -> bool {
1560    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_USE_LAYOUT_EFFECT_HOOK_ID)
1561}
1562
1563/// Returns true if the type is a useInsertionEffect hook.
1564pub fn is_use_insertion_effect_hook_type(ty: &Type) -> bool {
1565    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_USE_INSERTION_EFFECT_HOOK_ID)
1566}
1567
1568/// Returns true if the type is a useEffectEvent function.
1569pub fn is_use_effect_event_type(ty: &Type) -> bool {
1570    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_USE_EFFECT_EVENT_ID)
1571}
1572
1573/// Returns true if the type is a ref or ref-like mutable type (e.g. Reanimated shared values).
1574pub fn is_ref_or_ref_like_mutable_type(ty: &Type) -> bool {
1575    matches!(ty, Type::Object { shape_id: Some(id) }
1576        if id == object_shape::BUILT_IN_USE_REF_ID || id == object_shape::REANIMATED_SHARED_VALUE_ID)
1577}
1578
1579/// Returns true if the type is the `use()` operator (React.use).
1580pub fn is_use_operator_type(ty: &Type) -> bool {
1581    matches!(
1582        ty,
1583        Type::Function { shape_id: Some(id), .. }
1584            if id == BUILT_IN_USE_OPERATOR_ID
1585    )
1586}
1587
1588/// Returns true if the type is a plain object (BuiltInObject).
1589pub fn is_plain_object_type(ty: &Type) -> bool {
1590    matches!(ty, Type::Object { shape_id: Some(id) } if id == object_shape::BUILT_IN_OBJECT_ID)
1591}
1592
1593/// Returns true if the type is a startTransition function (BuiltInStartTransition).
1594pub fn is_start_transition_type(ty: &Type) -> bool {
1595    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_START_TRANSITION_ID)
1596}
1597
1598#[cfg(test)]
1599mod tests {
1600    use super::*;
1601
1602    #[test]
1603    fn test_format_js_number() {
1604        // Scientific notation for large numbers (>= 1e21)
1605        assert_eq!(format_js_number(1e21), "1e+21");
1606        assert_eq!(format_js_number(1.5e21), "1.5e+21");
1607        assert_eq!(
1608            format_js_number(2.18739127891275e22),
1609            "2.18739127891275e+22"
1610        );
1611        assert_eq!(format_js_number(1e100), "1e+100");
1612        assert_eq!(format_js_number(-1e21), "-1e+21");
1613        assert_eq!(format_js_number(-1e100), "-1e+100");
1614
1615        // Scientific notation for small numbers (< 1e-6)
1616        assert_eq!(format_js_number(1e-7), "1e-7");
1617        assert_eq!(format_js_number(5e-7), "5e-7");
1618        assert_eq!(format_js_number(1.5e-8), "1.5e-8");
1619        assert_eq!(format_js_number(-1.5e-8), "-1.5e-8");
1620
1621        // Non-scientific large numbers (< 1e21)
1622        assert_eq!(format_js_number(1e20), "100000000000000000000");
1623        assert_eq!(format_js_number(1e-6), "0.000001");
1624
1625        // Integers
1626        assert_eq!(format_js_number(0.0), "0");
1627        assert_eq!(format_js_number(-0.0), "0");
1628        assert_eq!(format_js_number(1.0), "1");
1629        assert_eq!(format_js_number(100.0), "100");
1630
1631        // Regular floats
1632        assert_eq!(format_js_number(1.5), "1.5");
1633        assert_eq!(format_js_number(0.5), "0.5");
1634        assert_eq!(format_js_number(0.1), "0.1");
1635
1636        // Special values
1637        assert_eq!(format_js_number(f64::NAN), "NaN");
1638        assert_eq!(format_js_number(f64::INFINITY), "Infinity");
1639        assert_eq!(format_js_number(f64::NEG_INFINITY), "-Infinity");
1640    }
1641}