Skip to main content

react_compiler_hir/
globals.rs

1// Copyright (c) Meta Platforms, Inc. and affiliates.
2//
3// This source code is licensed under the MIT license found in the
4// LICENSE file in the root directory of this source tree.
5
6//! Global type registry and built-in shape definitions, ported from Globals.ts.
7//!
8//! Provides `DEFAULT_SHAPES` (built-in object shapes) and `DEFAULT_GLOBALS`
9//! (global variable types including React hooks and JS built-ins).
10
11use std::collections::HashMap;
12use std::sync::LazyLock;
13
14use crate::Effect;
15use crate::Type;
16use crate::object_shape::*;
17use crate::type_config::AliasingEffectConfig;
18use crate::type_config::AliasingSignatureConfig;
19use crate::type_config::ApplyArgConfig;
20use crate::type_config::ApplyArgHoleKind;
21use crate::type_config::BuiltInTypeRef;
22use crate::type_config::TypeConfig;
23use crate::type_config::TypeReferenceConfig;
24use crate::type_config::ValueKind;
25use crate::type_config::ValueReason;
26
27/// Type alias matching TS `Global = BuiltInType | PolyType`.
28/// In the Rust port, both map to our `Type` enum.
29pub type Global = Type;
30
31/// Registry mapping global names to their types.
32///
33/// Supports two modes:
34/// - **Builder mode** (`base=None`): wraps a single HashMap, used during
35///   `build_default_globals` to construct the static base.
36/// - **Overlay mode** (`base=Some`): holds a `&'static HashMap` base plus a small
37///   extras HashMap. Lookups check extras first, then base. Inserts go into extras.
38///   Cloning only copies the extras map (the base pointer is shared).
39pub struct GlobalRegistry {
40    base: Option<&'static HashMap<String, Global>>,
41    entries: HashMap<String, Global>,
42}
43
44impl GlobalRegistry {
45    /// Create an empty builder-mode registry.
46    pub fn new() -> Self {
47        Self {
48            base: None,
49            entries: HashMap::new(),
50        }
51    }
52
53    /// Create an overlay-mode registry backed by a static base.
54    pub fn with_base(base: &'static HashMap<String, Global>) -> Self {
55        Self {
56            base: Some(base),
57            entries: HashMap::new(),
58        }
59    }
60
61    pub fn get(&self, key: &str) -> Option<&Global> {
62        self.entries
63            .get(key)
64            .or_else(|| self.base.and_then(|b| b.get(key)))
65    }
66
67    pub fn insert(&mut self, key: String, value: Global) {
68        self.entries.insert(key, value);
69    }
70
71    pub fn contains_key(&self, key: &str) -> bool {
72        self.entries.contains_key(key) || self.base.map_or(false, |b| b.contains_key(key))
73    }
74
75    /// Iterate over all keys in the registry (base + extras).
76    /// Keys in extras that shadow base keys appear only once.
77    pub fn keys(&self) -> impl Iterator<Item = &String> {
78        let base_keys = self
79            .base
80            .into_iter()
81            .flat_map(|b| b.keys())
82            .filter(|k| !self.entries.contains_key(k.as_str()));
83        self.entries.keys().chain(base_keys)
84    }
85
86    /// Consume the registry and return the inner HashMap.
87    /// Only valid in builder mode (no base).
88    pub fn into_inner(self) -> HashMap<String, Global> {
89        debug_assert!(
90            self.base.is_none(),
91            "into_inner() called on overlay-mode GlobalRegistry"
92        );
93        self.entries
94    }
95}
96
97impl Clone for GlobalRegistry {
98    fn clone(&self) -> Self {
99        Self {
100            base: self.base,
101            entries: self.entries.clone(),
102        }
103    }
104}
105
106// =============================================================================
107// Static base registries (initialized once, shared across all Environments)
108// =============================================================================
109
110struct BaseRegistries {
111    shapes: HashMap<String, ObjectShape>,
112    globals: HashMap<String, Global>,
113}
114
115static BASE: LazyLock<BaseRegistries> = LazyLock::new(|| {
116    let mut shapes = build_builtin_shapes();
117    let globals = build_default_globals(&mut shapes);
118    BaseRegistries {
119        shapes: shapes.into_inner(),
120        globals: globals.into_inner(),
121    }
122});
123
124/// Get a reference to the static base shapes registry.
125pub fn base_shapes() -> &'static HashMap<String, ObjectShape> {
126    &BASE.shapes
127}
128
129/// Get a reference to the static base globals registry.
130pub fn base_globals() -> &'static HashMap<String, Global> {
131    &BASE.globals
132}
133
134// =============================================================================
135// installTypeConfig — converts TypeConfig to internal Type
136// =============================================================================
137
138/// Convert a user-provided TypeConfig into an internal Type, registering shapes
139/// as needed. Ported from TS `installTypeConfig` in Globals.ts.
140/// If `errors` is provided, hook-name vs hook-type consistency validation
141/// errors are collected there.
142pub fn install_type_config(
143    _globals: &mut GlobalRegistry,
144    shapes: &mut ShapeRegistry,
145    type_config: &TypeConfig,
146    module_name: &str,
147    _loc: (),
148) -> Global {
149    install_type_config_inner(_globals, shapes, type_config, module_name, _loc, &mut None)
150}
151
152/// Like `install_type_config` but collects validation errors.
153pub fn install_type_config_with_errors(
154    _globals: &mut GlobalRegistry,
155    shapes: &mut ShapeRegistry,
156    type_config: &TypeConfig,
157    module_name: &str,
158    _loc: (),
159    errors: &mut Vec<String>,
160) -> Global {
161    install_type_config_inner(
162        _globals,
163        shapes,
164        type_config,
165        module_name,
166        _loc,
167        &mut Some(errors),
168    )
169}
170
171fn install_type_config_inner(
172    _globals: &mut GlobalRegistry,
173    shapes: &mut ShapeRegistry,
174    type_config: &TypeConfig,
175    module_name: &str,
176    _loc: (),
177    errors: &mut Option<&mut Vec<String>>,
178) -> Global {
179    match type_config {
180        TypeConfig::TypeReference(TypeReferenceConfig { name }) => match name {
181            BuiltInTypeRef::Array => Type::Object {
182                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
183            },
184            BuiltInTypeRef::MixedReadonly => Type::Object {
185                shape_id: Some(BUILT_IN_MIXED_READONLY_ID.to_string()),
186            },
187            BuiltInTypeRef::Primitive => Type::Primitive,
188            BuiltInTypeRef::Ref => Type::Object {
189                shape_id: Some(BUILT_IN_USE_REF_ID.to_string()),
190            },
191            BuiltInTypeRef::Any => Type::Poly,
192        },
193        TypeConfig::Function(func_config) => {
194            // Compute return type first to avoid double-borrow of shapes
195            let return_type = install_type_config_inner(
196                _globals,
197                shapes,
198                &func_config.return_type,
199                module_name,
200                (),
201                errors,
202            );
203            add_function(
204                shapes,
205                Vec::new(),
206                FunctionSignatureBuilder {
207                    positional_params: func_config.positional_params.clone(),
208                    rest_param: func_config.rest_param,
209                    callee_effect: func_config.callee_effect,
210                    return_type,
211                    return_value_kind: func_config.return_value_kind,
212                    no_alias: func_config.no_alias.unwrap_or(false),
213                    mutable_only_if_operands_are_mutable: func_config
214                        .mutable_only_if_operands_are_mutable
215                        .unwrap_or(false),
216                    impure: func_config.impure.unwrap_or(false),
217                    canonical_name: func_config.canonical_name.clone(),
218                    aliasing: func_config.aliasing.clone(),
219                    known_incompatible: func_config.known_incompatible.clone(),
220                    ..Default::default()
221                },
222                None,
223                false,
224            )
225        }
226        TypeConfig::Hook(hook_config) => {
227            // Compute return type first to avoid double-borrow of shapes
228            let return_type = install_type_config_inner(
229                _globals,
230                shapes,
231                &hook_config.return_type,
232                module_name,
233                (),
234                errors,
235            );
236            add_hook(
237                shapes,
238                HookSignatureBuilder {
239                    hook_kind: HookKind::Custom,
240                    positional_params: hook_config.positional_params.clone().unwrap_or_default(),
241                    rest_param: hook_config.rest_param.or(Some(Effect::Freeze)),
242                    callee_effect: Effect::Read,
243                    return_type,
244                    return_value_kind: hook_config.return_value_kind.unwrap_or(ValueKind::Frozen),
245                    no_alias: hook_config.no_alias.unwrap_or(false),
246                    aliasing: hook_config.aliasing.clone(),
247                    known_incompatible: hook_config.known_incompatible.clone(),
248                    ..Default::default()
249                },
250                None,
251            )
252        }
253        TypeConfig::Object(obj_config) => {
254            let properties: Vec<(String, Type)> = obj_config
255                .properties
256                .as_ref()
257                .map(|props| {
258                    props
259                        .iter()
260                        .map(|(key, value)| {
261                            let ty = install_type_config_inner(
262                                _globals,
263                                shapes,
264                                value,
265                                module_name,
266                                (),
267                                errors,
268                            );
269                            // Validate hook-name vs hook-type consistency (matching TS installTypeConfig)
270                            if let Some(errs) = errors {
271                                let expect_hook = crate::environment::is_hook_name(key);
272                                let is_hook = match &ty {
273                                    Type::Function { shape_id: Some(id), .. } => {
274                                        shapes.get(id)
275                                            .and_then(|shape| shape.function_type.as_ref())
276                                            .and_then(|ft| ft.hook_kind.as_ref())
277                                            .is_some()
278                                    }
279                                    _ => false,
280                                };
281                                if expect_hook != is_hook {
282                                    errs.push(format!(
283                                        "Expected type for object property '{}' from module '{}' {} based on the property name",
284                                        key,
285                                        module_name,
286                                        if expect_hook { "to be a hook" } else { "not to be a hook" }
287                                    ));
288                                }
289                            }
290                            (key.clone(), ty)
291                        })
292                        .collect()
293                })
294                .unwrap_or_default();
295            add_object(shapes, None, properties)
296        }
297    }
298}
299
300// =============================================================================
301// Build built-in shapes (BUILTIN_SHAPES from ObjectShape.ts)
302// =============================================================================
303
304/// Build the built-in shapes registry. This corresponds to TS `BUILTIN_SHAPES`
305/// defined at module level in ObjectShape.ts.
306pub fn build_builtin_shapes() -> ShapeRegistry {
307    let mut shapes = ShapeRegistry::new();
308
309    // BuiltInProps: { ref: UseRefType }
310    add_object(
311        &mut shapes,
312        Some(BUILT_IN_PROPS_ID),
313        vec![(
314            "ref".to_string(),
315            Type::Object {
316                shape_id: Some(BUILT_IN_USE_REF_ID.to_string()),
317            },
318        )],
319    );
320
321    build_array_shape(&mut shapes);
322    build_set_shape(&mut shapes);
323    build_map_shape(&mut shapes);
324    build_weak_set_shape(&mut shapes);
325    build_weak_map_shape(&mut shapes);
326    build_object_shape(&mut shapes);
327    build_ref_shapes(&mut shapes);
328    build_state_shapes(&mut shapes);
329    build_hook_shapes(&mut shapes);
330    build_misc_shapes(&mut shapes);
331
332    shapes
333}
334
335fn simple_function(
336    shapes: &mut ShapeRegistry,
337    positional_params: Vec<Effect>,
338    rest_param: Option<Effect>,
339    return_type: Type,
340    return_value_kind: ValueKind,
341) -> Type {
342    add_function(
343        shapes,
344        Vec::new(),
345        FunctionSignatureBuilder {
346            positional_params,
347            rest_param,
348            return_type,
349            return_value_kind,
350            ..Default::default()
351        },
352        None,
353        false,
354    )
355}
356
357/// Shorthand for a pure function returning Primitive.
358fn pure_primitive_fn(shapes: &mut ShapeRegistry) -> Type {
359    simple_function(
360        shapes,
361        Vec::new(),
362        Some(Effect::Read),
363        Type::Primitive,
364        ValueKind::Primitive,
365    )
366}
367
368fn build_array_shape(shapes: &mut ShapeRegistry) {
369    let index_of = pure_primitive_fn(shapes);
370    let includes = pure_primitive_fn(shapes);
371    let pop = add_function(
372        shapes,
373        Vec::new(),
374        FunctionSignatureBuilder {
375            callee_effect: Effect::Store,
376            return_type: Type::Poly,
377            return_value_kind: ValueKind::Mutable,
378            ..Default::default()
379        },
380        None,
381        false,
382    );
383    let at = add_function(
384        shapes,
385        Vec::new(),
386        FunctionSignatureBuilder {
387            positional_params: vec![Effect::Read],
388            callee_effect: Effect::Capture,
389            return_type: Type::Poly,
390            return_value_kind: ValueKind::Mutable,
391            ..Default::default()
392        },
393        None,
394        false,
395    );
396    let concat = add_function(
397        shapes,
398        Vec::new(),
399        FunctionSignatureBuilder {
400            rest_param: Some(Effect::Capture),
401            return_type: Type::Object {
402                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
403            },
404            return_value_kind: ValueKind::Mutable,
405            callee_effect: Effect::Capture,
406            ..Default::default()
407        },
408        None,
409        false,
410    );
411    let join = pure_primitive_fn(shapes);
412    let slice = add_function(
413        shapes,
414        Vec::new(),
415        FunctionSignatureBuilder {
416            rest_param: Some(Effect::Read),
417            callee_effect: Effect::Capture,
418            return_type: Type::Object {
419                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
420            },
421            return_value_kind: ValueKind::Mutable,
422            ..Default::default()
423        },
424        None,
425        false,
426    );
427    let map = add_function(
428        shapes,
429        Vec::new(),
430        FunctionSignatureBuilder {
431            rest_param: Some(Effect::ConditionallyMutate),
432            callee_effect: Effect::ConditionallyMutate,
433            return_type: Type::Object {
434                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
435            },
436            return_value_kind: ValueKind::Mutable,
437            no_alias: true,
438            mutable_only_if_operands_are_mutable: true,
439            aliasing: Some(AliasingSignatureConfig {
440                receiver: "@receiver".to_string(),
441                params: vec!["@callback".to_string()],
442                rest: None,
443                returns: "@returns".to_string(),
444                temporaries: vec![
445                    "@item".to_string(),
446                    "@callbackReturn".to_string(),
447                    "@thisArg".to_string(),
448                ],
449                effects: vec![
450                    // Map creates a new mutable array
451                    AliasingEffectConfig::Create {
452                        into: "@returns".to_string(),
453                        value: ValueKind::Mutable,
454                        reason: ValueReason::KnownReturnSignature,
455                    },
456                    // The first arg to the callback is an item extracted from the receiver array
457                    AliasingEffectConfig::CreateFrom {
458                        from: "@receiver".to_string(),
459                        into: "@item".to_string(),
460                    },
461                    // The undefined this for the callback
462                    AliasingEffectConfig::Create {
463                        into: "@thisArg".to_string(),
464                        value: ValueKind::Primitive,
465                        reason: ValueReason::KnownReturnSignature,
466                    },
467                    // Calls the callback, returning the result into a temporary
468                    AliasingEffectConfig::Apply {
469                        receiver: "@thisArg".to_string(),
470                        function: "@callback".to_string(),
471                        mutates_function: false,
472                        args: vec![
473                            ApplyArgConfig::Place("@item".to_string()),
474                            ApplyArgConfig::Hole {
475                                kind: ApplyArgHoleKind::Hole,
476                            },
477                            ApplyArgConfig::Place("@receiver".to_string()),
478                        ],
479                        into: "@callbackReturn".to_string(),
480                    },
481                    // Captures the result of the callback into the return array
482                    AliasingEffectConfig::Capture {
483                        from: "@callbackReturn".to_string(),
484                        into: "@returns".to_string(),
485                    },
486                ],
487            }),
488            ..Default::default()
489        },
490        None,
491        false,
492    );
493    let filter = add_function(
494        shapes,
495        Vec::new(),
496        FunctionSignatureBuilder {
497            rest_param: Some(Effect::ConditionallyMutate),
498            callee_effect: Effect::ConditionallyMutate,
499            return_type: Type::Object {
500                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
501            },
502            return_value_kind: ValueKind::Mutable,
503            no_alias: true,
504            mutable_only_if_operands_are_mutable: true,
505            ..Default::default()
506        },
507        None,
508        false,
509    );
510    let find = add_function(
511        shapes,
512        Vec::new(),
513        FunctionSignatureBuilder {
514            rest_param: Some(Effect::ConditionallyMutate),
515            callee_effect: Effect::ConditionallyMutate,
516            return_type: Type::Poly,
517            return_value_kind: ValueKind::Mutable,
518            no_alias: true,
519            mutable_only_if_operands_are_mutable: true,
520            ..Default::default()
521        },
522        None,
523        false,
524    );
525    let find_index = add_function(
526        shapes,
527        Vec::new(),
528        FunctionSignatureBuilder {
529            rest_param: Some(Effect::ConditionallyMutate),
530            callee_effect: Effect::ConditionallyMutate,
531            return_type: Type::Primitive,
532            return_value_kind: ValueKind::Primitive,
533            no_alias: true,
534            mutable_only_if_operands_are_mutable: true,
535            ..Default::default()
536        },
537        None,
538        false,
539    );
540    let every = add_function(
541        shapes,
542        Vec::new(),
543        FunctionSignatureBuilder {
544            rest_param: Some(Effect::ConditionallyMutate),
545            callee_effect: Effect::ConditionallyMutate,
546            return_type: Type::Primitive,
547            return_value_kind: ValueKind::Primitive,
548            no_alias: true,
549            mutable_only_if_operands_are_mutable: true,
550            ..Default::default()
551        },
552        None,
553        false,
554    );
555    let some = add_function(
556        shapes,
557        Vec::new(),
558        FunctionSignatureBuilder {
559            rest_param: Some(Effect::ConditionallyMutate),
560            callee_effect: Effect::ConditionallyMutate,
561            return_type: Type::Primitive,
562            return_value_kind: ValueKind::Primitive,
563            no_alias: true,
564            mutable_only_if_operands_are_mutable: true,
565            ..Default::default()
566        },
567        None,
568        false,
569    );
570    let flat_map = add_function(
571        shapes,
572        Vec::new(),
573        FunctionSignatureBuilder {
574            rest_param: Some(Effect::ConditionallyMutate),
575            callee_effect: Effect::ConditionallyMutate,
576            return_type: Type::Object {
577                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
578            },
579            return_value_kind: ValueKind::Mutable,
580            no_alias: true,
581            mutable_only_if_operands_are_mutable: true,
582            ..Default::default()
583        },
584        None,
585        false,
586    );
587    let length = Type::Primitive;
588    let push = add_function(
589        shapes,
590        Vec::new(),
591        FunctionSignatureBuilder {
592            rest_param: Some(Effect::Capture),
593            callee_effect: Effect::Store,
594            return_type: Type::Primitive,
595            return_value_kind: ValueKind::Primitive,
596            aliasing: Some(AliasingSignatureConfig {
597                receiver: "@receiver".to_string(),
598                params: Vec::new(),
599                rest: Some("@rest".to_string()),
600                returns: "@returns".to_string(),
601                temporaries: Vec::new(),
602                effects: vec![
603                    // Push directly mutates the array itself
604                    AliasingEffectConfig::Mutate {
605                        value: "@receiver".to_string(),
606                    },
607                    // The arguments are captured into the array
608                    AliasingEffectConfig::Capture {
609                        from: "@rest".to_string(),
610                        into: "@receiver".to_string(),
611                    },
612                    // Returns the new length, a primitive
613                    AliasingEffectConfig::Create {
614                        into: "@returns".to_string(),
615                        value: ValueKind::Primitive,
616                        reason: ValueReason::KnownReturnSignature,
617                    },
618                ],
619            }),
620            ..Default::default()
621        },
622        None,
623        false,
624    );
625
626    add_object(
627        shapes,
628        Some(BUILT_IN_ARRAY_ID),
629        vec![
630            ("indexOf".to_string(), index_of),
631            ("includes".to_string(), includes),
632            ("pop".to_string(), pop),
633            ("at".to_string(), at),
634            ("concat".to_string(), concat),
635            ("length".to_string(), length),
636            ("push".to_string(), push),
637            ("slice".to_string(), slice),
638            ("map".to_string(), map),
639            ("flatMap".to_string(), flat_map),
640            ("filter".to_string(), filter),
641            ("every".to_string(), every),
642            ("some".to_string(), some),
643            ("find".to_string(), find),
644            ("findIndex".to_string(), find_index),
645            ("join".to_string(), join),
646            // TODO: rest of Array properties
647        ],
648    );
649}
650
651fn build_set_shape(shapes: &mut ShapeRegistry) {
652    let has = add_function(
653        shapes,
654        Vec::new(),
655        FunctionSignatureBuilder {
656            positional_params: vec![Effect::Read],
657            return_type: Type::Primitive,
658            return_value_kind: ValueKind::Primitive,
659            ..Default::default()
660        },
661        None,
662        false,
663    );
664    let add = add_function(
665        shapes,
666        Vec::new(),
667        FunctionSignatureBuilder {
668            positional_params: vec![Effect::Capture],
669            callee_effect: Effect::Store,
670            return_type: Type::Object {
671                shape_id: Some(BUILT_IN_SET_ID.to_string()),
672            },
673            return_value_kind: ValueKind::Mutable,
674            aliasing: Some(AliasingSignatureConfig {
675                receiver: "@receiver".to_string(),
676                params: Vec::new(),
677                rest: Some("@rest".to_string()),
678                returns: "@returns".to_string(),
679                temporaries: Vec::new(),
680                effects: vec![
681                    // Set.add returns the receiver Set
682                    AliasingEffectConfig::Assign {
683                        from: "@receiver".to_string(),
684                        into: "@returns".to_string(),
685                    },
686                    // Set.add mutates the set itself
687                    AliasingEffectConfig::Mutate {
688                        value: "@receiver".to_string(),
689                    },
690                    // Captures the rest params into the set
691                    AliasingEffectConfig::Capture {
692                        from: "@rest".to_string(),
693                        into: "@receiver".to_string(),
694                    },
695                ],
696            }),
697            ..Default::default()
698        },
699        None,
700        false,
701    );
702    let clear = add_function(
703        shapes,
704        Vec::new(),
705        FunctionSignatureBuilder {
706            callee_effect: Effect::Store,
707            return_type: Type::Primitive,
708            return_value_kind: ValueKind::Primitive,
709            ..Default::default()
710        },
711        None,
712        false,
713    );
714    let delete = add_function(
715        shapes,
716        Vec::new(),
717        FunctionSignatureBuilder {
718            positional_params: vec![Effect::Read],
719            callee_effect: Effect::Store,
720            return_type: Type::Primitive,
721            return_value_kind: ValueKind::Primitive,
722            ..Default::default()
723        },
724        None,
725        false,
726    );
727    let size = Type::Primitive;
728    let difference = add_function(
729        shapes,
730        Vec::new(),
731        FunctionSignatureBuilder {
732            positional_params: vec![Effect::Capture],
733            callee_effect: Effect::Capture,
734            return_type: Type::Object {
735                shape_id: Some(BUILT_IN_SET_ID.to_string()),
736            },
737            return_value_kind: ValueKind::Mutable,
738            ..Default::default()
739        },
740        None,
741        false,
742    );
743    let union = add_function(
744        shapes,
745        Vec::new(),
746        FunctionSignatureBuilder {
747            positional_params: vec![Effect::Capture],
748            callee_effect: Effect::Capture,
749            return_type: Type::Object {
750                shape_id: Some(BUILT_IN_SET_ID.to_string()),
751            },
752            return_value_kind: ValueKind::Mutable,
753            ..Default::default()
754        },
755        None,
756        false,
757    );
758    let symmetrical_difference = add_function(
759        shapes,
760        Vec::new(),
761        FunctionSignatureBuilder {
762            positional_params: vec![Effect::Capture],
763            callee_effect: Effect::Capture,
764            return_type: Type::Object {
765                shape_id: Some(BUILT_IN_SET_ID.to_string()),
766            },
767            return_value_kind: ValueKind::Mutable,
768            ..Default::default()
769        },
770        None,
771        false,
772    );
773    let is_subset_of = add_function(
774        shapes,
775        Vec::new(),
776        FunctionSignatureBuilder {
777            positional_params: vec![Effect::Read],
778            callee_effect: Effect::Read,
779            return_type: Type::Primitive,
780            return_value_kind: ValueKind::Primitive,
781            ..Default::default()
782        },
783        None,
784        false,
785    );
786    let is_superset_of = add_function(
787        shapes,
788        Vec::new(),
789        FunctionSignatureBuilder {
790            positional_params: vec![Effect::Read],
791            callee_effect: Effect::Read,
792            return_type: Type::Primitive,
793            return_value_kind: ValueKind::Primitive,
794            ..Default::default()
795        },
796        None,
797        false,
798    );
799    let for_each = add_function(
800        shapes,
801        Vec::new(),
802        FunctionSignatureBuilder {
803            rest_param: Some(Effect::ConditionallyMutate),
804            callee_effect: Effect::ConditionallyMutate,
805            return_type: Type::Primitive,
806            return_value_kind: ValueKind::Primitive,
807            no_alias: true,
808            mutable_only_if_operands_are_mutable: true,
809            ..Default::default()
810        },
811        None,
812        false,
813    );
814    let values = add_function(
815        shapes,
816        Vec::new(),
817        FunctionSignatureBuilder {
818            callee_effect: Effect::Capture,
819            return_type: Type::Poly,
820            return_value_kind: ValueKind::Mutable,
821            ..Default::default()
822        },
823        None,
824        false,
825    );
826    let keys = add_function(
827        shapes,
828        Vec::new(),
829        FunctionSignatureBuilder {
830            callee_effect: Effect::Capture,
831            return_type: Type::Poly,
832            return_value_kind: ValueKind::Mutable,
833            ..Default::default()
834        },
835        None,
836        false,
837    );
838    let entries = add_function(
839        shapes,
840        Vec::new(),
841        FunctionSignatureBuilder {
842            callee_effect: Effect::Capture,
843            return_type: Type::Poly,
844            return_value_kind: ValueKind::Mutable,
845            ..Default::default()
846        },
847        None,
848        false,
849    );
850
851    add_object(
852        shapes,
853        Some(BUILT_IN_SET_ID),
854        vec![
855            ("add".to_string(), add),
856            ("clear".to_string(), clear),
857            ("delete".to_string(), delete),
858            ("has".to_string(), has),
859            ("size".to_string(), size),
860            ("difference".to_string(), difference),
861            ("union".to_string(), union),
862            ("symmetricalDifference".to_string(), symmetrical_difference),
863            ("isSubsetOf".to_string(), is_subset_of),
864            ("isSupersetOf".to_string(), is_superset_of),
865            ("forEach".to_string(), for_each),
866            ("values".to_string(), values),
867            ("keys".to_string(), keys),
868            ("entries".to_string(), entries),
869        ],
870    );
871}
872
873fn build_map_shape(shapes: &mut ShapeRegistry) {
874    let has = add_function(
875        shapes,
876        Vec::new(),
877        FunctionSignatureBuilder {
878            positional_params: vec![Effect::Read],
879            return_type: Type::Primitive,
880            return_value_kind: ValueKind::Primitive,
881            ..Default::default()
882        },
883        None,
884        false,
885    );
886    let get = add_function(
887        shapes,
888        Vec::new(),
889        FunctionSignatureBuilder {
890            positional_params: vec![Effect::Read],
891            callee_effect: Effect::Capture,
892            return_type: Type::Poly,
893            return_value_kind: ValueKind::Mutable,
894            ..Default::default()
895        },
896        None,
897        false,
898    );
899    let clear = add_function(
900        shapes,
901        Vec::new(),
902        FunctionSignatureBuilder {
903            callee_effect: Effect::Store,
904            return_type: Type::Primitive,
905            return_value_kind: ValueKind::Primitive,
906            ..Default::default()
907        },
908        None,
909        false,
910    );
911    let set = add_function(
912        shapes,
913        Vec::new(),
914        FunctionSignatureBuilder {
915            positional_params: vec![Effect::Capture, Effect::Capture],
916            callee_effect: Effect::Store,
917            return_type: Type::Object {
918                shape_id: Some(BUILT_IN_MAP_ID.to_string()),
919            },
920            return_value_kind: ValueKind::Mutable,
921            ..Default::default()
922        },
923        None,
924        false,
925    );
926    let delete = add_function(
927        shapes,
928        Vec::new(),
929        FunctionSignatureBuilder {
930            positional_params: vec![Effect::Read],
931            callee_effect: Effect::Store,
932            return_type: Type::Primitive,
933            return_value_kind: ValueKind::Primitive,
934            ..Default::default()
935        },
936        None,
937        false,
938    );
939    let size = Type::Primitive;
940    let for_each = add_function(
941        shapes,
942        Vec::new(),
943        FunctionSignatureBuilder {
944            rest_param: Some(Effect::ConditionallyMutate),
945            callee_effect: Effect::ConditionallyMutate,
946            return_type: Type::Primitive,
947            return_value_kind: ValueKind::Primitive,
948            no_alias: true,
949            mutable_only_if_operands_are_mutable: true,
950            ..Default::default()
951        },
952        None,
953        false,
954    );
955    let values = add_function(
956        shapes,
957        Vec::new(),
958        FunctionSignatureBuilder {
959            callee_effect: Effect::Capture,
960            return_type: Type::Poly,
961            return_value_kind: ValueKind::Mutable,
962            ..Default::default()
963        },
964        None,
965        false,
966    );
967    let keys = add_function(
968        shapes,
969        Vec::new(),
970        FunctionSignatureBuilder {
971            callee_effect: Effect::Capture,
972            return_type: Type::Poly,
973            return_value_kind: ValueKind::Mutable,
974            ..Default::default()
975        },
976        None,
977        false,
978    );
979    let entries = add_function(
980        shapes,
981        Vec::new(),
982        FunctionSignatureBuilder {
983            callee_effect: Effect::Capture,
984            return_type: Type::Poly,
985            return_value_kind: ValueKind::Mutable,
986            ..Default::default()
987        },
988        None,
989        false,
990    );
991
992    add_object(
993        shapes,
994        Some(BUILT_IN_MAP_ID),
995        vec![
996            ("has".to_string(), has),
997            ("get".to_string(), get),
998            ("set".to_string(), set),
999            ("clear".to_string(), clear),
1000            ("delete".to_string(), delete),
1001            ("size".to_string(), size),
1002            ("forEach".to_string(), for_each),
1003            ("values".to_string(), values),
1004            ("keys".to_string(), keys),
1005            ("entries".to_string(), entries),
1006        ],
1007    );
1008}
1009
1010fn build_weak_set_shape(shapes: &mut ShapeRegistry) {
1011    let has = pure_primitive_fn(shapes);
1012    let add = add_function(
1013        shapes,
1014        Vec::new(),
1015        FunctionSignatureBuilder {
1016            positional_params: vec![Effect::Capture],
1017            callee_effect: Effect::Store,
1018            return_type: Type::Object {
1019                shape_id: Some(BUILT_IN_WEAK_SET_ID.to_string()),
1020            },
1021            return_value_kind: ValueKind::Mutable,
1022            ..Default::default()
1023        },
1024        None,
1025        false,
1026    );
1027    let delete = add_function(
1028        shapes,
1029        Vec::new(),
1030        FunctionSignatureBuilder {
1031            positional_params: vec![Effect::Read],
1032            callee_effect: Effect::Store,
1033            return_type: Type::Primitive,
1034            return_value_kind: ValueKind::Primitive,
1035            ..Default::default()
1036        },
1037        None,
1038        false,
1039    );
1040
1041    add_object(
1042        shapes,
1043        Some(BUILT_IN_WEAK_SET_ID),
1044        vec![
1045            ("has".to_string(), has),
1046            ("add".to_string(), add),
1047            ("delete".to_string(), delete),
1048        ],
1049    );
1050}
1051
1052fn build_weak_map_shape(shapes: &mut ShapeRegistry) {
1053    let has = pure_primitive_fn(shapes);
1054    let get = add_function(
1055        shapes,
1056        Vec::new(),
1057        FunctionSignatureBuilder {
1058            positional_params: vec![Effect::Read],
1059            callee_effect: Effect::Capture,
1060            return_type: Type::Poly,
1061            return_value_kind: ValueKind::Mutable,
1062            ..Default::default()
1063        },
1064        None,
1065        false,
1066    );
1067    let set = add_function(
1068        shapes,
1069        Vec::new(),
1070        FunctionSignatureBuilder {
1071            positional_params: vec![Effect::Capture, Effect::Capture],
1072            callee_effect: Effect::Store,
1073            return_type: Type::Object {
1074                shape_id: Some(BUILT_IN_WEAK_MAP_ID.to_string()),
1075            },
1076            return_value_kind: ValueKind::Mutable,
1077            ..Default::default()
1078        },
1079        None,
1080        false,
1081    );
1082    let delete = add_function(
1083        shapes,
1084        Vec::new(),
1085        FunctionSignatureBuilder {
1086            positional_params: vec![Effect::Read],
1087            callee_effect: Effect::Store,
1088            return_type: Type::Primitive,
1089            return_value_kind: ValueKind::Primitive,
1090            ..Default::default()
1091        },
1092        None,
1093        false,
1094    );
1095
1096    add_object(
1097        shapes,
1098        Some(BUILT_IN_WEAK_MAP_ID),
1099        vec![
1100            ("has".to_string(), has),
1101            ("get".to_string(), get),
1102            ("set".to_string(), set),
1103            ("delete".to_string(), delete),
1104        ],
1105    );
1106}
1107
1108fn build_object_shape(shapes: &mut ShapeRegistry) {
1109    // BuiltInObject: has toString() returning Primitive (matches TS BuiltInObjectId shape)
1110    let to_string = add_function(
1111        shapes,
1112        Vec::new(),
1113        FunctionSignatureBuilder {
1114            return_type: Type::Primitive,
1115            return_value_kind: ValueKind::Primitive,
1116            ..Default::default()
1117        },
1118        None,
1119        false,
1120    );
1121    add_object(
1122        shapes,
1123        Some(BUILT_IN_OBJECT_ID),
1124        vec![("toString".to_string(), to_string)],
1125    );
1126    // BuiltInFunction: empty shape
1127    add_object(shapes, Some(BUILT_IN_FUNCTION_ID), Vec::new());
1128    // BuiltInJsx: empty shape
1129    add_object(shapes, Some(BUILT_IN_JSX_ID), Vec::new());
1130    // BuiltInMixedReadonly: has explicit method types + wildcard returning MixedReadonly
1131    // (matches TS BuiltInMixedReadonlyId shape)
1132    let mixed_to_string = add_function(
1133        shapes,
1134        Vec::new(),
1135        FunctionSignatureBuilder {
1136            rest_param: Some(Effect::Read),
1137            return_type: Type::Primitive,
1138            return_value_kind: ValueKind::Primitive,
1139            ..Default::default()
1140        },
1141        None,
1142        false,
1143    );
1144    let mixed_index_of = add_function(
1145        shapes,
1146        Vec::new(),
1147        FunctionSignatureBuilder {
1148            rest_param: Some(Effect::Read),
1149            return_type: Type::Primitive,
1150            return_value_kind: ValueKind::Primitive,
1151            ..Default::default()
1152        },
1153        None,
1154        false,
1155    );
1156    let mixed_includes = add_function(
1157        shapes,
1158        Vec::new(),
1159        FunctionSignatureBuilder {
1160            rest_param: Some(Effect::Read),
1161            return_type: Type::Primitive,
1162            return_value_kind: ValueKind::Primitive,
1163            ..Default::default()
1164        },
1165        None,
1166        false,
1167    );
1168    let mixed_at = add_function(
1169        shapes,
1170        Vec::new(),
1171        FunctionSignatureBuilder {
1172            positional_params: vec![Effect::Read],
1173            return_type: Type::Object {
1174                shape_id: Some(BUILT_IN_MIXED_READONLY_ID.to_string()),
1175            },
1176            callee_effect: Effect::Capture,
1177            return_value_kind: ValueKind::Frozen,
1178            ..Default::default()
1179        },
1180        None,
1181        false,
1182    );
1183    let mixed_map = add_function(
1184        shapes,
1185        Vec::new(),
1186        FunctionSignatureBuilder {
1187            rest_param: Some(Effect::ConditionallyMutate),
1188            return_type: Type::Object {
1189                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
1190            },
1191            callee_effect: Effect::ConditionallyMutate,
1192            return_value_kind: ValueKind::Mutable,
1193            no_alias: true,
1194            ..Default::default()
1195        },
1196        None,
1197        false,
1198    );
1199    let mixed_flat_map = add_function(
1200        shapes,
1201        Vec::new(),
1202        FunctionSignatureBuilder {
1203            rest_param: Some(Effect::ConditionallyMutate),
1204            return_type: Type::Object {
1205                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
1206            },
1207            callee_effect: Effect::ConditionallyMutate,
1208            return_value_kind: ValueKind::Mutable,
1209            no_alias: true,
1210            ..Default::default()
1211        },
1212        None,
1213        false,
1214    );
1215    let mixed_filter = add_function(
1216        shapes,
1217        Vec::new(),
1218        FunctionSignatureBuilder {
1219            rest_param: Some(Effect::ConditionallyMutate),
1220            return_type: Type::Object {
1221                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
1222            },
1223            callee_effect: Effect::ConditionallyMutate,
1224            return_value_kind: ValueKind::Mutable,
1225            no_alias: true,
1226            ..Default::default()
1227        },
1228        None,
1229        false,
1230    );
1231    let mixed_concat = add_function(
1232        shapes,
1233        Vec::new(),
1234        FunctionSignatureBuilder {
1235            rest_param: Some(Effect::Capture),
1236            return_type: Type::Object {
1237                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
1238            },
1239            callee_effect: Effect::Capture,
1240            return_value_kind: ValueKind::Mutable,
1241            ..Default::default()
1242        },
1243        None,
1244        false,
1245    );
1246    let mixed_slice = add_function(
1247        shapes,
1248        Vec::new(),
1249        FunctionSignatureBuilder {
1250            rest_param: Some(Effect::Read),
1251            return_type: Type::Object {
1252                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
1253            },
1254            callee_effect: Effect::Capture,
1255            return_value_kind: ValueKind::Mutable,
1256            ..Default::default()
1257        },
1258        None,
1259        false,
1260    );
1261    let mixed_every = add_function(
1262        shapes,
1263        Vec::new(),
1264        FunctionSignatureBuilder {
1265            rest_param: Some(Effect::ConditionallyMutate),
1266            return_type: Type::Primitive,
1267            callee_effect: Effect::ConditionallyMutate,
1268            return_value_kind: ValueKind::Primitive,
1269            no_alias: true,
1270            mutable_only_if_operands_are_mutable: true,
1271            ..Default::default()
1272        },
1273        None,
1274        false,
1275    );
1276    let mixed_some = add_function(
1277        shapes,
1278        Vec::new(),
1279        FunctionSignatureBuilder {
1280            rest_param: Some(Effect::ConditionallyMutate),
1281            return_type: Type::Primitive,
1282            callee_effect: Effect::ConditionallyMutate,
1283            return_value_kind: ValueKind::Primitive,
1284            no_alias: true,
1285            mutable_only_if_operands_are_mutable: true,
1286            ..Default::default()
1287        },
1288        None,
1289        false,
1290    );
1291    let mixed_find = add_function(
1292        shapes,
1293        Vec::new(),
1294        FunctionSignatureBuilder {
1295            rest_param: Some(Effect::ConditionallyMutate),
1296            return_type: Type::Object {
1297                shape_id: Some(BUILT_IN_MIXED_READONLY_ID.to_string()),
1298            },
1299            callee_effect: Effect::ConditionallyMutate,
1300            return_value_kind: ValueKind::Frozen,
1301            no_alias: true,
1302            mutable_only_if_operands_are_mutable: true,
1303            ..Default::default()
1304        },
1305        None,
1306        false,
1307    );
1308    let mixed_find_index = add_function(
1309        shapes,
1310        Vec::new(),
1311        FunctionSignatureBuilder {
1312            rest_param: Some(Effect::ConditionallyMutate),
1313            return_type: Type::Primitive,
1314            callee_effect: Effect::ConditionallyMutate,
1315            return_value_kind: ValueKind::Primitive,
1316            no_alias: true,
1317            mutable_only_if_operands_are_mutable: true,
1318            ..Default::default()
1319        },
1320        None,
1321        false,
1322    );
1323    let mixed_join = add_function(
1324        shapes,
1325        Vec::new(),
1326        FunctionSignatureBuilder {
1327            rest_param: Some(Effect::Read),
1328            return_type: Type::Primitive,
1329            return_value_kind: ValueKind::Primitive,
1330            ..Default::default()
1331        },
1332        None,
1333        false,
1334    );
1335    let mut mixed_props = HashMap::new();
1336    mixed_props.insert("toString".to_string(), mixed_to_string);
1337    mixed_props.insert("indexOf".to_string(), mixed_index_of);
1338    mixed_props.insert("includes".to_string(), mixed_includes);
1339    mixed_props.insert("at".to_string(), mixed_at);
1340    mixed_props.insert("map".to_string(), mixed_map);
1341    mixed_props.insert("flatMap".to_string(), mixed_flat_map);
1342    mixed_props.insert("filter".to_string(), mixed_filter);
1343    mixed_props.insert("concat".to_string(), mixed_concat);
1344    mixed_props.insert("slice".to_string(), mixed_slice);
1345    mixed_props.insert("every".to_string(), mixed_every);
1346    mixed_props.insert("some".to_string(), mixed_some);
1347    mixed_props.insert("find".to_string(), mixed_find);
1348    mixed_props.insert("findIndex".to_string(), mixed_find_index);
1349    mixed_props.insert("join".to_string(), mixed_join);
1350    mixed_props.insert(
1351        "*".to_string(),
1352        Type::Object {
1353        shape_id: Some(BUILT_IN_MIXED_READONLY_ID.to_string()),
1354        },
1355    );
1356    shapes.insert(
1357        BUILT_IN_MIXED_READONLY_ID.to_string(),
1358        ObjectShape {
1359            properties: mixed_props,
1360            function_type: None,
1361        },
1362    );
1363}
1364
1365fn build_ref_shapes(shapes: &mut ShapeRegistry) {
1366    // BuiltInUseRefId: { current: Object { shapeId: BuiltInRefValue } }
1367    add_object(
1368        shapes,
1369        Some(BUILT_IN_USE_REF_ID),
1370        vec![(
1371            "current".to_string(),
1372            Type::Object {
1373            shape_id: Some(BUILT_IN_REF_VALUE_ID.to_string()),
1374            },
1375        )],
1376    );
1377    // BuiltInRefValue: { *: Object { shapeId: BuiltInRefValue } } (self-referencing)
1378    add_object(
1379        shapes,
1380        Some(BUILT_IN_REF_VALUE_ID),
1381        vec![(
1382            "*".to_string(),
1383            Type::Object {
1384            shape_id: Some(BUILT_IN_REF_VALUE_ID.to_string()),
1385            },
1386        )],
1387    );
1388}
1389
1390fn build_state_shapes(shapes: &mut ShapeRegistry) {
1391    // BuiltInSetState: function that freezes its argument
1392    let set_state = add_function(
1393        shapes,
1394        Vec::new(),
1395        FunctionSignatureBuilder {
1396            rest_param: Some(Effect::Freeze),
1397            return_type: Type::Primitive,
1398            return_value_kind: ValueKind::Primitive,
1399            ..Default::default()
1400        },
1401        Some(BUILT_IN_SET_STATE_ID),
1402        false,
1403    );
1404
1405    // BuiltInUseState: object with [0] = Poly (state), [1] = setState function
1406    add_object(
1407        shapes,
1408        Some(BUILT_IN_USE_STATE_ID),
1409        vec![("0".to_string(), Type::Poly), ("1".to_string(), set_state)],
1410    );
1411
1412    // BuiltInSetActionState
1413    let set_action_state = add_function(
1414        shapes,
1415        Vec::new(),
1416        FunctionSignatureBuilder {
1417            rest_param: Some(Effect::Freeze),
1418            return_type: Type::Primitive,
1419            return_value_kind: ValueKind::Primitive,
1420            ..Default::default()
1421        },
1422        Some(BUILT_IN_SET_ACTION_STATE_ID),
1423        false,
1424    );
1425
1426    // BuiltInUseActionState: [0] = Poly, [1] = setActionState function
1427    add_object(
1428        shapes,
1429        Some(BUILT_IN_USE_ACTION_STATE_ID),
1430        vec![
1431            ("0".to_string(), Type::Poly),
1432            ("1".to_string(), set_action_state),
1433        ],
1434    );
1435
1436    // BuiltInDispatch
1437    let dispatch = add_function(
1438        shapes,
1439        Vec::new(),
1440        FunctionSignatureBuilder {
1441            rest_param: Some(Effect::Freeze),
1442            return_type: Type::Primitive,
1443            return_value_kind: ValueKind::Primitive,
1444            ..Default::default()
1445        },
1446        Some(BUILT_IN_DISPATCH_ID),
1447        false,
1448    );
1449
1450    // BuiltInUseReducer: [0] = Poly, [1] = dispatch function
1451    add_object(
1452        shapes,
1453        Some(BUILT_IN_USE_REDUCER_ID),
1454        vec![("0".to_string(), Type::Poly), ("1".to_string(), dispatch)],
1455    );
1456
1457    // BuiltInStartTransition
1458    let start_transition = add_function(
1459        shapes,
1460        Vec::new(),
1461        FunctionSignatureBuilder {
1462            // Note: TS uses restParam: null for startTransition
1463            return_type: Type::Primitive,
1464            return_value_kind: ValueKind::Primitive,
1465            ..Default::default()
1466        },
1467        Some(BUILT_IN_START_TRANSITION_ID),
1468        false,
1469    );
1470
1471    // BuiltInUseTransition: [0] = Primitive (isPending), [1] = startTransition function
1472    add_object(
1473        shapes,
1474        Some(BUILT_IN_USE_TRANSITION_ID),
1475        vec![
1476            ("0".to_string(), Type::Primitive),
1477            ("1".to_string(), start_transition),
1478        ],
1479    );
1480
1481    // BuiltInSetOptimistic
1482    let set_optimistic = add_function(
1483        shapes,
1484        Vec::new(),
1485        FunctionSignatureBuilder {
1486            rest_param: Some(Effect::Freeze),
1487            return_type: Type::Primitive,
1488            return_value_kind: ValueKind::Primitive,
1489            ..Default::default()
1490        },
1491        Some(BUILT_IN_SET_OPTIMISTIC_ID),
1492        false,
1493    );
1494
1495    // BuiltInUseOptimistic: [0] = Poly, [1] = setOptimistic function
1496    add_object(
1497        shapes,
1498        Some(BUILT_IN_USE_OPTIMISTIC_ID),
1499        vec![
1500            ("0".to_string(), Type::Poly),
1501            ("1".to_string(), set_optimistic),
1502        ],
1503    );
1504}
1505
1506fn build_hook_shapes(shapes: &mut ShapeRegistry) {
1507    // BuiltInEffectEvent function shape (the return value of useEffectEvent)
1508    add_function(
1509        shapes,
1510        Vec::new(),
1511        FunctionSignatureBuilder {
1512            rest_param: Some(Effect::ConditionallyMutate),
1513            callee_effect: Effect::ConditionallyMutate,
1514            return_type: Type::Poly,
1515            return_value_kind: ValueKind::Mutable,
1516            ..Default::default()
1517        },
1518        Some(BUILT_IN_EFFECT_EVENT_ID),
1519        false,
1520    );
1521}
1522
1523fn build_misc_shapes(shapes: &mut ShapeRegistry) {
1524    // ReanimatedSharedValue: empty properties (matching TS)
1525    add_object(shapes, Some(REANIMATED_SHARED_VALUE_ID), Vec::new());
1526}
1527
1528/// Build the reanimated module type. Ported from TS `getReanimatedModuleType`.
1529pub fn get_reanimated_module_type(shapes: &mut ShapeRegistry) -> Type {
1530    let mut reanimated_type: Vec<(String, Type)> = Vec::new();
1531
1532    // hooks that freeze args and return frozen value
1533    let frozen_hooks = [
1534        "useFrameCallback",
1535        "useAnimatedStyle",
1536        "useAnimatedProps",
1537        "useAnimatedScrollHandler",
1538        "useAnimatedReaction",
1539        "useWorkletCallback",
1540    ];
1541    for hook in &frozen_hooks {
1542        let hook_type = add_hook(
1543            shapes,
1544            HookSignatureBuilder {
1545                rest_param: Some(Effect::Freeze),
1546                return_type: Type::Poly,
1547                return_value_kind: ValueKind::Frozen,
1548                no_alias: true,
1549                hook_kind: HookKind::Custom,
1550                ..Default::default()
1551            },
1552            None,
1553        );
1554        reanimated_type.push((hook.to_string(), hook_type));
1555    }
1556
1557    // hooks that return a mutable value (modelled as shared value)
1558    let mutable_hooks = ["useSharedValue", "useDerivedValue"];
1559    for hook in &mutable_hooks {
1560        let hook_type = add_hook(
1561            shapes,
1562            HookSignatureBuilder {
1563                rest_param: Some(Effect::Freeze),
1564                return_type: Type::Object {
1565                    shape_id: Some(REANIMATED_SHARED_VALUE_ID.to_string()),
1566                },
1567                return_value_kind: ValueKind::Mutable,
1568                no_alias: true,
1569                hook_kind: HookKind::Custom,
1570                ..Default::default()
1571            },
1572            None,
1573        );
1574        reanimated_type.push((hook.to_string(), hook_type));
1575    }
1576
1577    // functions that return mutable value
1578    let funcs = [
1579        "withTiming",
1580        "withSpring",
1581        "createAnimatedPropAdapter",
1582        "withDecay",
1583        "withRepeat",
1584        "runOnUI",
1585        "executeOnUIRuntimeSync",
1586    ];
1587    for func_name in &funcs {
1588        let func_type = add_function(
1589            shapes,
1590            Vec::new(),
1591            FunctionSignatureBuilder {
1592                rest_param: Some(Effect::Read),
1593                return_type: Type::Poly,
1594                return_value_kind: ValueKind::Mutable,
1595                no_alias: true,
1596                ..Default::default()
1597            },
1598            None,
1599            false,
1600        );
1601        reanimated_type.push((func_name.to_string(), func_type));
1602    }
1603
1604    add_object(shapes, None, reanimated_type)
1605}
1606
1607// =============================================================================
1608// Build default globals (DEFAULT_GLOBALS from Globals.ts)
1609// =============================================================================
1610
1611/// Build the default globals registry. This corresponds to TS `DEFAULT_GLOBALS`.
1612///
1613/// Requires a mutable reference to the shapes registry because some globals
1614/// (like Object.keys, Array.isArray) register new shapes.
1615pub fn build_default_globals(shapes: &mut ShapeRegistry) -> GlobalRegistry {
1616    let mut globals = GlobalRegistry::new();
1617
1618    // React APIs — returns the list so we can reuse them for the React namespace
1619    let react_apis = build_react_apis(shapes, &mut globals);
1620
1621    // Untyped globals (treated as Poly) — must come before typed globals
1622    // so typed definitions take priority (matching TS ordering)
1623    for name in UNTYPED_GLOBALS {
1624        globals.insert(name.to_string(), Type::Poly);
1625    }
1626
1627    // Typed JS globals (overwrites Poly entries from UNTYPED_GLOBALS).
1628    // Returns the list of typed globals for use as globalThis/global properties.
1629    let typed_globals = build_typed_globals(shapes, &mut globals, react_apis);
1630
1631    // globalThis and global — populated with all typed globals as properties
1632    // (matching TS: `addObject(DEFAULT_SHAPES, 'globalThis', TYPED_GLOBALS)`)
1633    globals.insert(
1634        "globalThis".to_string(),
1635        add_object(shapes, Some("globalThis"), typed_globals.clone()),
1636    );
1637    globals.insert(
1638        "global".to_string(),
1639        add_object(shapes, Some("global"), typed_globals),
1640    );
1641
1642    globals
1643}
1644
1645const UNTYPED_GLOBALS: &[&str] = &[
1646    "Object",
1647    "Function",
1648    "RegExp",
1649    "Date",
1650    "Error",
1651    "TypeError",
1652    "RangeError",
1653    "ReferenceError",
1654    "SyntaxError",
1655    "URIError",
1656    "EvalError",
1657    "DataView",
1658    "Float32Array",
1659    "Float64Array",
1660    "Int8Array",
1661    "Int16Array",
1662    "Int32Array",
1663    "WeakMap",
1664    "Uint8Array",
1665    "Uint8ClampedArray",
1666    "Uint16Array",
1667    "Uint32Array",
1668    "ArrayBuffer",
1669    "JSON",
1670    "console",
1671    "eval",
1672];
1673
1674/// Build the React API types (REACT_APIS from TS). Returns the list of (name, type) pairs
1675/// so they can be reused as properties of the React namespace object (matching TS behavior
1676/// where the SAME type objects are used in both DEFAULT_GLOBALS and the React namespace).
1677fn build_react_apis(
1678    shapes: &mut ShapeRegistry,
1679    globals: &mut GlobalRegistry,
1680) -> Vec<(String, Type)> {
1681    let mut react_apis: Vec<(String, Type)> = Vec::new();
1682
1683    // useContext
1684    let use_context = add_hook(
1685        shapes,
1686        HookSignatureBuilder {
1687            rest_param: Some(Effect::Read),
1688            return_type: Type::Poly,
1689            return_value_kind: ValueKind::Frozen,
1690            return_value_reason: Some(ValueReason::Context),
1691            hook_kind: HookKind::UseContext,
1692            ..Default::default()
1693        },
1694        Some(BUILT_IN_USE_CONTEXT_HOOK_ID),
1695    );
1696    react_apis.push(("useContext".to_string(), use_context));
1697
1698    // useState
1699    let use_state = add_hook(
1700        shapes,
1701        HookSignatureBuilder {
1702            rest_param: Some(Effect::Freeze),
1703            return_type: Type::Object {
1704                shape_id: Some(BUILT_IN_USE_STATE_ID.to_string()),
1705            },
1706            return_value_kind: ValueKind::Frozen,
1707            return_value_reason: Some(ValueReason::State),
1708            hook_kind: HookKind::UseState,
1709            ..Default::default()
1710        },
1711        None,
1712    );
1713    react_apis.push(("useState".to_string(), use_state));
1714
1715    // useActionState
1716    let use_action_state = add_hook(
1717        shapes,
1718        HookSignatureBuilder {
1719            rest_param: Some(Effect::Freeze),
1720            return_type: Type::Object {
1721                shape_id: Some(BUILT_IN_USE_ACTION_STATE_ID.to_string()),
1722            },
1723            return_value_kind: ValueKind::Frozen,
1724            return_value_reason: Some(ValueReason::State),
1725            hook_kind: HookKind::UseActionState,
1726            ..Default::default()
1727        },
1728        None,
1729    );
1730    react_apis.push(("useActionState".to_string(), use_action_state));
1731
1732    // useReducer
1733    let use_reducer = add_hook(
1734        shapes,
1735        HookSignatureBuilder {
1736            rest_param: Some(Effect::Freeze),
1737            return_type: Type::Object {
1738                shape_id: Some(BUILT_IN_USE_REDUCER_ID.to_string()),
1739            },
1740            return_value_kind: ValueKind::Frozen,
1741            return_value_reason: Some(ValueReason::ReducerState),
1742            hook_kind: HookKind::UseReducer,
1743            ..Default::default()
1744        },
1745        None,
1746    );
1747    react_apis.push(("useReducer".to_string(), use_reducer));
1748
1749    // useRef
1750    let use_ref = add_hook(
1751        shapes,
1752        HookSignatureBuilder {
1753            rest_param: Some(Effect::Capture),
1754            return_type: Type::Object {
1755                shape_id: Some(BUILT_IN_USE_REF_ID.to_string()),
1756            },
1757            return_value_kind: ValueKind::Mutable,
1758            hook_kind: HookKind::UseRef,
1759            ..Default::default()
1760        },
1761        None,
1762    );
1763    react_apis.push(("useRef".to_string(), use_ref));
1764
1765    // useImperativeHandle
1766    let use_imperative_handle = add_hook(
1767        shapes,
1768        HookSignatureBuilder {
1769            rest_param: Some(Effect::Freeze),
1770            return_type: Type::Primitive,
1771            return_value_kind: ValueKind::Frozen,
1772            hook_kind: HookKind::UseImperativeHandle,
1773            ..Default::default()
1774        },
1775        None,
1776    );
1777    react_apis.push(("useImperativeHandle".to_string(), use_imperative_handle));
1778
1779    // useMemo
1780    let use_memo = add_hook(
1781        shapes,
1782        HookSignatureBuilder {
1783            rest_param: Some(Effect::Freeze),
1784            return_type: Type::Poly,
1785            return_value_kind: ValueKind::Frozen,
1786            hook_kind: HookKind::UseMemo,
1787            ..Default::default()
1788        },
1789        None,
1790    );
1791    react_apis.push(("useMemo".to_string(), use_memo));
1792
1793    // useCallback
1794    let use_callback = add_hook(
1795        shapes,
1796        HookSignatureBuilder {
1797            rest_param: Some(Effect::Freeze),
1798            return_type: Type::Poly,
1799            return_value_kind: ValueKind::Frozen,
1800            hook_kind: HookKind::UseCallback,
1801            ..Default::default()
1802        },
1803        None,
1804    );
1805    react_apis.push(("useCallback".to_string(), use_callback));
1806
1807    // useEffect (with aliasing signature)
1808    let use_effect = add_hook(
1809        shapes,
1810        HookSignatureBuilder {
1811            rest_param: Some(Effect::Freeze),
1812            return_type: Type::Primitive,
1813            return_value_kind: ValueKind::Frozen,
1814            hook_kind: HookKind::UseEffect,
1815            aliasing: Some(AliasingSignatureConfig {
1816                receiver: "@receiver".to_string(),
1817                params: Vec::new(),
1818                rest: Some("@rest".to_string()),
1819                returns: "@returns".to_string(),
1820                temporaries: vec!["@effect".to_string()],
1821                effects: vec![
1822                    AliasingEffectConfig::Freeze {
1823                        value: "@rest".to_string(),
1824                        reason: ValueReason::Effect,
1825                    },
1826                    AliasingEffectConfig::Create {
1827                        into: "@effect".to_string(),
1828                        value: ValueKind::Frozen,
1829                        reason: ValueReason::KnownReturnSignature,
1830                    },
1831                    AliasingEffectConfig::Capture {
1832                        from: "@rest".to_string(),
1833                        into: "@effect".to_string(),
1834                    },
1835                    AliasingEffectConfig::Create {
1836                        into: "@returns".to_string(),
1837                        value: ValueKind::Primitive,
1838                        reason: ValueReason::KnownReturnSignature,
1839                    },
1840                ],
1841            }),
1842            ..Default::default()
1843        },
1844        Some(BUILT_IN_USE_EFFECT_HOOK_ID),
1845    );
1846    react_apis.push(("useEffect".to_string(), use_effect));
1847
1848    // useLayoutEffect
1849    let use_layout_effect = add_hook(
1850        shapes,
1851        HookSignatureBuilder {
1852            rest_param: Some(Effect::Freeze),
1853            return_type: Type::Poly,
1854            return_value_kind: ValueKind::Frozen,
1855            hook_kind: HookKind::UseLayoutEffect,
1856            ..Default::default()
1857        },
1858        Some(BUILT_IN_USE_LAYOUT_EFFECT_HOOK_ID),
1859    );
1860    react_apis.push(("useLayoutEffect".to_string(), use_layout_effect));
1861
1862    // useInsertionEffect
1863    let use_insertion_effect = add_hook(
1864        shapes,
1865        HookSignatureBuilder {
1866            rest_param: Some(Effect::Freeze),
1867            return_type: Type::Poly,
1868            return_value_kind: ValueKind::Frozen,
1869            hook_kind: HookKind::UseInsertionEffect,
1870            ..Default::default()
1871        },
1872        Some(BUILT_IN_USE_INSERTION_EFFECT_HOOK_ID),
1873    );
1874    react_apis.push(("useInsertionEffect".to_string(), use_insertion_effect));
1875
1876    // useTransition
1877    let use_transition = add_hook(
1878        shapes,
1879        HookSignatureBuilder {
1880            rest_param: None,
1881            return_type: Type::Object {
1882                shape_id: Some(BUILT_IN_USE_TRANSITION_ID.to_string()),
1883            },
1884            return_value_kind: ValueKind::Frozen,
1885            hook_kind: HookKind::UseTransition,
1886            ..Default::default()
1887        },
1888        None,
1889    );
1890    react_apis.push(("useTransition".to_string(), use_transition));
1891
1892    // useOptimistic
1893    let use_optimistic = add_hook(
1894        shapes,
1895        HookSignatureBuilder {
1896            rest_param: Some(Effect::Freeze),
1897            return_type: Type::Object {
1898                shape_id: Some(BUILT_IN_USE_OPTIMISTIC_ID.to_string()),
1899            },
1900            return_value_kind: ValueKind::Frozen,
1901            return_value_reason: Some(ValueReason::State),
1902            hook_kind: HookKind::UseOptimistic,
1903            ..Default::default()
1904        },
1905        None,
1906    );
1907    react_apis.push(("useOptimistic".to_string(), use_optimistic));
1908
1909    // use (not a hook, it's a function)
1910    let use_fn = add_function(
1911        shapes,
1912        Vec::new(),
1913        FunctionSignatureBuilder {
1914            rest_param: Some(Effect::Freeze),
1915            return_type: Type::Poly,
1916            return_value_kind: ValueKind::Frozen,
1917            ..Default::default()
1918        },
1919        Some(BUILT_IN_USE_OPERATOR_ID),
1920        false,
1921    );
1922    react_apis.push(("use".to_string(), use_fn));
1923
1924    // useEffectEvent
1925    let use_effect_event = add_hook(
1926        shapes,
1927        HookSignatureBuilder {
1928            rest_param: Some(Effect::Freeze),
1929            return_type: Type::Function {
1930                shape_id: Some(BUILT_IN_EFFECT_EVENT_ID.to_string()),
1931                return_type: Box::new(Type::Poly),
1932                is_constructor: false,
1933            },
1934            return_value_kind: ValueKind::Frozen,
1935            hook_kind: HookKind::UseEffectEvent,
1936            ..Default::default()
1937        },
1938        Some(BUILT_IN_USE_EFFECT_EVENT_ID),
1939    );
1940    react_apis.push(("useEffectEvent".to_string(), use_effect_event));
1941
1942    // Insert all React APIs as standalone globals
1943    for (name, ty) in &react_apis {
1944        globals.insert(name.clone(), ty.clone());
1945    }
1946
1947    react_apis
1948}
1949
1950/// Build typed globals and return them as a list for use as globalThis/global properties.
1951fn build_typed_globals(
1952    shapes: &mut ShapeRegistry,
1953    globals: &mut GlobalRegistry,
1954    react_apis: Vec<(String, Type)>,
1955) -> Vec<(String, Type)> {
1956    let mut typed_globals: Vec<(String, Type)> = Vec::new();
1957    // Object
1958    let obj_keys = add_function(
1959        shapes,
1960        Vec::new(),
1961        FunctionSignatureBuilder {
1962            positional_params: vec![Effect::Read],
1963            return_type: Type::Object {
1964                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
1965            },
1966            return_value_kind: ValueKind::Mutable,
1967            aliasing: Some(AliasingSignatureConfig {
1968                receiver: "@receiver".to_string(),
1969                params: vec!["@object".to_string()],
1970                rest: None,
1971                returns: "@returns".to_string(),
1972                temporaries: Vec::new(),
1973                effects: vec![
1974                    AliasingEffectConfig::Create {
1975                        into: "@returns".to_string(),
1976                        value: ValueKind::Mutable,
1977                        reason: ValueReason::KnownReturnSignature,
1978                    },
1979                    // Only keys are captured, and keys are immutable
1980                    AliasingEffectConfig::ImmutableCapture {
1981                        from: "@object".to_string(),
1982                        into: "@returns".to_string(),
1983                    },
1984                ],
1985            }),
1986            ..Default::default()
1987        },
1988        None,
1989        false,
1990    );
1991    let obj_from_entries = add_function(
1992        shapes,
1993        Vec::new(),
1994        FunctionSignatureBuilder {
1995            positional_params: vec![Effect::ConditionallyMutate],
1996            return_type: Type::Object {
1997                shape_id: Some(BUILT_IN_OBJECT_ID.to_string()),
1998            },
1999            return_value_kind: ValueKind::Mutable,
2000            ..Default::default()
2001        },
2002        None,
2003        false,
2004    );
2005    let obj_entries = add_function(
2006        shapes,
2007        Vec::new(),
2008        FunctionSignatureBuilder {
2009            positional_params: vec![Effect::Capture],
2010            return_type: Type::Object {
2011                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
2012            },
2013            return_value_kind: ValueKind::Mutable,
2014            aliasing: Some(AliasingSignatureConfig {
2015                receiver: "@receiver".to_string(),
2016                params: vec!["@object".to_string()],
2017                rest: None,
2018                returns: "@returns".to_string(),
2019                temporaries: Vec::new(),
2020                effects: vec![
2021                    AliasingEffectConfig::Create {
2022                        into: "@returns".to_string(),
2023                        value: ValueKind::Mutable,
2024                        reason: ValueReason::KnownReturnSignature,
2025                    },
2026                    // Object values are captured into the return
2027                    AliasingEffectConfig::Capture {
2028                        from: "@object".to_string(),
2029                        into: "@returns".to_string(),
2030                    },
2031                ],
2032            }),
2033            ..Default::default()
2034        },
2035        None,
2036        false,
2037    );
2038    let obj_values = add_function(
2039        shapes,
2040        Vec::new(),
2041        FunctionSignatureBuilder {
2042            positional_params: vec![Effect::Capture],
2043            return_type: Type::Object {
2044                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
2045            },
2046            return_value_kind: ValueKind::Mutable,
2047            aliasing: Some(AliasingSignatureConfig {
2048                receiver: "@receiver".to_string(),
2049                params: vec!["@object".to_string()],
2050                rest: None,
2051                returns: "@returns".to_string(),
2052                temporaries: Vec::new(),
2053                effects: vec![
2054                    AliasingEffectConfig::Create {
2055                        into: "@returns".to_string(),
2056                        value: ValueKind::Mutable,
2057                        reason: ValueReason::KnownReturnSignature,
2058                    },
2059                    // Object values are captured into the return
2060                    AliasingEffectConfig::Capture {
2061                        from: "@object".to_string(),
2062                        into: "@returns".to_string(),
2063                    },
2064                ],
2065            }),
2066            ..Default::default()
2067        },
2068        None,
2069        false,
2070    );
2071    let object_global = add_object(
2072        shapes,
2073        Some("Object"),
2074        vec![
2075            ("keys".to_string(), obj_keys),
2076            ("fromEntries".to_string(), obj_from_entries),
2077            ("entries".to_string(), obj_entries),
2078            ("values".to_string(), obj_values),
2079        ],
2080    );
2081    typed_globals.push(("Object".to_string(), object_global.clone()));
2082    globals.insert("Object".to_string(), object_global);
2083
2084    // Array
2085    let array_is_array = add_function(
2086        shapes,
2087        Vec::new(),
2088        FunctionSignatureBuilder {
2089            positional_params: vec![Effect::Read],
2090            return_type: Type::Primitive,
2091            return_value_kind: ValueKind::Primitive,
2092            ..Default::default()
2093        },
2094        None,
2095        false,
2096    );
2097    let array_from = add_function(
2098        shapes,
2099        Vec::new(),
2100        FunctionSignatureBuilder {
2101            positional_params: vec![
2102                Effect::ConditionallyMutateIterator,
2103                Effect::ConditionallyMutate,
2104                Effect::ConditionallyMutate,
2105            ],
2106            rest_param: Some(Effect::Read),
2107            return_type: Type::Object {
2108                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
2109            },
2110            return_value_kind: ValueKind::Mutable,
2111            ..Default::default()
2112        },
2113        None,
2114        false,
2115    );
2116    let array_of = add_function(
2117        shapes,
2118        Vec::new(),
2119        FunctionSignatureBuilder {
2120            rest_param: Some(Effect::Read),
2121            return_type: Type::Object {
2122                shape_id: Some(BUILT_IN_ARRAY_ID.to_string()),
2123            },
2124            return_value_kind: ValueKind::Mutable,
2125            ..Default::default()
2126        },
2127        None,
2128        false,
2129    );
2130    let array_global = add_object(
2131        shapes,
2132        Some("Array"),
2133        vec![
2134            ("isArray".to_string(), array_is_array),
2135            ("from".to_string(), array_from),
2136            ("of".to_string(), array_of),
2137        ],
2138    );
2139    typed_globals.push(("Array".to_string(), array_global.clone()));
2140    globals.insert("Array".to_string(), array_global);
2141
2142    // Math
2143    let math_fns: Vec<(String, Type)> = ["max", "min", "trunc", "ceil", "floor", "pow"]
2144    .iter()
2145    .map(|name| (name.to_string(), pure_primitive_fn(shapes)))
2146    .collect();
2147    let mut math_props = math_fns;
2148    math_props.push(("PI".to_string(), Type::Primitive));
2149    // Math.random is impure
2150    let math_random = add_function(
2151        shapes,
2152        Vec::new(),
2153        FunctionSignatureBuilder {
2154            return_type: Type::Poly,
2155            return_value_kind: ValueKind::Mutable,
2156            impure: true,
2157            canonical_name: Some("Math.random".to_string()),
2158            ..Default::default()
2159        },
2160        None,
2161        false,
2162    );
2163    math_props.push(("random".to_string(), math_random));
2164    let math_global = add_object(shapes, Some("Math"), math_props);
2165    typed_globals.push(("Math".to_string(), math_global.clone()));
2166    globals.insert("Math".to_string(), math_global);
2167
2168    // performance
2169    let perf_now = add_function(
2170        shapes,
2171        Vec::new(),
2172        FunctionSignatureBuilder {
2173            rest_param: Some(Effect::Read),
2174            return_type: Type::Poly,
2175            return_value_kind: ValueKind::Mutable,
2176            impure: true,
2177            canonical_name: Some("performance.now".to_string()),
2178            ..Default::default()
2179        },
2180        None,
2181        false,
2182    );
2183    let perf_global = add_object(
2184        shapes,
2185        Some("performance"),
2186        vec![("now".to_string(), perf_now)],
2187    );
2188    typed_globals.push(("performance".to_string(), perf_global.clone()));
2189    globals.insert("performance".to_string(), perf_global);
2190
2191    // Date
2192    let date_now = add_function(
2193        shapes,
2194        Vec::new(),
2195        FunctionSignatureBuilder {
2196            rest_param: Some(Effect::Read),
2197            return_type: Type::Poly,
2198            return_value_kind: ValueKind::Mutable,
2199            impure: true,
2200            canonical_name: Some("Date.now".to_string()),
2201            ..Default::default()
2202        },
2203        None,
2204        false,
2205    );
2206    let date_global = add_object(shapes, Some("Date"), vec![("now".to_string(), date_now)]);
2207    typed_globals.push(("Date".to_string(), date_global.clone()));
2208    globals.insert("Date".to_string(), date_global);
2209
2210    // console
2211    let console_methods: Vec<(String, Type)> = ["error", "info", "log", "table", "trace", "warn"]
2212            .iter()
2213            .map(|name| (name.to_string(), pure_primitive_fn(shapes)))
2214            .collect();
2215    let console_global = add_object(shapes, Some("console"), console_methods);
2216    typed_globals.push(("console".to_string(), console_global.clone()));
2217    globals.insert("console".to_string(), console_global);
2218
2219    // Simple global functions returning Primitive
2220    for name in &[
2221        "Boolean",
2222        "Number",
2223        "String",
2224        "parseInt",
2225        "parseFloat",
2226        "isNaN",
2227        "isFinite",
2228        "encodeURI",
2229        "encodeURIComponent",
2230        "decodeURI",
2231        "decodeURIComponent",
2232    ] {
2233        let f = pure_primitive_fn(shapes);
2234        typed_globals.push((name.to_string(), f.clone()));
2235        globals.insert(name.to_string(), f);
2236    }
2237
2238    // Primitive globals
2239    typed_globals.push(("Infinity".to_string(), Type::Primitive));
2240    globals.insert("Infinity".to_string(), Type::Primitive);
2241    typed_globals.push(("NaN".to_string(), Type::Primitive));
2242    globals.insert("NaN".to_string(), Type::Primitive);
2243
2244    // Map, Set, WeakMap, WeakSet constructors
2245    let map_ctor = add_function(
2246        shapes,
2247        Vec::new(),
2248        FunctionSignatureBuilder {
2249            positional_params: vec![Effect::ConditionallyMutateIterator],
2250            return_type: Type::Object {
2251                shape_id: Some(BUILT_IN_MAP_ID.to_string()),
2252            },
2253            return_value_kind: ValueKind::Mutable,
2254            ..Default::default()
2255        },
2256        None,
2257        true,
2258    );
2259    typed_globals.push(("Map".to_string(), map_ctor.clone()));
2260    globals.insert("Map".to_string(), map_ctor);
2261
2262    let set_ctor = add_function(
2263        shapes,
2264        Vec::new(),
2265        FunctionSignatureBuilder {
2266            positional_params: vec![Effect::ConditionallyMutateIterator],
2267            return_type: Type::Object {
2268                shape_id: Some(BUILT_IN_SET_ID.to_string()),
2269            },
2270            return_value_kind: ValueKind::Mutable,
2271            ..Default::default()
2272        },
2273        None,
2274        true,
2275    );
2276    typed_globals.push(("Set".to_string(), set_ctor.clone()));
2277    globals.insert("Set".to_string(), set_ctor);
2278
2279    let weak_map_ctor = add_function(
2280        shapes,
2281        Vec::new(),
2282        FunctionSignatureBuilder {
2283            positional_params: vec![Effect::ConditionallyMutateIterator],
2284            return_type: Type::Object {
2285                shape_id: Some(BUILT_IN_WEAK_MAP_ID.to_string()),
2286            },
2287            return_value_kind: ValueKind::Mutable,
2288            ..Default::default()
2289        },
2290        None,
2291        true,
2292    );
2293    typed_globals.push(("WeakMap".to_string(), weak_map_ctor.clone()));
2294    globals.insert("WeakMap".to_string(), weak_map_ctor);
2295
2296    let weak_set_ctor = add_function(
2297        shapes,
2298        Vec::new(),
2299        FunctionSignatureBuilder {
2300            positional_params: vec![Effect::ConditionallyMutateIterator],
2301            return_type: Type::Object {
2302                shape_id: Some(BUILT_IN_WEAK_SET_ID.to_string()),
2303            },
2304            return_value_kind: ValueKind::Mutable,
2305            ..Default::default()
2306        },
2307        None,
2308        true,
2309    );
2310    typed_globals.push(("WeakSet".to_string(), weak_set_ctor.clone()));
2311    globals.insert("WeakSet".to_string(), weak_set_ctor);
2312
2313    // React global object — reuses the same REACT_APIS types (matching TS behavior
2314    // where the same type objects are used as both standalone globals and React.* properties)
2315    let react_create_element = add_function(
2316        shapes,
2317        Vec::new(),
2318        FunctionSignatureBuilder {
2319            rest_param: Some(Effect::Freeze),
2320            return_type: Type::Poly,
2321            return_value_kind: ValueKind::Frozen,
2322            ..Default::default()
2323        },
2324        None,
2325        false,
2326    );
2327    let react_clone_element = add_function(
2328        shapes,
2329        Vec::new(),
2330        FunctionSignatureBuilder {
2331            rest_param: Some(Effect::Freeze),
2332            return_type: Type::Poly,
2333            return_value_kind: ValueKind::Frozen,
2334            ..Default::default()
2335        },
2336        None,
2337        false,
2338    );
2339    let react_create_ref = add_function(
2340        shapes,
2341        Vec::new(),
2342        FunctionSignatureBuilder {
2343            rest_param: Some(Effect::Capture),
2344            return_type: Type::Object {
2345                shape_id: Some(BUILT_IN_USE_REF_ID.to_string()),
2346            },
2347            return_value_kind: ValueKind::Mutable,
2348            ..Default::default()
2349        },
2350        None,
2351        false,
2352    );
2353
2354    // Build React namespace properties from react_apis + React-specific functions
2355    let mut react_props: Vec<(String, Type)> = react_apis;
2356    react_props.push(("createElement".to_string(), react_create_element));
2357    react_props.push(("cloneElement".to_string(), react_clone_element));
2358    react_props.push(("createRef".to_string(), react_create_ref));
2359
2360    let react_global = add_object(shapes, None, react_props);
2361    typed_globals.push(("React".to_string(), react_global.clone()));
2362    globals.insert("React".to_string(), react_global);
2363
2364    // _jsx (used by JSX transform)
2365    let jsx_fn = add_function(
2366        shapes,
2367        Vec::new(),
2368        FunctionSignatureBuilder {
2369            rest_param: Some(Effect::Freeze),
2370            return_type: Type::Poly,
2371            return_value_kind: ValueKind::Frozen,
2372            ..Default::default()
2373        },
2374        None,
2375        false,
2376    );
2377    typed_globals.push(("_jsx".to_string(), jsx_fn.clone()));
2378    globals.insert("_jsx".to_string(), jsx_fn);
2379
2380    typed_globals
2381}