sixtyfps_compilerlib/
namedreference.rs1use std::cell::RefCell;
9use std::collections::HashMap;
10use std::hash::Hash;
11use std::rc::{Rc, Weak};
12
13use crate::langtype::Type;
14use crate::object_tree::{Element, ElementRc, PropertyAnalysis};
15
16#[derive(Clone)]
18pub struct NamedReference(Rc<NamedReferenceInner>);
19
20pub fn pretty_print_element_ref(
21 f: &mut dyn std::fmt::Write,
22 element: &Weak<RefCell<Element>>,
23) -> std::fmt::Result {
24 match element.upgrade() {
25 Some(e) => match e.try_borrow() {
26 Ok(el) => write!(f, "{}", el.id),
27 Err(_) => write!(f, "<borrowed>"),
28 },
29 None => write!(f, "<null>"),
30 }
31}
32
33impl std::fmt::Debug for NamedReference {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 pretty_print_element_ref(f, &self.0.element)?;
36 write!(f, ".{}", self.0.name)
37 }
38}
39
40impl NamedReference {
41 pub fn new(element: &ElementRc, name: &str) -> Self {
42 Self(NamedReferenceInner::from_name(element, name))
43 }
44 pub fn name(&self) -> &str {
45 &self.0.name
46 }
47 pub fn element(&self) -> ElementRc {
48 self.0.element.upgrade().unwrap()
49 }
50 pub fn ty(&self) -> Type {
51 self.element().borrow().lookup_property(self.name()).property_type
52 }
53
54 pub fn is_constant(&self) -> bool {
56 self.is_constant_impl(true)
57 }
58
59 pub fn is_externally_modified(&self) -> bool {
61 !self.is_constant_impl(false)
62 }
63
64 fn is_constant_impl(&self, mut check_binding: bool) -> bool {
66 let mut elem = self.element();
67 let e = elem.borrow();
68 if let Some(decl) = e.property_declarations.get(self.name()) {
69 if decl.expose_in_public_api {
70 return false;
72 }
73 }
74 if e.property_analysis.borrow().get(self.name()).map_or(false, |a| a.is_set_externally) {
75 return false;
76 }
77 drop(e);
78
79 loop {
80 let e = elem.borrow();
81 if e.property_analysis.borrow().get(self.name()).map_or(false, |a| a.is_set) {
82 return false;
84 }
85
86 if check_binding {
87 if let Some(b) = e.bindings.get(self.name()) {
88 if !b.borrow().analysis.as_ref().map_or(false, |a| a.is_const) {
89 return false;
90 }
91 check_binding = false;
92 }
93 }
94 if let Some(decl) = e.property_declarations.get(self.name()) {
95 if let Some(alias) = &decl.is_alias {
96 return alias.is_constant();
97 }
98 return true;
99 }
100 match &e.base_type {
101 Type::Component(c) => {
102 let next = c.root_element.clone();
103 drop(e);
104 elem = next;
105 continue;
106 }
107 Type::Builtin(b) => {
108 return b.properties.get(self.name()).map_or(true, |pi| !pi.is_native_output)
109 }
110 Type::Native(n) => {
111 return n.properties.get(self.name()).map_or(true, |pi| !pi.is_native_output)
112 }
113 _ => return true,
114 }
115 }
116 }
117
118 pub fn mark_as_set(&self) {
120 let element = self.element();
121 element
122 .borrow()
123 .property_analysis
124 .borrow_mut()
125 .entry(self.name().to_owned())
126 .or_default()
127 .is_set = true;
128 mark_property_set_derived_in_base(element, self.name())
129 }
130}
131
132impl Eq for NamedReference {}
133
134impl PartialEq for NamedReference {
135 fn eq(&self, other: &Self) -> bool {
136 Rc::ptr_eq(&self.0, &other.0)
137 }
138}
139
140impl Hash for NamedReference {
141 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
142 Rc::as_ptr(&self.0).hash(state);
143 }
144}
145
146struct NamedReferenceInner {
147 element: Weak<RefCell<Element>>,
149 name: String,
151}
152
153impl NamedReferenceInner {
154 fn check_invariant(&self) {
155 debug_assert!(std::ptr::eq(
156 self as *const Self,
157 Rc::as_ptr(
158 &self.element.upgrade().unwrap().borrow().named_references.0.borrow()[&self.name]
159 )
160 ))
161 }
162
163 pub fn from_name(element: &ElementRc, name: &str) -> Rc<Self> {
164 let elem = element.borrow();
165 let mut named_references = elem.named_references.0.borrow_mut();
166 let result = if let Some(r) = named_references.get(name) {
167 r.clone()
168 } else {
169 let r = Rc::new(Self { element: Rc::downgrade(element), name: name.to_owned() });
170 named_references.insert(name.to_owned(), r.clone());
171 r
172 };
173 drop(named_references);
174 result.check_invariant();
175 result
176 }
177}
178
179#[derive(Default)]
181pub struct NamedReferenceContainer(RefCell<HashMap<String, Rc<NamedReferenceInner>>>);
182
183impl NamedReferenceContainer {
184 pub fn is_referenced(&self, name: &str) -> bool {
186 if let Some(nri) = self.0.borrow().get(name) {
187 Rc::strong_count(nri) > 1
189 } else {
190 false
191 }
192 }
193}
194
195pub(crate) fn mark_property_set_derived_in_base(mut element: ElementRc, name: &str) {
197 loop {
198 let next = if let Type::Component(c) = &element.borrow().base_type {
199 if element.borrow().property_declarations.contains_key(name) {
200 return;
201 };
202 match c.root_element.borrow().property_analysis.borrow_mut().entry(name.to_owned()) {
203 std::collections::hash_map::Entry::Occupied(e) if e.get().is_set_externally => {
204 return;
205 }
206 std::collections::hash_map::Entry::Occupied(mut e) => {
207 e.get_mut().is_set_externally = true;
208 }
209 std::collections::hash_map::Entry::Vacant(e) => {
210 e.insert(PropertyAnalysis { is_set_externally: true, ..Default::default() });
211 }
212 }
213 c.root_element.clone()
214 } else {
215 return;
216 };
217 element = next;
218 }
219}