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.element.upgrade().expect("NamedReference to a dead element")
54 }
55 pub fn ty(&self) -> Type {
56 self.element().borrow().lookup_property(self.name()).property_type
57 }
58
59 pub fn is_constant(&self) -> bool {
61 self.is_constant_impl(true)
62 }
63
64 pub fn is_externally_modified(&self) -> bool {
66 !self.is_constant_impl(false)
67 }
68
69 fn is_constant_impl(&self, mut check_binding: bool) -> bool {
71 let mut elem = self.element();
72 let e = elem.borrow();
73 if let Some(decl) = e.property_declarations.get(self.name()) {
74 if decl.expose_in_public_api && decl.visibility != PropertyVisibility::Input {
75 return false;
77 }
78 }
79 if e.property_analysis.borrow().get(self.name()).is_some_and(|a| a.is_set_externally) {
80 return false;
81 }
82 drop(e);
83
84 loop {
85 let e = elem.borrow();
86 if e.property_analysis.borrow().get(self.name()).is_some_and(|a| a.is_set) {
87 return false;
89 }
90
91 if let Some(b) = e.bindings.get(self.name()) {
92 if check_binding && !b.borrow().analysis.as_ref().is_some_and(|a| a.is_const) {
93 return false;
94 }
95 if !b.borrow().two_way_bindings.iter().all(|n| n.is_constant()) {
96 return false;
97 }
98 check_binding = false;
99 }
100 if let Some(decl) = e.property_declarations.get(self.name()) {
101 if let Some(alias) = &decl.is_alias {
102 return alias.is_constant();
103 }
104 return true;
105 }
106 match &e.base_type {
107 ElementType::Component(c) => {
108 let next = c.root_element.clone();
109 drop(e);
110 elem = next;
111 continue;
112 }
113 ElementType::Builtin(b) => {
114 return b.properties.get(self.name()).map_or(true, |pi| !pi.is_native_output())
115 }
116 ElementType::Native(n) => {
117 return n.properties.get(self.name()).map_or(true, |pi| !pi.is_native_output())
118 }
119 crate::langtype::ElementType::Error | crate::langtype::ElementType::Global => {
120 return true
121 }
122 }
123 }
124 }
125
126 pub fn mark_as_set(&self) {
128 let element = self.element();
129 element
130 .borrow()
131 .property_analysis
132 .borrow_mut()
133 .entry(self.name().clone())
134 .or_default()
135 .is_set = true;
136 mark_property_set_derived_in_base(element, self.name())
137 }
138}
139
140impl Eq for NamedReference {}
141
142impl PartialEq for NamedReference {
143 fn eq(&self, other: &Self) -> bool {
144 Rc::ptr_eq(&self.0, &other.0)
145 }
146}
147
148impl Hash for NamedReference {
149 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
150 Rc::as_ptr(&self.0).hash(state);
151 }
152}
153
154struct NamedReferenceInner {
155 element: Weak<RefCell<Element>>,
157 name: SmolStr,
159}
160
161impl NamedReferenceInner {
162 fn check_invariant(&self) {
163 debug_assert!(std::ptr::eq(
164 self as *const Self,
165 Rc::as_ptr(
166 &self.element.upgrade().unwrap().borrow().named_references.0.borrow()[&self.name]
167 )
168 ))
169 }
170
171 pub fn from_name(element: &ElementRc, name: SmolStr) -> Rc<Self> {
172 let elem = element.borrow();
173 let mut named_references = elem.named_references.0.borrow_mut();
174 let result = if let Some(r) = named_references.get(&name) {
175 r.clone()
176 } else {
177 let r = Rc::new(Self { element: Rc::downgrade(element), name });
178 named_references.insert(r.name.clone(), r.clone());
179 r
180 };
181 drop(named_references);
182 result.check_invariant();
183 result
184 }
185
186 pub(crate) fn snapshot(&self, snapshotter: &mut crate::typeloader::Snapshotter) -> Self {
187 let element = if let Some(el) = self.element.upgrade() {
188 Rc::downgrade(&snapshotter.use_element(&el))
189 } else {
190 std::rc::Weak::default()
191 };
192
193 Self { element, name: self.name.clone() }
194 }
195}
196
197#[derive(Default)]
199pub struct NamedReferenceContainer(RefCell<HashMap<SmolStr, Rc<NamedReferenceInner>>>);
200
201impl NamedReferenceContainer {
202 pub fn is_referenced(&self, name: &str) -> bool {
204 if let Some(nri) = self.0.borrow().get(name) {
205 Rc::strong_count(nri) > 1
207 } else {
208 false
209 }
210 }
211
212 pub(crate) fn snapshot(
213 &self,
214 snapshotter: &mut crate::typeloader::Snapshotter,
215 ) -> NamedReferenceContainer {
216 let inner = self
217 .0
218 .borrow()
219 .iter()
220 .map(|(k, v)| (k.clone(), Rc::new(v.snapshot(snapshotter))))
221 .collect();
222 NamedReferenceContainer(RefCell::new(inner))
223 }
224}
225
226pub(crate) fn mark_property_set_derived_in_base(mut element: ElementRc, name: &str) {
228 loop {
229 let next = if let ElementType::Component(c) = &element.borrow().base_type {
230 if element.borrow().property_declarations.contains_key(name) {
231 return;
232 };
233 match c.root_element.borrow().property_analysis.borrow_mut().entry(name.into()) {
234 std::collections::hash_map::Entry::Occupied(e) if e.get().is_set_externally => {
235 return;
236 }
237 std::collections::hash_map::Entry::Occupied(mut e) => {
238 e.get_mut().is_set_externally = true;
239 }
240 std::collections::hash_map::Entry::Vacant(e) => {
241 e.insert(PropertyAnalysis { is_set_externally: true, ..Default::default() });
242 }
243 }
244 c.root_element.clone()
245 } else {
246 return;
247 };
248 element = next;
249 }
250}
251
252pub(crate) fn mark_property_read_derived_in_base(mut element: ElementRc, name: &str) {
254 loop {
255 let next = if let ElementType::Component(c) = &element.borrow().base_type {
256 if element.borrow().property_declarations.contains_key(name) {
257 return;
258 };
259 match c.root_element.borrow().property_analysis.borrow_mut().entry(name.into()) {
260 std::collections::hash_map::Entry::Occupied(e) if e.get().is_read_externally => {
261 return;
262 }
263 std::collections::hash_map::Entry::Occupied(mut e) => {
264 e.get_mut().is_read_externally = true;
265 }
266 std::collections::hash_map::Entry::Vacant(e) => {
267 e.insert(PropertyAnalysis { is_read_externally: true, ..Default::default() });
268 }
269 }
270 c.root_element.clone()
271 } else {
272 return;
273 };
274 element = next;
275 }
276}