1 | mod blobs; |
2 | mod codes; |
3 | mod file; |
4 | mod strings; |
5 | mod tables; |
6 | mod traits; |
7 | mod r#type; |
8 | |
9 | use super::*; |
10 | use blobs::Blobs; |
11 | pub use codes::*; |
12 | pub use r#type::*; |
13 | use std::collections::HashMap; |
14 | use strings::Strings; |
15 | pub use tables::*; |
16 | use traits::*; |
17 | |
18 | pub struct Writer { |
19 | pub blobs: Blobs, |
20 | pub strings: Strings, |
21 | pub tables: Tables, |
22 | pub scopes: HashMap<String, u32>, |
23 | // TODO: is this faster than jsut using a single HashMap with a (String,String) key? |
24 | pub type_refs: HashMap<String, HashMap<String, u32>>, |
25 | pub type_specs: HashMap<Type, u32>, |
26 | } |
27 | |
28 | impl Writer { |
29 | pub fn new(name: &str) -> Self { |
30 | let mut writer = Self { |
31 | blobs: Default::default(), |
32 | strings: Default::default(), |
33 | tables: Default::default(), |
34 | scopes: Default::default(), |
35 | type_refs: Default::default(), |
36 | type_specs: Default::default(), |
37 | }; |
38 | |
39 | writer.tables.TypeDef.push(TypeDef { TypeName: writer.strings.insert("<Module>" ), ..Default::default() }); |
40 | |
41 | let name = name.rsplit_once(&['/' , ' \\' ]).map_or(name, |(_, name)| name); |
42 | |
43 | writer.tables.Module.push(Module { Name: writer.strings.insert(name), Mvid: 1, ..Default::default() }); |
44 | |
45 | let name = name.rsplit_once('.' ).map_or(name, |(_, name)| name); |
46 | |
47 | writer.tables.Assembly.push(Assembly { |
48 | Name: writer.strings.insert(name), |
49 | HashAlgId: 0x00008004, |
50 | MajorVersion: 0xFF, |
51 | MinorVersion: 0xFF, |
52 | BuildNumber: 0xFF, |
53 | RevisionNumber: 0xFF, |
54 | Flags: metadata::AssemblyFlags::WindowsRuntime.0, |
55 | ..Default::default() |
56 | }); |
57 | |
58 | // Some winmd parsers will fail to read without an `mscorlib` reference. The `insert_module_types` function will typically include it |
59 | // automatically but a minimal `Module` tree may not add this dependency. |
60 | writer.insert_scope("System" ); |
61 | |
62 | writer |
63 | } |
64 | |
65 | pub fn into_stream(self) -> Vec<u8> { |
66 | file::write(self.tables.into_stream(), self.strings.into_stream(), self.blobs.into_stream()) |
67 | } |
68 | |
69 | pub fn insert_method_sig(&mut self, call_flags: metadata::MethodCallAttributes, return_type: &Type, param_types: &[Type]) -> u32 { |
70 | let mut blob = vec![call_flags.0]; |
71 | usize_blob(param_types.len(), &mut blob); |
72 | self.type_blob(return_type, &mut blob); |
73 | |
74 | for ty in param_types { |
75 | self.type_blob(ty, &mut blob); |
76 | } |
77 | |
78 | self.blobs.insert(&blob) |
79 | } |
80 | |
81 | pub fn insert_field_sig(&mut self, ty: &Type) -> u32 { |
82 | // TODO: can either cache in Writer, like we do for scopes and type_refs, or regenerate each time. |
83 | // Profile once we can stress test this with field/method signatures. |
84 | |
85 | let mut blob = vec![0x6]; // FIELD |
86 | self.type_blob(ty, &mut blob); |
87 | |
88 | self.blobs.insert(&blob) |
89 | } |
90 | |
91 | fn insert_scope(&mut self, namespace: &str) -> u32 { |
92 | if let Some(scope) = self.scopes.get(namespace) { |
93 | *scope |
94 | } else if namespace == "System" { |
95 | let scope = ResolutionScope::AssemblyRef(self.tables.AssemblyRef.push2(AssemblyRef { |
96 | Name: self.strings.insert("mscorlib" ), |
97 | MajorVersion: 4, |
98 | PublicKeyOrToken: self.blobs.insert(&[0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89]), // TODO: comment on this |
99 | ..Default::default() |
100 | })) |
101 | .encode(); |
102 | self.scopes.insert(namespace.to_string(), scope); |
103 | scope |
104 | } else { |
105 | // TODO: may need to capture the original assembly info for external type_refs. |
106 | let scope = ResolutionScope::AssemblyRef(self.tables.AssemblyRef.push2(AssemblyRef { |
107 | Name: self.strings.insert(namespace), |
108 | MajorVersion: 0xFF, |
109 | MinorVersion: 0xFF, |
110 | BuildNumber: 0xFF, |
111 | RevisionNumber: 0xFF, |
112 | Flags: metadata::AssemblyFlags::WindowsRuntime.0, |
113 | ..Default::default() |
114 | })) |
115 | .encode(); |
116 | self.scopes.insert(namespace.to_string(), scope); |
117 | scope |
118 | } |
119 | } |
120 | |
121 | pub fn insert_type_ref(&mut self, namespace: &str, name: &str) -> u32 { |
122 | if let Some(key) = self.type_refs.get(namespace) { |
123 | if let Some(reference) = key.get(name) { |
124 | return *reference; |
125 | } |
126 | } |
127 | |
128 | let scope = self.insert_scope(namespace); |
129 | |
130 | let reference = TypeDefOrRef::TypeRef(self.tables.TypeRef.push2(TypeRef { TypeName: self.strings.insert(name), TypeNamespace: self.strings.insert(namespace), ResolutionScope: scope })).encode(); |
131 | self.type_refs.entry(namespace.to_string()).or_default().insert(name.to_string(), reference); |
132 | reference |
133 | } |
134 | |
135 | pub fn insert_type_spec(&mut self, ty: Type) -> u32 { |
136 | if let Some(key) = self.type_specs.get(&ty) { |
137 | return *key; |
138 | } |
139 | |
140 | let mut blob = vec![]; |
141 | self.type_blob(&ty, &mut blob); |
142 | let signature = self.blobs.insert(&blob); |
143 | |
144 | let reference = TypeDefOrRef::TypeSpec(self.tables.TypeSpec.push2(TypeSpec { Signature: signature })).encode(); |
145 | |
146 | self.type_specs.insert(ty, reference); |
147 | reference |
148 | } |
149 | |
150 | fn type_blob(&mut self, ty: &Type, blob: &mut Vec<u8>) { |
151 | match ty { |
152 | Type::Void => blob.push(metadata::ELEMENT_TYPE_VOID), |
153 | Type::Bool => blob.push(metadata::ELEMENT_TYPE_BOOLEAN), |
154 | Type::Char => blob.push(metadata::ELEMENT_TYPE_CHAR), |
155 | Type::I8 => blob.push(metadata::ELEMENT_TYPE_I1), |
156 | Type::U8 => blob.push(metadata::ELEMENT_TYPE_U1), |
157 | Type::I16 => blob.push(metadata::ELEMENT_TYPE_I2), |
158 | Type::U16 => blob.push(metadata::ELEMENT_TYPE_U2), |
159 | Type::I32 => blob.push(metadata::ELEMENT_TYPE_I4), |
160 | Type::U32 => blob.push(metadata::ELEMENT_TYPE_U4), |
161 | Type::I64 => blob.push(metadata::ELEMENT_TYPE_I8), |
162 | Type::U64 => blob.push(metadata::ELEMENT_TYPE_U8), |
163 | Type::F32 => blob.push(metadata::ELEMENT_TYPE_R4), |
164 | Type::F64 => blob.push(metadata::ELEMENT_TYPE_R8), |
165 | Type::ISize => blob.push(metadata::ELEMENT_TYPE_I), |
166 | Type::USize => blob.push(metadata::ELEMENT_TYPE_U), |
167 | Type::String => blob.push(metadata::ELEMENT_TYPE_STRING), |
168 | Type::IInspectable => blob.push(metadata::ELEMENT_TYPE_OBJECT), |
169 | Type::GUID => { |
170 | let code = self.insert_type_ref("System" , "Guid" ); |
171 | blob.push(metadata::ELEMENT_TYPE_VALUETYPE); |
172 | usize_blob(code as usize, blob); |
173 | } |
174 | Type::HRESULT => { |
175 | let code = self.insert_type_ref("Windows.Foundation" , "HResult" ); |
176 | blob.push(metadata::ELEMENT_TYPE_VALUETYPE); |
177 | usize_blob(code as usize, blob); |
178 | } |
179 | Type::TypeRef(ty) => { |
180 | if !ty.generics.is_empty() { |
181 | blob.push(metadata::ELEMENT_TYPE_GENERICINST); |
182 | } |
183 | let code = self.insert_type_ref(&ty.namespace, &ty.name); |
184 | blob.push(metadata::ELEMENT_TYPE_VALUETYPE); |
185 | usize_blob(code as usize, blob); |
186 | |
187 | if !ty.generics.is_empty() { |
188 | usize_blob(ty.generics.len(), blob); |
189 | |
190 | for ty in &ty.generics { |
191 | self.type_blob(ty, blob); |
192 | } |
193 | } |
194 | } |
195 | Type::BSTR => { |
196 | let code = self.insert_type_ref("Windows.Win32.Foundation" , "BSTR" ); |
197 | blob.push(metadata::ELEMENT_TYPE_VALUETYPE); |
198 | usize_blob(code as usize, blob); |
199 | } |
200 | Type::IUnknown => { |
201 | let code = self.insert_type_ref("Windows.Win32.Foundation" , "IUnknown" ); |
202 | blob.push(metadata::ELEMENT_TYPE_VALUETYPE); |
203 | usize_blob(code as usize, blob); |
204 | } |
205 | Type::PCWSTR | Type::PWSTR => { |
206 | let code = self.insert_type_ref("Windows.Win32.Foundation" , "PWSTR" ); |
207 | blob.push(metadata::ELEMENT_TYPE_VALUETYPE); |
208 | usize_blob(code as usize, blob); |
209 | } |
210 | Type::PCSTR | Type::PSTR => { |
211 | let code = self.insert_type_ref("Windows.Win32.Foundation" , "PSTR" ); |
212 | blob.push(metadata::ELEMENT_TYPE_VALUETYPE); |
213 | usize_blob(code as usize, blob); |
214 | } |
215 | Type::ConstRef(ty) => { |
216 | usize_blob(metadata::ELEMENT_TYPE_CMOD_OPT as usize, blob); |
217 | usize_blob(self.insert_type_ref("System.Runtime.CompilerServices" , "IsConst" ) as usize, blob); |
218 | usize_blob(metadata::ELEMENT_TYPE_BYREF as usize, blob); |
219 | self.type_blob(ty, blob); |
220 | } |
221 | Type::WinrtArrayRef(ty) => { |
222 | usize_blob(metadata::ELEMENT_TYPE_BYREF as usize, blob); |
223 | usize_blob(metadata::ELEMENT_TYPE_SZARRAY as usize, blob); |
224 | self.type_blob(ty, blob); |
225 | } |
226 | Type::WinrtArray(ty) => { |
227 | usize_blob(metadata::ELEMENT_TYPE_SZARRAY as usize, blob); |
228 | self.type_blob(ty, blob); |
229 | } |
230 | Type::Win32Array(ty, bounds) => { |
231 | usize_blob(metadata::ELEMENT_TYPE_ARRAY as usize, blob); |
232 | self.type_blob(ty, blob); |
233 | usize_blob(1, blob); // rank |
234 | usize_blob(1, blob); // count |
235 | usize_blob(*bounds, blob); |
236 | } |
237 | Type::Type => { |
238 | let code = self.insert_type_ref("System" , "Type" ); |
239 | blob.push(metadata::ELEMENT_TYPE_CLASS); |
240 | usize_blob(code as usize, blob); |
241 | } |
242 | Type::MutPtr(ty, pointers) | Type::ConstPtr(ty, pointers) => { |
243 | for _ in 0..*pointers { |
244 | usize_blob(metadata::ELEMENT_TYPE_PTR as usize, blob); |
245 | } |
246 | self.type_blob(ty, blob); |
247 | } |
248 | Type::GenericParam(index) => { |
249 | blob.push(metadata::ELEMENT_TYPE_VAR); |
250 | usize_blob(*index as usize, blob); |
251 | } |
252 | } |
253 | } |
254 | } |
255 | |
256 | fn round(size: usize, round: usize) -> usize { |
257 | let round: usize = round - 1; |
258 | (size + round) & !round |
259 | } |
260 | |
261 | fn usize_blob(value: usize, blob: &mut Vec<u8>) { |
262 | // See II.23.2 in ECMA-335 |
263 | assert!(value < 0x20000000); |
264 | |
265 | if value < 0x80 { |
266 | blob.push(value as u8); |
267 | } else if value < 0x4000 { |
268 | blob.push((0x80 | (value & 0x3F00) >> 8) as u8); |
269 | blob.push((value & 0xFF) as u8); |
270 | } else { |
271 | blob.push((0xC0 | (value & 0x1F000000) >> 24) as u8); |
272 | blob.push(((value & 0xFF0000) >> 16) as u8); |
273 | blob.push(((value & 0xFF00) >> 8) as u8); |
274 | blob.push((value & 0xFF) as u8); |
275 | } |
276 | } |
277 | |
278 | // #[cfg(test)] |
279 | // mod tests { |
280 | // use super::*; |
281 | |
282 | // #[test] |
283 | // fn test_usize_blob() { |
284 | // let mut blob = vec![]; |
285 | // usize_blob(0, &mut blob); |
286 | // usize_blob(1, &mut blob); |
287 | // usize_blob(2, &mut blob); |
288 | |
289 | // usize_blob(0x80 - 2, &mut blob); |
290 | // usize_blob(0x80 - 1, &mut blob); |
291 | // usize_blob(0x80, &mut blob); |
292 | // usize_blob(0x80 + 1, &mut blob); |
293 | // usize_blob(0x80 + 2, &mut blob); |
294 | |
295 | // usize_blob(0x4000 - 2, &mut blob); |
296 | // usize_blob(0x4000 - 1, &mut blob); |
297 | // usize_blob(0x4000, &mut blob); |
298 | // usize_blob(0x4000 + 1, &mut blob); |
299 | // usize_blob(0x4000 + 2, &mut blob); |
300 | |
301 | // usize_blob(0x20000000 - 3, &mut blob); |
302 | // usize_blob(0x20000000 - 2, &mut blob); |
303 | // usize_blob(0x20000000 - 1, &mut blob); |
304 | |
305 | // let mut blob = metadata::Blob::new(0, &blob); |
306 | // assert_eq!(blob.read_usize(), 0); |
307 | // assert_eq!(blob.read_usize(), 1); |
308 | // assert_eq!(blob.read_usize(), 2); |
309 | |
310 | // assert_eq!(blob.read_usize(), 0x80 - 2); |
311 | // assert_eq!(blob.read_usize(), 0x80 - 1); |
312 | // assert_eq!(blob.read_usize(), 0x80); |
313 | // assert_eq!(blob.read_usize(), 0x80 + 1); |
314 | // assert_eq!(blob.read_usize(), 0x80 + 2); |
315 | |
316 | // assert_eq!(blob.read_usize(), 0x4000 - 2); |
317 | // assert_eq!(blob.read_usize(), 0x4000 - 1); |
318 | // assert_eq!(blob.read_usize(), 0x4000); |
319 | // assert_eq!(blob.read_usize(), 0x4000 + 1); |
320 | // assert_eq!(blob.read_usize(), 0x4000 + 2); |
321 | |
322 | // assert_eq!(blob.read_usize(), 0x20000000 - 3); |
323 | // assert_eq!(blob.read_usize(), 0x20000000 - 2); |
324 | // assert_eq!(blob.read_usize(), 0x20000000 - 1); |
325 | |
326 | // assert_eq!(blob.slice.len(), 0); |
327 | // } |
328 | // } |
329 | |