1mod blobs;
2mod codes;
3mod file;
4mod strings;
5mod tables;
6mod traits;
7mod r#type;
8
9use super::*;
10use blobs::Blobs;
11pub use codes::*;
12pub use r#type::*;
13use std::collections::HashMap;
14use strings::Strings;
15pub use tables::*;
16use traits::*;
17
18pub 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
28impl 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
256fn round(size: usize, round: usize) -> usize {
257 let round: usize = round - 1;
258 (size + round) & !round
259}
260
261fn 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