Skip to main content

luaur_analysis/methods/
normalizer_normalize.rs

1use crate::enums::normalization_result::NormalizationResult;
2use crate::records::fuel_initializer::FuelInitializer;
3use crate::records::normalized_extern_type::NormalizedExternType;
4use crate::records::normalized_function_type::NormalizedFunctionType;
5use crate::records::normalized_string_type::NormalizedStringType;
6use crate::records::normalized_type::NormalizedType;
7use crate::records::normalizer::Normalizer;
8use crate::records::normalizer_hit_limits::NormalizerHitLimits;
9use crate::records::type_ids::TypeIds;
10use crate::type_aliases::type_id::TypeId;
11use alloc::collections::BTreeMap;
12use alloc::sync::Arc;
13use luaur_common::records::dense_hash_set::DenseHashSet;
14
15impl Normalizer {
16    pub fn normalize(&mut self, ty: TypeId) -> Arc<NormalizedType> {
17        self.try_normalize(ty)
18            .unwrap_or_else(|| Arc::new(self.empty_normalized_type()))
19    }
20
21    pub fn try_normalize(&mut self, ty: TypeId) -> Option<Arc<NormalizedType>> {
22        match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| self.normalize_uncaught(ty)))
23        {
24            Ok(norm) => norm,
25            Err(payload) if payload.downcast_ref::<NormalizerHitLimits>().is_some() => None,
26            Err(payload) => std::panic::resume_unwind(payload),
27        }
28    }
29
30    fn normalize_uncaught(&mut self, ty: TypeId) -> Option<Arc<NormalizedType>> {
31        if self.arena.is_null() {
32            unsafe {
33                (*(*self.shared_state).ice_handler)
34                    .ice_string("Normalizing types outside a module");
35            }
36        }
37
38        if let Some(shared) = self.cached_normals.get(&ty) {
39            return Some(shared.clone());
40        }
41
42        let mut norm = self.empty_normalized_type();
43        let mut seen_set_types: DenseHashSet<TypeId> = DenseHashSet::new(core::ptr::null_mut());
44        let mut seen_table_prop_pairs: crate::type_aliases::seen_table_prop_pairs::SeenTablePropPairs =
45            crate::type_aliases::seen_table_prop_pairs::SeenTablePropPairs::new((core::ptr::null(), core::ptr::null()));
46
47        // FuelInitializer handles initializing and tearing down normalization fuel limits.
48        let mut fi = FuelInitializer {
49            normalizer: self as *mut Normalizer,
50            initialized_fuel: false,
51        };
52        fi.fuel_initializer_not_null_normalizer(self as *mut Normalizer);
53        let _ = fi;
54
55        let res = self.union_normal_with_ty(
56            &mut norm,
57            ty,
58            &mut seen_table_prop_pairs,
59            &mut seen_set_types,
60            -1,
61        );
62
63        if res != NormalizationResult::True {
64            return None;
65        }
66
67        if norm.is_unknown() {
68            self.clear_normal(&mut norm);
69            norm.tops = unsafe { (*self.builtin_types).unknownType };
70        }
71
72        let shared = Arc::new(norm);
73
74        if shared.is_cacheable {
75            self.cached_normals.insert(ty, shared.clone());
76        }
77
78        Some(shared)
79    }
80
81    fn empty_normalized_type(&self) -> NormalizedType {
82        let never_type = unsafe { (*self.builtin_types).neverType };
83
84        NormalizedType {
85            builtin_types: self.builtin_types,
86            tops: never_type,
87            booleans: never_type,
88            extern_types: NormalizedExternType {
89                extern_types: BTreeMap::new(),
90                shape_extensions: TypeIds::type_ids(),
91                ordering: Vec::new(),
92            },
93            errors: never_type,
94            nils: never_type,
95            numbers: never_type,
96            integers: never_type,
97            strings: NormalizedStringType::never,
98            threads: never_type,
99            buffers: never_type,
100            tables: TypeIds::type_ids(),
101            functions: NormalizedFunctionType {
102                is_top: false,
103                parts: TypeIds::type_ids(),
104            },
105            tyvars: BTreeMap::new(),
106            is_cacheable: true,
107        }
108    }
109}