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