1 | use super::*; |
2 | |
3 | #[derive (Clone)] |
4 | pub enum Item { |
5 | Type(TypeDef), |
6 | Const(Field), |
7 | // TODO: get rid of the trailing String - that's just a hack to get around a silly Win32 metadata deficiency where parsing method signatures |
8 | // requires knowing which namespace the method's surrounding interface was defined in. |
9 | Fn(MethodDef, &'static str), |
10 | } |
11 | |
12 | pub struct Reader { |
13 | // TODO: get rid of inner Vec - that's just a hack to support multi-arch structs in Win32 metadata. |
14 | items: BTreeMap<&'static str, BTreeMap<&'static str, Vec<Item>>>, |
15 | |
16 | // TODO: riddle should just avoid nested structs |
17 | nested: HashMap<TypeDef, BTreeMap<&'static str, TypeDef>>, |
18 | |
19 | // The reader needs to store the filter since standalone code generation needs more than just the filtered items |
20 | // in order to chase dependencies automatically. This is why `Reader::filter` can't just filter everything up front. |
21 | filter: Filter, |
22 | } |
23 | |
24 | impl Reader { |
25 | pub fn new(files: Vec<File>) -> &'static Self { |
26 | Self::filter(files, &[], &[]) |
27 | } |
28 | |
29 | pub fn filter(files: Vec<File>, include: &[&str], exclude: &[&str]) -> &'static Self { |
30 | let reader: &'static mut Reader = Box::leak(Box::new(Self { items: Default::default(), nested: Default::default(), filter: Filter::new(include, exclude) })); |
31 | |
32 | for mut file in files { |
33 | file.reader = reader as *mut Reader; |
34 | let file = Box::leak(Box::new(file)); |
35 | |
36 | for def in file.table::<TypeDef>() { |
37 | let namespace = def.namespace(); |
38 | |
39 | if namespace.is_empty() { |
40 | continue; |
41 | } |
42 | |
43 | let namespace_items = reader.items.entry(namespace).or_default(); |
44 | let name = def.name(); |
45 | |
46 | if name == "Apis" { |
47 | for method in def.methods() { |
48 | namespace_items.entry(method.name()).or_default().push(Item::Fn(method, namespace)); |
49 | } |
50 | |
51 | for field in def.fields() { |
52 | namespace_items.entry(field.name()).or_default().push(Item::Const(field)); |
53 | } |
54 | } else { |
55 | namespace_items.entry(name).or_default().push(Item::Type(def)); |
56 | |
57 | // TODO: these should all be fields on the Apis class so we don't have to go looking for all of these as well. |
58 | if def.extends() == Some(TypeName::Enum) && !def.flags().contains(TypeAttributes::WindowsRuntime) && !def.has_attribute("ScopedEnumAttribute" ) { |
59 | for field in def.fields().filter(|field| field.flags().contains(FieldAttributes::Literal)) { |
60 | namespace_items.entry(field.name()).or_default().push(Item::Const(field)); |
61 | } |
62 | } |
63 | } |
64 | } |
65 | |
66 | for key in file.table::<NestedClass>() { |
67 | let inner = key.inner(); |
68 | reader.nested.entry(key.outer()).or_default().insert(inner.name(), inner); |
69 | } |
70 | } |
71 | |
72 | reader |
73 | } |
74 | |
75 | pub fn includes_namespace(&self, namespace: &str) -> bool { |
76 | self.filter.includes_namespace(namespace) |
77 | } |
78 | |
79 | pub fn namespaces(&self) -> impl Iterator<Item = &str> + '_ { |
80 | self.items.keys().copied() |
81 | } |
82 | |
83 | pub fn items(&self) -> impl Iterator<Item = Item> + '_ { |
84 | self.items.iter().filter(move |(namespace, _)| self.filter.includes_namespace(namespace)).flat_map(move |(namespace, items)| items.iter().filter(move |(name, _)| self.filter.includes_type_name(namespace, name))).flat_map(move |(_, items)| items).cloned() |
85 | } |
86 | |
87 | pub fn namespace_items(&self, namespace: &str) -> impl Iterator<Item = Item> + '_ { |
88 | self.items.get_key_value(namespace).into_iter().flat_map(move |(namespace, items)| items.iter().filter(move |(name, _)| self.filter.includes_type_name(namespace, name))).flat_map(move |(_, items)| items).cloned() |
89 | } |
90 | |
91 | pub fn unused(&self) -> impl Iterator<Item = &str> + '_ { |
92 | self.filter.0.iter().filter_map(|(name, _)| if self.is_unused(name) { Some(name.as_str()) } else { None }) |
93 | } |
94 | |
95 | fn is_unused(&self, filter: &str) -> bool { |
96 | // Match namespaces |
97 | if self.items.contains_key(filter) { |
98 | return false; |
99 | } |
100 | |
101 | // Match type names |
102 | if let Some((namespace, name)) = filter.rsplit_once('.' ) { |
103 | if self.items.get(namespace).is_some_and(|items| items.contains_key(name)) { |
104 | return false; |
105 | } |
106 | } |
107 | |
108 | // Match empty parent namespaces |
109 | for namespace in self.items.keys() { |
110 | if namespace.len() > filter.len() && namespace.starts_with(filter) && namespace.as_bytes()[filter.len()] == b'.' { |
111 | return false; |
112 | } |
113 | } |
114 | |
115 | true |
116 | } |
117 | |
118 | fn get_item(&self, namespace: &str, name: &str) -> impl Iterator<Item = Item> + '_ { |
119 | if let Some(items) = self.items.get(namespace) { |
120 | if let Some(items) = items.get(name) { |
121 | return Some(items.iter().cloned()).into_iter().flatten(); |
122 | } |
123 | } |
124 | None.into_iter().flatten() |
125 | } |
126 | |
127 | pub fn get_type_def(&self, namespace: &str, name: &str) -> impl Iterator<Item = TypeDef> + '_ { |
128 | self.get_item(namespace, name).filter_map(|item| if let Item::Type(def) = item { Some(def) } else { None }) |
129 | } |
130 | |
131 | pub fn get_method_def(&self, namespace: &str, name: &str) -> impl Iterator<Item = (MethodDef, &'static str)> + '_ { |
132 | self.get_item(namespace, name).filter_map(|item| if let Item::Fn(def, namespace) = item { Some((def, namespace)) } else { None }) |
133 | } |
134 | |
135 | pub fn nested_types(&self, type_def: TypeDef) -> impl Iterator<Item = TypeDef> + '_ { |
136 | self.nested.get(&type_def).map(|map| map.values().copied()).into_iter().flatten() |
137 | } |
138 | |
139 | pub fn type_from_ref(&self, code: TypeDefOrRef, enclosing: Option<TypeDef>, generics: &[Type]) -> Type { |
140 | if let TypeDefOrRef::TypeSpec(def) = code { |
141 | let mut blob = def.blob(0); |
142 | return self.type_from_blob_impl(&mut blob, None, generics); |
143 | } |
144 | |
145 | let mut full_name = code.type_name(); |
146 | |
147 | // TODO: remove this |
148 | for (known_name, kind) in CORE_TYPES { |
149 | if full_name == known_name { |
150 | return kind; |
151 | } |
152 | } |
153 | |
154 | // TODO: remove this |
155 | for (from, to) in REMAP_TYPES { |
156 | if full_name == from { |
157 | full_name = to; |
158 | break; |
159 | } |
160 | } |
161 | |
162 | if let Some(outer) = enclosing { |
163 | if full_name.namespace.is_empty() { |
164 | let nested = &self.nested[&outer]; |
165 | let Some(inner) = nested.get(full_name.name) else { |
166 | panic!("Nested type not found: {}. {}" , outer.type_name(), full_name.name); |
167 | }; |
168 | return Type::TypeDef(*inner, Vec::new()); |
169 | } |
170 | } |
171 | |
172 | if let Some(def) = self.get_type_def(full_name.namespace, full_name.name).next() { |
173 | Type::TypeDef(def, Vec::new()) |
174 | } else { |
175 | Type::TypeRef(full_name) |
176 | } |
177 | } |
178 | |
179 | pub fn type_from_blob(&self, blob: &mut Blob, enclosing: Option<TypeDef>, generics: &[Type]) -> Type { |
180 | // Used by WinRT to indicate that a struct input parameter is passed by reference rather than by value on the ABI. |
181 | let is_const = blob.read_modifiers().iter().any(|def| def.type_name() == TypeName::IsConst); |
182 | |
183 | // Used by WinRT to indicate an output parameter, but there are other ways to determine this direction so here |
184 | // it is only used to distinguish between slices and heap-allocated arrays. |
185 | let is_ref = blob.read_expected(ELEMENT_TYPE_BYREF as usize); |
186 | |
187 | if blob.read_expected(ELEMENT_TYPE_VOID as usize) { |
188 | return Type::Void; |
189 | } |
190 | |
191 | let is_array = blob.read_expected(ELEMENT_TYPE_SZARRAY as usize); // Used by WinRT to indicate an array |
192 | |
193 | let mut pointers = 0; |
194 | |
195 | while blob.read_expected(ELEMENT_TYPE_PTR as usize) { |
196 | pointers += 1; |
197 | } |
198 | |
199 | let kind = self.type_from_blob_impl(blob, enclosing, generics); |
200 | |
201 | if pointers > 0 { |
202 | Type::MutPtr(Box::new(kind), pointers) |
203 | } else if is_const { |
204 | Type::ConstRef(Box::new(kind)) |
205 | } else if is_array { |
206 | if is_ref { |
207 | Type::WinrtArrayRef(Box::new(kind)) |
208 | } else { |
209 | Type::WinrtArray(Box::new(kind)) |
210 | } |
211 | } else { |
212 | kind |
213 | } |
214 | } |
215 | |
216 | fn type_from_blob_impl(&self, blob: &mut Blob, enclosing: Option<TypeDef>, generics: &[Type]) -> Type { |
217 | let code = blob.read_usize(); |
218 | |
219 | if let Some(code) = Type::from_code(code) { |
220 | return code; |
221 | } |
222 | |
223 | match code as u8 { |
224 | ELEMENT_TYPE_VALUETYPE | ELEMENT_TYPE_CLASS => self.type_from_ref(TypeDefOrRef::decode(blob.file, blob.read_usize()), enclosing, generics), |
225 | ELEMENT_TYPE_VAR => generics.get(blob.read_usize()).unwrap_or(&Type::Void).clone(), |
226 | ELEMENT_TYPE_ARRAY => { |
227 | let kind = self.type_from_blob(blob, enclosing, generics); |
228 | let _rank = blob.read_usize(); |
229 | let _count = blob.read_usize(); |
230 | let bounds = blob.read_usize(); |
231 | Type::Win32Array(Box::new(kind), bounds) |
232 | } |
233 | ELEMENT_TYPE_GENERICINST => { |
234 | blob.read_usize(); // ELEMENT_TYPE_VALUETYPE or ELEMENT_TYPE_CLASS |
235 | |
236 | let type_name = TypeDefOrRef::decode(blob.file, blob.read_usize()).type_name(); |
237 | let def = self.get_type_def(type_name.namespace, type_name.name).next().unwrap_or_else(|| panic!("Type not found: {}" , type_name)); |
238 | let mut args = Vec::with_capacity(blob.read_usize()); |
239 | |
240 | for _ in 0..args.capacity() { |
241 | args.push(self.type_from_blob_impl(blob, enclosing, generics)); |
242 | } |
243 | |
244 | Type::TypeDef(def, args) |
245 | } |
246 | rest => unimplemented!(" {rest:?}" ), |
247 | } |
248 | } |
249 | } |
250 | |
251 | // TODO: this should be in riddle's Rust generator if at all - perhaps as convertible types rather than remapped types since there's already some precedent for that. |
252 | pub const REMAP_TYPES: [(TypeName, TypeName); 2] = [(TypeName::D2D_MATRIX_3X2_F, TypeName::Matrix3x2), (TypeName::D3DMATRIX, TypeName::Matrix4x4)]; |
253 | |
254 | // TODO: get rid of at least the second tuple if not the whole thing. |
255 | pub const CORE_TYPES: [(TypeName, Type); 11] = [(TypeName::GUID, Type::GUID), (TypeName::IUnknown, Type::IUnknown), (TypeName::HResult, Type::HRESULT), (TypeName::HRESULT, Type::HRESULT), (TypeName::HSTRING, Type::String), (TypeName::BSTR, Type::BSTR), (TypeName::IInspectable, Type::IInspectable), (TypeName::PSTR, Type::PSTR), (TypeName::PWSTR, Type::PWSTR), (TypeName::Type, Type::Type), (TypeName::CHAR, Type::U8)]; |
256 | |