i_slint_compiler/
namedreference.rs1use smol_str::SmolStr;
9use std::cell::RefCell;
10use std::collections::HashMap;
11use std::hash::Hash;
12use std::rc::{Rc, Weak};
13
14use crate::expression_tree::Expression;
15use crate::langtype::{ElementType, PropertyLookupMode, Type};
16use crate::object_tree::{Element, ElementRc, PropertyAnalysis, PropertyVisibility};
17
18#[derive(Clone)]
20pub struct NamedReference(Rc<NamedReferenceInner>);
21
22pub fn pretty_print_element_ref(
23 f: &mut dyn std::fmt::Write,
24 element: &Weak<RefCell<Element>>,
25) -> std::fmt::Result {
26 match element.upgrade() {
27 Some(e) => match e.try_borrow() {
28 Ok(el) => write!(f, "{}", el.id),
29 Err(_) => write!(f, "<borrowed>"),
30 },
31 None => write!(f, "<null>"),
32 }
33}
34
35impl std::fmt::Debug for NamedReference {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 pretty_print_element_ref(f, &self.0.element)?;
38 write!(f, ".{}", self.0.name)
39 }
40}
41
42impl NamedReference {
43 pub fn new(element: &ElementRc, name: SmolStr) -> Self {
44 Self(NamedReferenceInner::from_name(element, name))
45 }
46 pub(crate) fn snapshot(&self, snapshotter: &mut crate::typeloader::Snapshotter) -> Self {
47 NamedReference(Rc::new(self.0.snapshot(snapshotter)))
48 }
49 pub fn name(&self) -> &SmolStr {
50 &self.0.name
51 }
52 #[track_caller]
53 pub fn element(&self) -> ElementRc {
54 self.0
55 .element
56 .upgrade()
57 .unwrap_or_else(|| panic!("{}: NamedReference to a dead element", self.0.name))
58 }
59 pub fn ty(&self) -> Type {
60 self.element()
61 .borrow()
62 .lookup_property(self.name(), PropertyLookupMode::InternalName)
63 .property_type
64 }
65
66 pub fn declared_name(&self) -> SmolStr {
69 let elem = self.element();
70 let elem = elem.borrow();
71 elem.property_declarations
72 .get(self.name())
73 .map_or_else(|| self.name().clone(), |d| d.declared_name(self.name()).clone())
74 }
75
76 pub fn is_constant(&self) -> bool {
78 self.is_constant_impl(true)
79 }
80
81 pub fn is_externally_modified(&self) -> bool {
83 !self.is_constant_impl(false)
84 }
85
86 fn is_constant_impl(&self, mut check_binding: bool) -> bool {
88 let mut elem = self.element();
89 let e = elem.borrow();
90 if let Some(decl) = e.property_declarations.get(self.name())
91 && decl.expose_in_public_api
92 && decl.visibility != PropertyVisibility::Input
93 {
94 return false;
96 }
97 if e.property_analysis.borrow().get(self.name()).is_some_and(|a| a.is_set_externally) {
98 return false;
99 }
100 if e.binding_cell_including_synthetic(self.name()).is_some_and(|binding| {
101 matches!(binding.borrow().expression, Expression::DebugHook { .. })
102 }) {
103 return false;
104 }
105 drop(e);
106
107 loop {
108 let e = elem.borrow();
109 if e.property_analysis.borrow().get(self.name()).is_some_and(|a| a.is_set) {
110 return false;
112 }
113
114 if let Some(binding) = e.binding(self.name()) {
115 if check_binding && !binding.analysis.as_ref().is_some_and(|a| a.is_const) {
116 return false;
117 }
118 if !binding.two_way_bindings.iter().all(|n| n.is_constant()) {
119 return false;
120 }
121 check_binding = false;
122 }
123 if let Some(decl) = e.property_declarations.get(self.name()) {
124 if let Some(alias) = &decl.is_alias {
125 return alias.is_constant();
126 }
127 return true;
128 }
129 match &e.base_type {
130 ElementType::Component(c) => {
131 let next = c.root_element.clone();
132 drop(e);
133 elem = next;
134 continue;
135 }
136 ElementType::Builtin(b) => {
137 return b.properties.get(self.name()).is_none_or(|pi| !pi.is_native_output());
138 }
139 ElementType::Native(n) => {
140 return n.properties.get(self.name()).is_none_or(|pi| !pi.is_native_output());
141 }
142 crate::langtype::ElementType::Error
143 | crate::langtype::ElementType::Global
144 | crate::langtype::ElementType::Interface => {
145 return true;
146 }
147 }
148 }
149 }
150
151 pub fn mark_as_set(&self) {
153 let element = self.element();
154 element
155 .borrow()
156 .property_analysis
157 .borrow_mut()
158 .entry(self.name().clone())
159 .or_default()
160 .is_set = true;
161 mark_property_set_derived_in_base(element, self.name())
162 }
163}
164
165impl Eq for NamedReference {}
166
167impl PartialEq for NamedReference {
168 fn eq(&self, other: &Self) -> bool {
169 Rc::ptr_eq(&self.0, &other.0)
170 }
171}
172
173impl Hash for NamedReference {
174 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
175 Rc::as_ptr(&self.0).hash(state);
176 }
177}
178
179struct NamedReferenceInner {
180 element: Weak<RefCell<Element>>,
182 name: SmolStr,
184}
185
186impl NamedReferenceInner {
187 fn check_invariant(&self) {
188 debug_assert!(std::ptr::eq(
189 self as *const Self,
190 Rc::as_ptr(
191 &self.element.upgrade().unwrap().borrow().named_references.0.borrow()[&self.name]
192 )
193 ))
194 }
195
196 pub fn from_name(element: &ElementRc, name: SmolStr) -> Rc<Self> {
197 let elem = element.borrow();
198 let mut named_references = elem.named_references.0.borrow_mut();
199 let result = if let Some(r) = named_references.get(&name) {
200 r.clone()
201 } else {
202 let r = Rc::new(Self { element: Rc::downgrade(element), name });
203 named_references.insert(r.name.clone(), r.clone());
204 r
205 };
206 drop(named_references);
207 result.check_invariant();
208 result
209 }
210
211 pub(crate) fn snapshot(&self, snapshotter: &mut crate::typeloader::Snapshotter) -> Self {
212 let element = if let Some(el) = self.element.upgrade() {
213 Rc::downgrade(&snapshotter.use_element(&el))
214 } else {
215 std::rc::Weak::default()
216 };
217
218 Self { element, name: self.name.clone() }
219 }
220}
221
222#[derive(Default)]
224pub struct NamedReferenceContainer(RefCell<HashMap<SmolStr, Rc<NamedReferenceInner>>>);
225
226impl NamedReferenceContainer {
227 pub fn is_referenced(&self, name: &str) -> bool {
229 if let Some(nri) = self.0.borrow().get(name) {
230 Rc::strong_count(nri) > 1
232 } else {
233 false
234 }
235 }
236
237 pub(crate) fn snapshot(
238 &self,
239 snapshotter: &mut crate::typeloader::Snapshotter,
240 ) -> NamedReferenceContainer {
241 let inner = self
242 .0
243 .borrow()
244 .iter()
245 .map(|(k, v)| (k.clone(), Rc::new(v.snapshot(snapshotter))))
246 .collect();
247 NamedReferenceContainer(RefCell::new(inner))
248 }
249}
250
251pub(crate) fn mark_property_set_derived_in_base(mut element: ElementRc, name: &str) {
253 loop {
254 let next = if let ElementType::Component(c) = &element.borrow().base_type {
255 if element.borrow().property_declarations.contains_key(name) {
256 return;
257 };
258 match c.root_element.borrow().property_analysis.borrow_mut().entry(name.into()) {
259 std::collections::btree_map::Entry::Occupied(e) if e.get().is_set_externally => {
260 return;
261 }
262 std::collections::btree_map::Entry::Occupied(mut e) => {
263 e.get_mut().is_set_externally = true;
264 }
265 std::collections::btree_map::Entry::Vacant(e) => {
266 e.insert(PropertyAnalysis { is_set_externally: true, ..Default::default() });
267 }
268 }
269 c.root_element.clone()
270 } else {
271 return;
272 };
273 element = next;
274 }
275}
276
277pub(crate) fn mark_property_read_derived_in_base(mut element: ElementRc, name: &str) {
279 loop {
280 let next = if let ElementType::Component(c) = &element.borrow().base_type {
281 if element.borrow().property_declarations.contains_key(name) {
282 return;
283 };
284 match c.root_element.borrow().property_analysis.borrow_mut().entry(name.into()) {
285 std::collections::btree_map::Entry::Occupied(e) if e.get().is_read_externally => {
286 return;
287 }
288 std::collections::btree_map::Entry::Occupied(mut e) => {
289 e.get_mut().is_read_externally = true;
290 }
291 std::collections::btree_map::Entry::Vacant(e) => {
292 e.insert(PropertyAnalysis { is_read_externally: true, ..Default::default() });
293 }
294 }
295 c.root_element.clone()
296 } else {
297 return;
298 };
299 element = next;
300 }
301}