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 react_compiler_utils::FxIndexMap;
13use react_compiler_utils::FxIndexSet;
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 FxHashMap 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: FxIndexMap<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: FxIndexSet<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: FxIndexMap<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, preserved verbatim so codegen can re-emit it.
783        original_node: Option<react_compiler_ast::OriginalNode>,
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(react_compiler_diagnostics::JsString),
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(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
1040pub enum Effect {
1041    #[serde(rename = "<unknown>")]
1042    Unknown,
1043    #[serde(rename = "freeze")]
1044    Freeze,
1045    #[serde(rename = "read")]
1046    Read,
1047    #[serde(rename = "capture")]
1048    Capture,
1049    #[serde(rename = "mutate-iterator?")]
1050    ConditionallyMutateIterator,
1051    #[serde(rename = "mutate?")]
1052    ConditionallyMutate,
1053    #[serde(rename = "mutate")]
1054    Mutate,
1055    #[serde(rename = "store")]
1056    Store,
1057}
1058
1059impl Effect {
1060    /// Returns true if this effect represents a mutable operation.
1061    /// Mutable effects are: Capture, Store, ConditionallyMutate,
1062    /// ConditionallyMutateIterator, and Mutate.
1063    pub fn is_mutable(&self) -> bool {
1064        matches!(
1065            self,
1066            Effect::Capture
1067                | Effect::Store
1068                | Effect::ConditionallyMutate
1069                | Effect::ConditionallyMutateIterator
1070                | Effect::Mutate
1071        )
1072    }
1073}
1074
1075impl std::fmt::Display for Effect {
1076    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1077        match self {
1078            Effect::Unknown => write!(f, "<unknown>"),
1079            Effect::Freeze => write!(f, "freeze"),
1080            Effect::Read => write!(f, "read"),
1081            Effect::Capture => write!(f, "capture"),
1082            Effect::ConditionallyMutateIterator => write!(f, "mutate-iterator?"),
1083            Effect::ConditionallyMutate => write!(f, "mutate?"),
1084            Effect::Mutate => write!(f, "mutate"),
1085            Effect::Store => write!(f, "store"),
1086        }
1087    }
1088}
1089
1090#[derive(Debug, Clone)]
1091pub struct SpreadPattern {
1092    pub place: Place,
1093}
1094
1095#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1096pub enum Hole {
1097    Hole,
1098}
1099
1100#[derive(Debug, Clone)]
1101pub struct ArrayPattern {
1102    pub items: Vec<ArrayPatternElement>,
1103    pub loc: Option<SourceLocation>,
1104}
1105
1106#[derive(Debug, Clone)]
1107pub enum ArrayPatternElement {
1108    Place(Place),
1109    Spread(SpreadPattern),
1110    Hole,
1111}
1112
1113#[derive(Debug, Clone)]
1114pub struct ObjectPattern {
1115    pub properties: Vec<ObjectPropertyOrSpread>,
1116    pub loc: Option<SourceLocation>,
1117}
1118
1119#[derive(Debug, Clone)]
1120pub enum ObjectPropertyOrSpread {
1121    Property(ObjectProperty),
1122    Spread(SpreadPattern),
1123}
1124
1125#[derive(Debug, Clone)]
1126pub struct ObjectProperty {
1127    pub key: ObjectPropertyKey,
1128    pub property_type: ObjectPropertyType,
1129    pub place: Place,
1130}
1131
1132#[derive(Debug, Clone)]
1133pub enum ObjectPropertyKey {
1134    String { name: String },
1135    Identifier { name: String },
1136    Computed { name: Place },
1137    Number { name: FloatValue },
1138}
1139
1140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1141pub enum ObjectPropertyType {
1142    Property,
1143    Method,
1144}
1145
1146impl std::fmt::Display for ObjectPropertyType {
1147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1148        match self {
1149            ObjectPropertyType::Property => write!(f, "property"),
1150            ObjectPropertyType::Method => write!(f, "method"),
1151        }
1152    }
1153}
1154
1155#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1156pub enum PropertyLiteral {
1157    String(String),
1158    Number(FloatValue),
1159}
1160
1161impl std::fmt::Display for PropertyLiteral {
1162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1163        match self {
1164            PropertyLiteral::String(s) => write!(f, "{}", s),
1165            PropertyLiteral::Number(n) => write!(f, "{}", n),
1166        }
1167    }
1168}
1169
1170#[derive(Debug, Clone)]
1171pub enum PlaceOrSpread {
1172    Place(Place),
1173    Spread(SpreadPattern),
1174}
1175
1176#[derive(Debug, Clone)]
1177pub enum ArrayElement {
1178    Place(Place),
1179    Spread(SpreadPattern),
1180    Hole,
1181}
1182
1183#[derive(Debug, Clone)]
1184pub struct LoweredFunction {
1185    pub func: FunctionId,
1186}
1187
1188#[derive(Debug, Clone)]
1189pub struct BuiltinTag {
1190    pub name: String,
1191    pub loc: Option<SourceLocation>,
1192}
1193
1194#[derive(Debug, Clone)]
1195pub enum JsxTag {
1196    Place(Place),
1197    Builtin(BuiltinTag),
1198}
1199
1200#[derive(Debug, Clone)]
1201pub enum JsxAttribute {
1202    SpreadAttribute { argument: Place },
1203    Attribute { name: String, place: Place },
1204}
1205
1206// =============================================================================
1207// Variable Binding types
1208// =============================================================================
1209
1210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1211pub enum BindingKind {
1212    Var,
1213    Let,
1214    Const,
1215    Param,
1216    Module,
1217    Hoisted,
1218    Local,
1219    Unknown,
1220}
1221
1222#[derive(Debug, Clone)]
1223pub enum VariableBinding {
1224    Identifier {
1225        identifier: IdentifierId,
1226        binding_kind: BindingKind,
1227    },
1228    Global {
1229        name: String,
1230    },
1231    ImportDefault {
1232        name: String,
1233        module: String,
1234    },
1235    ImportSpecifier {
1236        name: String,
1237        module: String,
1238        imported: String,
1239    },
1240    ImportNamespace {
1241        name: String,
1242        module: String,
1243    },
1244    ModuleLocal {
1245        name: String,
1246    },
1247}
1248
1249#[derive(Debug, Clone)]
1250pub enum NonLocalBinding {
1251    ImportDefault {
1252        name: String,
1253        module: String,
1254    },
1255    ImportSpecifier {
1256        name: String,
1257        module: String,
1258        imported: String,
1259    },
1260    ImportNamespace {
1261        name: String,
1262        module: String,
1263    },
1264    ModuleLocal {
1265        name: String,
1266    },
1267    Global {
1268        name: String,
1269    },
1270}
1271
1272impl NonLocalBinding {
1273    /// Returns the `name` field common to all variants.
1274    pub fn name(&self) -> &str {
1275        match self {
1276            NonLocalBinding::ImportDefault { name, .. }
1277            | NonLocalBinding::ImportSpecifier { name, .. }
1278            | NonLocalBinding::ImportNamespace { name, .. }
1279            | NonLocalBinding::ModuleLocal { name, .. }
1280            | NonLocalBinding::Global { name, .. } => name,
1281        }
1282    }
1283}
1284
1285// =============================================================================
1286// Type system (from Types.ts)
1287// =============================================================================
1288
1289#[derive(Debug, Clone)]
1290pub enum Type {
1291    Primitive,
1292    Function {
1293        shape_id: Option<String>,
1294        return_type: Box<Type>,
1295        is_constructor: bool,
1296    },
1297    Object {
1298        shape_id: Option<String>,
1299    },
1300    TypeVar {
1301        id: TypeId,
1302    },
1303    Poly,
1304    Phi {
1305        operands: Vec<Type>,
1306    },
1307    Property {
1308        object_type: Box<Type>,
1309        object_name: String,
1310        property_name: PropertyNameKind,
1311    },
1312    ObjectMethod,
1313}
1314
1315#[derive(Debug, Clone)]
1316pub enum PropertyNameKind {
1317    Literal { value: PropertyLiteral },
1318    Computed { value: Box<Type> },
1319}
1320
1321// =============================================================================
1322// ReactiveScope
1323// =============================================================================
1324
1325#[derive(Debug, Clone)]
1326pub struct ReactiveScope {
1327    pub id: ScopeId,
1328    pub range: MutableRange,
1329
1330    /// The inputs to this reactive scope (populated by later passes)
1331    pub dependencies: Vec<ReactiveScopeDependency>,
1332
1333    /// The set of values produced by this scope (populated by later passes)
1334    pub declarations: Vec<(IdentifierId, ReactiveScopeDeclaration)>,
1335
1336    /// Identifiers which are reassigned by this scope (populated by later passes)
1337    pub reassignments: Vec<IdentifierId>,
1338
1339    /// If the scope contains an early return, this stores info about it (populated by later passes)
1340    pub early_return_value: Option<ReactiveScopeEarlyReturn>,
1341
1342    /// Scopes that were merged into this one (populated by later passes)
1343    pub merged: Vec<ScopeId>,
1344
1345    /// Source location spanning the scope
1346    pub loc: Option<SourceLocation>,
1347}
1348
1349/// A dependency of a reactive scope.
1350#[derive(Debug, Clone)]
1351pub struct ReactiveScopeDependency {
1352    pub identifier: IdentifierId,
1353    pub reactive: bool,
1354    pub path: Vec<DependencyPathEntry>,
1355    pub loc: Option<SourceLocation>,
1356}
1357
1358/// A declaration produced by a reactive scope.
1359#[derive(Debug, Clone)]
1360pub struct ReactiveScopeDeclaration {
1361    pub identifier: IdentifierId,
1362    pub scope: ScopeId,
1363}
1364
1365/// Early return value info for a reactive scope.
1366#[derive(Debug, Clone)]
1367pub struct ReactiveScopeEarlyReturn {
1368    pub value: IdentifierId,
1369    pub loc: Option<SourceLocation>,
1370    pub label: BlockId,
1371}
1372
1373// =============================================================================
1374// Aliasing effects (runtime types, from AliasingEffects.ts)
1375// =============================================================================
1376
1377use crate::object_shape::FunctionSignature;
1378use crate::type_config::ValueKind;
1379use crate::type_config::ValueReason;
1380
1381/// Reason for a mutation, used for generating hints (e.g. rename to "Ref").
1382#[derive(Debug, Clone, PartialEq, Eq)]
1383pub enum MutationReason {
1384    AssignCurrentProperty,
1385}
1386
1387/// Describes the aliasing/mutation/data-flow effects of an instruction or terminal.
1388/// Ported from TS `AliasingEffect` in `AliasingEffects.ts`.
1389#[derive(Debug, Clone)]
1390pub enum AliasingEffect {
1391    /// Marks the given value and its direct aliases as frozen.
1392    Freeze { value: Place, reason: ValueReason },
1393    /// Mutate the value and any direct aliases.
1394    Mutate {
1395        value: Place,
1396        reason: Option<MutationReason>,
1397    },
1398    /// Mutate the value conditionally (only if mutable).
1399    MutateConditionally { value: Place },
1400    /// Mutate the value and transitive captures.
1401    MutateTransitive { value: Place },
1402    /// Mutate the value and transitive captures conditionally.
1403    MutateTransitiveConditionally { value: Place },
1404    /// Information flow from `from` to `into` (non-aliasing capture).
1405    Capture { from: Place, into: Place },
1406    /// Direct aliasing: mutation of `into` implies mutation of `from`.
1407    Alias { from: Place, into: Place },
1408    /// Potential aliasing relationship.
1409    MaybeAlias { from: Place, into: Place },
1410    /// Direct assignment: `into = from`.
1411    Assign { from: Place, into: Place },
1412    /// Creates a value of the given kind at the given place.
1413    Create {
1414        into: Place,
1415        value: ValueKind,
1416        reason: ValueReason,
1417    },
1418    /// Creates a new value with the same kind as the source.
1419    CreateFrom { from: Place, into: Place },
1420    /// Immutable data flow (escape analysis only, no mutable range influence).
1421    ImmutableCapture { from: Place, into: Place },
1422    /// Function call application.
1423    Apply {
1424        receiver: Place,
1425        function: Place,
1426        mutates_function: bool,
1427        args: Vec<PlaceOrSpreadOrHole>,
1428        into: Place,
1429        signature: Option<FunctionSignature>,
1430        loc: Option<SourceLocation>,
1431    },
1432    /// Function expression creation with captures.
1433    CreateFunction {
1434        captures: Vec<Place>,
1435        function_id: FunctionId,
1436        into: Place,
1437    },
1438    /// Mutation of a value known to be frozen (error).
1439    MutateFrozen {
1440        place: Place,
1441        error: CompilerDiagnostic,
1442    },
1443    /// Mutation of a global value (error).
1444    MutateGlobal {
1445        place: Place,
1446        error: CompilerDiagnostic,
1447    },
1448    /// Side-effect not safe during render.
1449    Impure {
1450        place: Place,
1451        error: CompilerDiagnostic,
1452    },
1453    /// Value is accessed during render.
1454    Render { place: Place },
1455}
1456
1457/// Combined Place/Spread/Hole for Apply args.
1458#[derive(Debug, Clone)]
1459pub enum PlaceOrSpreadOrHole {
1460    Place(Place),
1461    Spread(SpreadPattern),
1462    Hole,
1463}
1464
1465/// Aliasing signature for function calls.
1466/// Ported from TS `AliasingSignature` in `AliasingEffects.ts`.
1467#[derive(Debug, Clone)]
1468pub struct AliasingSignature {
1469    pub receiver: IdentifierId,
1470    pub params: Vec<IdentifierId>,
1471    pub rest: Option<IdentifierId>,
1472    pub returns: IdentifierId,
1473    pub effects: Vec<AliasingEffect>,
1474    pub temporaries: Vec<Place>,
1475}
1476
1477// =============================================================================
1478// Type helper functions (ported from HIR.ts)
1479// =============================================================================
1480
1481use crate::object_shape::BUILT_IN_ARRAY_ID;
1482use crate::object_shape::BUILT_IN_JSX_ID;
1483use crate::object_shape::BUILT_IN_MAP_ID;
1484use crate::object_shape::BUILT_IN_PROPS_ID;
1485use crate::object_shape::BUILT_IN_REF_VALUE_ID;
1486use crate::object_shape::BUILT_IN_SET_ID;
1487use crate::object_shape::BUILT_IN_USE_OPERATOR_ID;
1488use crate::object_shape::BUILT_IN_USE_REF_ID;
1489
1490/// Returns true if the type (looked up via identifier) is primitive.
1491pub fn is_primitive_type(ty: &Type) -> bool {
1492    matches!(ty, Type::Primitive)
1493}
1494
1495/// Returns true if the type is the props object.
1496pub fn is_props_type(ty: &Type) -> bool {
1497    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_PROPS_ID)
1498}
1499
1500/// Returns true if the type is an array.
1501pub fn is_array_type(ty: &Type) -> bool {
1502    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_ARRAY_ID)
1503}
1504
1505/// Returns true if the type is a Set.
1506pub fn is_set_type(ty: &Type) -> bool {
1507    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_SET_ID)
1508}
1509
1510/// Returns true if the type is a Map.
1511pub fn is_map_type(ty: &Type) -> bool {
1512    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_MAP_ID)
1513}
1514
1515/// Returns true if the type is JSX.
1516pub fn is_jsx_type(ty: &Type) -> bool {
1517    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_JSX_ID)
1518}
1519
1520/// Returns true if the identifier type is a ref value.
1521pub fn is_ref_value_type(ty: &Type) -> bool {
1522    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_REF_VALUE_ID)
1523}
1524
1525/// Returns true if the identifier type is useRef.
1526pub fn is_use_ref_type(ty: &Type) -> bool {
1527    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_USE_REF_ID)
1528}
1529
1530/// Returns true if the type is a ref or ref value.
1531pub fn is_ref_or_ref_value(ty: &Type) -> bool {
1532    is_use_ref_type(ty) || is_ref_value_type(ty)
1533}
1534
1535/// Returns true if the type is a useState result (BuiltInUseState).
1536pub fn is_use_state_type(ty: &Type) -> bool {
1537    matches!(ty, Type::Object { shape_id: Some(id) } if id == object_shape::BUILT_IN_USE_STATE_ID)
1538}
1539
1540/// Returns true if the type is a setState function (BuiltInSetState).
1541pub fn is_set_state_type(ty: &Type) -> bool {
1542    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_SET_STATE_ID)
1543}
1544
1545/// Returns true if the type is a useEffect hook.
1546pub fn is_use_effect_hook_type(ty: &Type) -> bool {
1547    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_USE_EFFECT_HOOK_ID)
1548}
1549
1550/// Returns true if the type is a useLayoutEffect hook.
1551pub fn is_use_layout_effect_hook_type(ty: &Type) -> bool {
1552    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_USE_LAYOUT_EFFECT_HOOK_ID)
1553}
1554
1555/// Returns true if the type is a useInsertionEffect hook.
1556pub fn is_use_insertion_effect_hook_type(ty: &Type) -> bool {
1557    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_USE_INSERTION_EFFECT_HOOK_ID)
1558}
1559
1560/// Returns true if the type is a useEffectEvent function.
1561pub fn is_use_effect_event_type(ty: &Type) -> bool {
1562    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_USE_EFFECT_EVENT_ID)
1563}
1564
1565/// Returns true if the type is a ref or ref-like mutable type (e.g. Reanimated shared values).
1566pub fn is_ref_or_ref_like_mutable_type(ty: &Type) -> bool {
1567    matches!(ty, Type::Object { shape_id: Some(id) }
1568        if id == object_shape::BUILT_IN_USE_REF_ID || id == object_shape::REANIMATED_SHARED_VALUE_ID)
1569}
1570
1571/// Returns true if the type is the `use()` operator (React.use).
1572pub fn is_use_operator_type(ty: &Type) -> bool {
1573    matches!(
1574        ty,
1575        Type::Function { shape_id: Some(id), .. }
1576            if id == BUILT_IN_USE_OPERATOR_ID
1577    )
1578}
1579
1580/// Returns true if the type is a plain object (BuiltInObject).
1581pub fn is_plain_object_type(ty: &Type) -> bool {
1582    matches!(ty, Type::Object { shape_id: Some(id) } if id == object_shape::BUILT_IN_OBJECT_ID)
1583}
1584
1585/// Returns true if the type is a startTransition function (BuiltInStartTransition).
1586pub fn is_start_transition_type(ty: &Type) -> bool {
1587    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_START_TRANSITION_ID)
1588}
1589
1590#[cfg(test)]
1591mod tests {
1592    use super::*;
1593
1594    #[test]
1595    fn test_format_js_number() {
1596        // Scientific notation for large numbers (>= 1e21)
1597        assert_eq!(format_js_number(1e21), "1e+21");
1598        assert_eq!(format_js_number(1.5e21), "1.5e+21");
1599        assert_eq!(
1600            format_js_number(2.18739127891275e22),
1601            "2.18739127891275e+22"
1602        );
1603        assert_eq!(format_js_number(1e100), "1e+100");
1604        assert_eq!(format_js_number(-1e21), "-1e+21");
1605        assert_eq!(format_js_number(-1e100), "-1e+100");
1606
1607        // Scientific notation for small numbers (< 1e-6)
1608        assert_eq!(format_js_number(1e-7), "1e-7");
1609        assert_eq!(format_js_number(5e-7), "5e-7");
1610        assert_eq!(format_js_number(1.5e-8), "1.5e-8");
1611        assert_eq!(format_js_number(-1.5e-8), "-1.5e-8");
1612
1613        // Non-scientific large numbers (< 1e21)
1614        assert_eq!(format_js_number(1e20), "100000000000000000000");
1615        assert_eq!(format_js_number(1e-6), "0.000001");
1616
1617        // Integers
1618        assert_eq!(format_js_number(0.0), "0");
1619        assert_eq!(format_js_number(-0.0), "0");
1620        assert_eq!(format_js_number(1.0), "1");
1621        assert_eq!(format_js_number(100.0), "100");
1622
1623        // Regular floats
1624        assert_eq!(format_js_number(1.5), "1.5");
1625        assert_eq!(format_js_number(0.5), "0.5");
1626        assert_eq!(format_js_number(0.1), "0.1");
1627
1628        // Special values
1629        assert_eq!(format_js_number(f64::NAN), "NaN");
1630        assert_eq!(format_js_number(f64::INFINITY), "Infinity");
1631        assert_eq!(format_js_number(f64::NEG_INFINITY), "-Infinity");
1632    }
1633}