main
rs 1,633 lines 46.6 KB
Raw
1 pub mod default_module_type_provider;
2 pub mod dominator;
3 pub mod environment;
4 pub mod environment_config;
5 pub mod globals;
6 pub mod object_shape;
7 pub mod print;
8 pub mod reactive;
9 pub mod type_config;
10 pub mod visitors;
11
12 use indexmap::{IndexMap, IndexSet};
13 pub use react_compiler_diagnostics::CompilerDiagnostic;
14 pub use react_compiler_diagnostics::ErrorCategory;
15 pub use react_compiler_diagnostics::GENERATED_SOURCE;
16 pub use react_compiler_diagnostics::Position;
17 pub use react_compiler_diagnostics::SourceLocation;
18 pub use reactive::*;
19 use rustc_hash::FxBuildHasher;
20
21 // =============================================================================
22 // ID newtypes
23 // =============================================================================
24
25 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
26 pub struct BlockId(pub u32);
27
28 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
29 pub struct IdentifierId(pub u32);
30
31 /// Index into the flat instruction table on HirFunction.
32 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
33 pub 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)]
38 pub struct EvaluationOrder(pub u32);
39
40 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
41 pub struct DeclarationId(pub u32);
42
43 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
44 pub struct ScopeId(pub u32);
45
46 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
47 pub struct TypeId(pub u32);
48
49 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
50 pub struct FunctionId(pub u32);
51
52 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53 pub 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)]
62 pub struct FloatValue(u64);
63
64 impl 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
74 impl From<f64> for FloatValue {
75 fn from(value: f64) -> Self {
76 FloatValue::new(value)
77 }
78 }
79
80 impl From<FloatValue> for f64 {
81 fn from(value: FloatValue) -> Self {
82 value.value()
83 }
84 }
85
86 impl PartialEq for FloatValue {
87 fn eq(&self, other: &Self) -> bool {
88 self.0 == other.0
89 }
90 }
91
92 impl Eq for FloatValue {}
93
94 impl std::hash::Hash for FloatValue {
95 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
96 self.0.hash(state);
97 }
98 }
99
100 impl 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"
113 pub 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)]
159 pub 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)]
177 pub enum ReactFunctionType {
178 Component,
179 Hook,
180 Other,
181 }
182
183 #[derive(Debug, Clone)]
184 pub enum ParamPattern {
185 Place(Place),
186 Spread(SpreadPattern),
187 }
188
189 /// The HIR control-flow graph
190 #[derive(Debug, Clone)]
191 pub struct HIR {
192 pub entry: BlockId,
193 pub blocks: IndexMap<BlockId, BasicBlock, FxBuildHasher>,
194 }
195
196 /// Block kinds
197 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
198 pub enum BlockKind {
199 Block,
200 Value,
201 Loop,
202 Sequence,
203 Catch,
204 }
205
206 impl 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)]
220 pub struct BasicBlock {
221 pub kind: BlockKind,
222 pub id: BlockId,
223 pub instructions: Vec<InstructionId>,
224 pub terminal: Terminal,
225 pub preds: IndexSet<BlockId, FxBuildHasher>,
226 pub phis: Vec<Phi>,
227 }
228
229 /// Phi node for SSA
230 #[derive(Debug, Clone)]
231 pub struct Phi {
232 pub place: Place,
233 pub operands: IndexMap<BlockId, Place, FxBuildHasher>,
234 }
235
236 // =============================================================================
237 // Terminal enum
238 // =============================================================================
239
240 #[derive(Debug, Clone)]
241 pub 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
392 impl 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)]
479 pub enum ReturnVariant {
480 Void,
481 Implicit,
482 Explicit,
483 }
484
485 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
486 pub enum GotoVariant {
487 Break,
488 Continue,
489 Try,
490 }
491
492 #[derive(Debug, Clone)]
493 pub struct Case {
494 pub test: Option<Place>,
495 pub block: BlockId,
496 }
497
498 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
499 pub enum LogicalOperator {
500 And,
501 Or,
502 NullishCoalescing,
503 }
504
505 impl 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)]
520 pub 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)]
529 pub enum InstructionKind {
530 Const,
531 Let,
532 Reassign,
533 Catch,
534 HoistedConst,
535 HoistedLet,
536 HoistedFunction,
537 Function,
538 }
539
540 #[derive(Debug, Clone)]
541 pub struct LValue {
542 pub place: Place,
543 pub kind: InstructionKind,
544 }
545
546 #[derive(Debug, Clone)]
547 pub struct LValuePattern {
548 pub pattern: Pattern,
549 pub kind: InstructionKind,
550 }
551
552 #[derive(Debug, Clone)]
553 pub enum Pattern {
554 Array(ArrayPattern),
555 Object(ObjectPattern),
556 }
557
558 // =============================================================================
559 // InstructionValue enum
560 // =============================================================================
561
562 #[derive(Debug, Clone)]
563 pub 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
788 impl 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)]
843 pub 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)]
852 pub 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
877 impl 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)]
907 pub enum UnaryOperator {
908 Minus,
909 Plus,
910 Not,
911 BitwiseNot,
912 TypeOf,
913 Void,
914 }
915
916 impl 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)]
930 pub enum UpdateOperator {
931 Increment,
932 Decrement,
933 }
934
935 impl 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)]
945 pub enum FunctionExpressionType {
946 ArrowFunctionExpression,
947 FunctionExpression,
948 FunctionDeclaration,
949 }
950
951 #[derive(Debug, Clone)]
952 pub struct TemplateQuasi {
953 pub raw: String,
954 pub cooked: Option<String>,
955 }
956
957 #[derive(Debug, Clone)]
958 pub struct ManualMemoDependency {
959 pub root: ManualMemoDependencyRoot,
960 pub path: Vec<DependencyPathEntry>,
961 pub loc: Option<SourceLocation>,
962 }
963
964 #[derive(Debug, Clone)]
965 pub enum ManualMemoDependencyRoot {
966 NamedLocal { value: Place, constant: bool },
967 Global { identifier_name: String },
968 }
969
970 #[derive(Debug, Clone, PartialEq, Eq)]
971 pub 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)]
982 pub 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)]
990 pub 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)]
1001 pub 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
1010 impl 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)]
1026 pub enum IdentifierName {
1027 Named(String),
1028 Promoted(String),
1029 }
1030
1031 impl 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, serde::Deserialize)]
1040 pub 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
1059 impl 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
1075 impl 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)]
1091 pub struct SpreadPattern {
1092 pub place: Place,
1093 }
1094
1095 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1096 pub enum Hole {
1097 Hole,
1098 }
1099
1100 #[derive(Debug, Clone)]
1101 pub struct ArrayPattern {
1102 pub items: Vec<ArrayPatternElement>,
1103 pub loc: Option<SourceLocation>,
1104 }
1105
1106 #[derive(Debug, Clone)]
1107 pub enum ArrayPatternElement {
1108 Place(Place),
1109 Spread(SpreadPattern),
1110 Hole,
1111 }
1112
1113 #[derive(Debug, Clone)]
1114 pub struct ObjectPattern {
1115 pub properties: Vec<ObjectPropertyOrSpread>,
1116 pub loc: Option<SourceLocation>,
1117 }
1118
1119 #[derive(Debug, Clone)]
1120 pub enum ObjectPropertyOrSpread {
1121 Property(ObjectProperty),
1122 Spread(SpreadPattern),
1123 }
1124
1125 #[derive(Debug, Clone)]
1126 pub struct ObjectProperty {
1127 pub key: ObjectPropertyKey,
1128 pub property_type: ObjectPropertyType,
1129 pub place: Place,
1130 }
1131
1132 #[derive(Debug, Clone)]
1133 pub 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)]
1141 pub enum ObjectPropertyType {
1142 Property,
1143 Method,
1144 }
1145
1146 impl 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)]
1156 pub enum PropertyLiteral {
1157 String(String),
1158 Number(FloatValue),
1159 }
1160
1161 impl 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)]
1171 pub enum PlaceOrSpread {
1172 Place(Place),
1173 Spread(SpreadPattern),
1174 }
1175
1176 #[derive(Debug, Clone)]
1177 pub enum ArrayElement {
1178 Place(Place),
1179 Spread(SpreadPattern),
1180 Hole,
1181 }
1182
1183 #[derive(Debug, Clone)]
1184 pub struct LoweredFunction {
1185 pub func: FunctionId,
1186 }
1187
1188 #[derive(Debug, Clone)]
1189 pub struct BuiltinTag {
1190 pub name: String,
1191 pub loc: Option<SourceLocation>,
1192 }
1193
1194 #[derive(Debug, Clone)]
1195 pub enum JsxTag {
1196 Place(Place),
1197 Builtin(BuiltinTag),
1198 }
1199
1200 #[derive(Debug, Clone)]
1201 pub 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)]
1211 pub enum BindingKind {
1212 Var,
1213 Let,
1214 Const,
1215 Param,
1216 Module,
1217 Hoisted,
1218 Local,
1219 Unknown,
1220 }
1221
1222 #[derive(Debug, Clone)]
1223 pub 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)]
1250 pub 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
1272 impl 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)]
1290 pub 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)]
1316 pub enum PropertyNameKind {
1317 Literal { value: PropertyLiteral },
1318 Computed { value: Box<Type> },
1319 }
1320
1321 // =============================================================================
1322 // ReactiveScope
1323 // =============================================================================
1324
1325 #[derive(Debug, Clone)]
1326 pub 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)]
1351 pub 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)]
1360 pub 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)]
1367 pub 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
1377 use crate::object_shape::FunctionSignature;
1378 use crate::type_config::ValueKind;
1379 use 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)]
1383 pub 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)]
1390 pub 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)]
1459 pub 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)]
1468 pub 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
1481 use crate::object_shape::BUILT_IN_ARRAY_ID;
1482 use crate::object_shape::BUILT_IN_JSX_ID;
1483 use crate::object_shape::BUILT_IN_MAP_ID;
1484 use crate::object_shape::BUILT_IN_PROPS_ID;
1485 use crate::object_shape::BUILT_IN_REF_VALUE_ID;
1486 use crate::object_shape::BUILT_IN_SET_ID;
1487 use crate::object_shape::BUILT_IN_USE_OPERATOR_ID;
1488 use crate::object_shape::BUILT_IN_USE_REF_ID;
1489
1490 /// Returns true if the type (looked up via identifier) is primitive.
1491 pub fn is_primitive_type(ty: &Type) -> bool {
1492 matches!(ty, Type::Primitive)
1493 }
1494
1495 /// Returns true if the type is the props object.
1496 pub 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.
1501 pub 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.
1506 pub 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.
1511 pub 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.
1516 pub 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.
1521 pub 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.
1526 pub 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.
1531 pub 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).
1536 pub 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).
1541 pub 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.
1546 pub 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.
1551 pub 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.
1556 pub 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.
1561 pub 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).
1566 pub 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).
1572 pub 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).
1581 pub 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).
1586 pub 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)]
1591 mod 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 }