windows_metadata/writer/file/
mod.rs

1use super::*;
2mod into_stream;
3
4mod rec;
5
6mod blobs;
7use blobs::*;
8
9mod strings;
10use strings::*;
11
12mod helpers;
13use helpers::*;
14
15/// Represents an ECMA-335 file in memory so that it can be built incrementally.
16#[derive(Default)]
17pub struct File {
18    strings: Strings,
19    blobs: Blobs,
20    records: rec::Records,
21
22    // Indexes for fast lookup of preexisting rows.
23    TypeRef: HashMap<String, HashMap<String, id::TypeRef>>,
24    AssemblyRef: HashMap<String, id::AssemblyRef>,
25    ModuleRef: HashMap<String, id::ModuleRef>,
26    MemberRef: HashMap<rec::MemberRef, id::MemberRef>,
27
28    // Staging for sorted rows before these records can be written. BTreeMap is used rather than HashMap to allow reproducible builds.
29    Constant: BTreeMap<HasConstant, rec::Constant>,
30    Attribute: BTreeMap<HasAttribute, Vec<rec::Attribute>>,
31    GenericParam: BTreeMap<TypeOrMethodDef, Vec<rec::GenericParam>>,
32}
33
34impl File {
35    /// Creates a minimal ECMA-335 file representation.
36    pub fn new(name: &str) -> Self {
37        let mut file = Self::default();
38
39        // This assembly.
40        file.records.Assembly.push(rec::Assembly {
41            Name: file.strings.insert(name),
42            HashAlgId: 0x00008004,
43            MajorVersion: 0xFF,
44            MinorVersion: 0xFF,
45            BuildNumber: 0xFF,
46            RevisionNumber: 0xFF,
47            Flags: AssemblyFlags::WindowsRuntime,
48            ..Default::default()
49        });
50
51        // This module.
52        file.records.Module.push(rec::Module {
53            Name: file.strings.insert(name),
54            Mvid: 1,
55            ..Default::default()
56        });
57
58        // Some parsers will fail to read without an `mscorlib` reference implied by "System" types.
59        file.AssemblyRef("System");
60
61        // The parent type of "globals" expected by most parsers.
62        file.TypeDef("", "<Module>", TypeDefOrRef::default(), TypeAttributes(0));
63
64        file
65    }
66
67    fn ModuleRef(&mut self, name: &str) -> id::ModuleRef {
68        if let Some(pos) = self.ModuleRef.get(name) {
69            return *pos;
70        }
71
72        let pos = id::ModuleRef(self.records.ModuleRef.push_pos(rec::ModuleRef {
73            Name: self.strings.insert(name),
74        }));
75
76        self.ModuleRef.insert(name.to_string(), pos);
77        pos
78    }
79
80    pub fn ImplMap(
81        &mut self,
82        method: id::MethodDef,
83        flags: PInvokeAttributes,
84        import_name: &str,
85        import_scope: &str,
86    ) {
87        let scope = self.ModuleRef(import_scope);
88
89        self.records.ImplMap.push(rec::ImplMap {
90            MappingFlags: flags,
91            MemberForwarded: MemberForwarded::MethodDef(method),
92            ImportName: self.strings.insert(import_name),
93            ImportScope: scope,
94        })
95    }
96
97    /// Adds an `AssemblyRef` row representing the given namespace to the file, returning the row offset.
98    fn AssemblyRef(&mut self, namespace: &str) -> id::AssemblyRef {
99        // This generates a synthetic `AssemblyRef` for every root namespace, but the alternative requires a
100        // lot more contextual information which we can hopefully avoid for now.
101        let namespace = namespace
102            .split_once('.')
103            .map_or(namespace, |(prefix, _)| prefix);
104
105        if let Some(pos) = self.AssemblyRef.get(namespace) {
106            return *pos;
107        }
108
109        let pos = id::AssemblyRef(if namespace == "System" {
110            self.records.AssemblyRef.push_pos(rec::AssemblyRef {
111                Name: self.strings.insert("mscorlib"),
112                MajorVersion: 4,
113                PublicKeyOrToken: self
114                    .blobs
115                    .insert(&[0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89]),
116                ..Default::default()
117            })
118        } else {
119            self.records.AssemblyRef.push_pos(rec::AssemblyRef {
120                Name: self.strings.insert(namespace),
121                MajorVersion: 0xFF,
122                MinorVersion: 0xFF,
123                BuildNumber: 0xFF,
124                RevisionNumber: 0xFF,
125                Flags: AssemblyFlags::WindowsRuntime,
126                ..Default::default()
127            })
128        });
129
130        self.AssemblyRef.insert(namespace.to_string(), pos);
131        pos
132    }
133
134    /// Adds a `TypeDef` row to the file, returning the row offset.
135    pub fn TypeDef(
136        &mut self,
137        namespace: &str,
138        name: &str,
139        extends: TypeDefOrRef,
140        flags: TypeAttributes,
141    ) -> id::TypeDef {
142        id::TypeDef(self.records.TypeDef.push_pos(rec::TypeDef {
143            TypeName: self.strings.insert(name),
144            TypeNamespace: self.strings.insert(namespace),
145            Flags: flags,
146            Extends: extends,
147            FieldList: self.records.Field.len() as u32,
148            MethodList: self.records.MethodDef.len() as u32,
149        }))
150    }
151
152    /// Adds a `TypeRef` row to the file, returning the row offset.
153    pub fn TypeRef(&mut self, namespace: &str, name: &str) -> id::TypeRef {
154        if let Some(key) = self.TypeRef.get(namespace) {
155            if let Some(pos) = key.get(name) {
156                return *pos;
157            }
158        }
159
160        // The type may be local to the module but that requires more contextual information.
161        let scope = ResolutionScope::AssemblyRef(self.AssemblyRef(namespace));
162
163        let pos = id::TypeRef(self.records.TypeRef.push_pos(rec::TypeRef {
164            TypeName: self.strings.insert(name),
165            TypeNamespace: self.strings.insert(namespace),
166            ResolutionScope: scope,
167        }));
168
169        self.TypeRef
170            .entry(namespace.to_string())
171            .or_default()
172            .insert(name.to_string(), pos);
173
174        pos
175    }
176
177    pub fn TypeSpec(&mut self, namespace: &str, name: &str, generics: &[Type]) -> id::TypeSpec {
178        debug_assert!(!generics.is_empty());
179
180        let type_ref = self.TypeRef(namespace, name);
181
182        let mut buffer = vec![];
183        buffer.push(ELEMENT_TYPE_GENERICINST);
184        buffer.push(ELEMENT_TYPE_CLASS);
185        buffer.write_compressed(TypeDefOrRef::TypeRef(type_ref).encode() as usize);
186        buffer.write_compressed(generics.len());
187
188        for ty in generics {
189            self.Type(ty, &mut buffer);
190        }
191
192        // tODO: need to reuse here as well
193        id::TypeSpec(self.records.TypeSpec.push_pos(rec::TypeSpec {
194            Signature: self.blobs.insert(&buffer),
195        }))
196    }
197
198    /// Adds a `Field` row to the file, returning the row offset.
199    pub fn Field(&mut self, name: &str, ty: &Type, flags: FieldAttributes) -> id::Field {
200        let signature = self.FieldSig(ty);
201
202        id::Field(self.records.Field.push_pos(rec::Field {
203            Name: self.strings.insert(name),
204            Flags: flags,
205            Signature: signature,
206        }))
207    }
208
209    /// Adds a `MethodDef` row to the file, returning the row offset.
210    pub fn MethodDef(
211        &mut self,
212        name: &str,
213        signature: &Signature,
214        flags: MethodAttributes,
215        impl_flags: MethodImplAttributes,
216    ) -> id::MethodDef {
217        let signature = self.MethodDefSig(signature);
218
219        id::MethodDef(self.records.MethodDef.push_pos(rec::MethodDef {
220            RVA: 0,
221            ImplFlags: impl_flags,
222            Flags: flags,
223            Name: self.strings.insert(name),
224            Signature: signature,
225            ParamList: self.records.Param.len() as u32,
226        }))
227    }
228
229    pub fn MemberRef(
230        &mut self,
231        name: &str,
232        signature: &Signature,
233        parent: MemberRefParent,
234    ) -> id::MemberRef {
235        let signature = self.MethodDefSig(signature);
236
237        let record = rec::MemberRef {
238            Name: self.strings.insert(name),
239            Signature: signature,
240            Parent: parent,
241        };
242
243        if let Some(pos) = self.MemberRef.get(&record) {
244            return *pos;
245        }
246
247        let pos = id::MemberRef(self.records.MemberRef.push_pos(record));
248        self.MemberRef.insert(record, pos);
249        pos
250    }
251
252    /// Adds a `Param` row to the file, returning the row offset.
253    pub fn Param(&mut self, name: &str, sequence: u16, flags: ParamAttributes) -> id::Param {
254        id::Param(self.records.Param.push_pos(rec::Param {
255            Flags: flags,
256            Sequence: sequence,
257            Name: self.strings.insert(name),
258        }))
259    }
260
261    /// Adds an `Attribute` row to the file. This is a sorted table so the row offset is not yet available.
262    pub fn Attribute(
263        &mut self,
264        parent: HasAttribute,
265        ty: AttributeType,
266        value: &[(String, Value)],
267    ) {
268        let value = self.AttributeValue(value);
269
270        self.Attribute
271            .entry(parent)
272            .or_default()
273            .push(rec::Attribute {
274                Parent: parent,
275                Type: ty,
276                Value: value,
277            });
278    }
279
280    pub fn Constant(&mut self, parent: HasConstant, value: &Value) {
281        let ty = value.ty().code();
282        let value = self.ConstantValue(value);
283
284        self.Constant.insert(
285            parent,
286            rec::Constant {
287                Parent: parent,
288                Type: ty,
289                Value: value,
290            },
291        );
292    }
293
294    pub fn GenericParam(
295        &mut self,
296        name: &str,
297        owner: TypeOrMethodDef,
298        number: u16,
299        flags: GenericParamAttributes,
300    ) {
301        self.GenericParam
302            .entry(owner)
303            .or_default()
304            .push(rec::GenericParam {
305                Name: self.strings.insert(name),
306                Number: number,
307                Owner: owner,
308                Flags: flags,
309            });
310    }
311
312    pub fn ClassLayout(&mut self, parent: id::TypeDef, packing_size: u16, class_size: u32) {
313        self.records.ClassLayout.push(rec::ClassLayout {
314            PackingSize: packing_size,
315            ClassSize: class_size,
316            Parent: parent.0,
317        })
318    }
319
320    pub fn NestedClass(&mut self, inner: id::TypeDef, outer: id::TypeDef) {
321        debug_assert!(inner.0 > outer.0);
322
323        self.records.NestedClass.push(rec::NestedClass {
324            NestedClass: inner.0,
325            EnclosingClass: outer.0,
326        })
327    }
328
329    pub fn InterfaceImpl(&mut self, class: id::TypeDef, interface: &Type) -> id::InterfaceImpl {
330        let Type::Name(interface) = interface else {
331            panic!("invalid interfae type");
332        };
333
334        let interface = if interface.generics.is_empty() {
335            TypeDefOrRef::TypeRef(self.TypeRef(&interface.namespace, &interface.name))
336        } else {
337            TypeDefOrRef::TypeSpec(self.TypeSpec(
338                &interface.namespace,
339                &interface.name,
340                &interface.generics,
341            ))
342        };
343
344        id::InterfaceImpl(self.records.InterfaceImpl.push_pos(rec::InterfaceImpl {
345            Class: class,
346            Interface: interface,
347        }))
348    }
349
350    /// Encodes the `Type` in the buffer. Any required `TypeRef` rows will be added to the file, returning the blob offset.
351    fn Type(&mut self, ty: &Type, buffer: &mut Vec<u8>) {
352        match ty {
353            Type::Void => buffer.push(ELEMENT_TYPE_VOID),
354            Type::Bool => buffer.push(ELEMENT_TYPE_BOOLEAN),
355            Type::Char => buffer.push(ELEMENT_TYPE_CHAR),
356            Type::I8 => buffer.push(ELEMENT_TYPE_I1),
357            Type::U8 => buffer.push(ELEMENT_TYPE_U1),
358            Type::I16 => buffer.push(ELEMENT_TYPE_I2),
359            Type::U16 => buffer.push(ELEMENT_TYPE_U2),
360            Type::I32 => buffer.push(ELEMENT_TYPE_I4),
361            Type::U32 => buffer.push(ELEMENT_TYPE_U4),
362            Type::I64 => buffer.push(ELEMENT_TYPE_I8),
363            Type::U64 => buffer.push(ELEMENT_TYPE_U8),
364            Type::F32 => buffer.push(ELEMENT_TYPE_R4),
365            Type::F64 => buffer.push(ELEMENT_TYPE_R8),
366            Type::ISize => buffer.push(ELEMENT_TYPE_I),
367            Type::USize => buffer.push(ELEMENT_TYPE_U),
368            Type::String => buffer.push(ELEMENT_TYPE_STRING),
369            Type::Object => buffer.push(ELEMENT_TYPE_OBJECT),
370
371            Type::Array(ty) => {
372                buffer.push(ELEMENT_TYPE_SZARRAY);
373                self.Type(ty, buffer);
374            }
375
376            Type::ArrayRef(ty) => {
377                buffer.push(ELEMENT_TYPE_BYREF);
378                buffer.push(ELEMENT_TYPE_SZARRAY);
379                self.Type(ty, buffer);
380            }
381
382            Type::ConstRef(ty) => {
383                buffer.write_compressed(ELEMENT_TYPE_CMOD_REQD as usize);
384                let pos = self.TypeRef("System.Runtime.CompilerServices", "IsConst");
385                buffer.write_compressed(TypeDefOrRef::TypeRef(pos).encode() as usize);
386                self.Type(ty, buffer);
387            }
388
389            Type::PtrMut(ty, pointers) => {
390                for _ in 0..*pointers {
391                    buffer.write_compressed(ELEMENT_TYPE_PTR as usize);
392                }
393
394                self.Type(ty, buffer);
395            }
396
397            Type::PtrConst(ty, pointers) => {
398                buffer.write_compressed(ELEMENT_TYPE_CMOD_REQD as usize);
399                let pos = self.TypeRef("System.Runtime.CompilerServices", "IsConst");
400                buffer.write_compressed(TypeDefOrRef::TypeRef(pos).encode() as usize);
401
402                for _ in 0..*pointers {
403                    buffer.write_compressed(ELEMENT_TYPE_PTR as usize);
404                }
405
406                self.Type(ty, buffer);
407            }
408
409            Type::ArrayFixed(ty, len) => {
410                // See II.23.2.13 ArrayShape
411                buffer.push(ELEMENT_TYPE_ARRAY);
412                self.Type(ty, buffer);
413                buffer.write_compressed(1); // rank
414                buffer.write_compressed(1); // num_sizes
415                buffer.write_compressed(*len); // size
416                buffer.write_compressed(0); // num_lo_bounds
417            }
418
419            Type::Generic(number) => {
420                buffer.push(ELEMENT_TYPE_VAR);
421                buffer.write_compressed((*number) as usize);
422            }
423
424            Type::Name(ty) => self.TypeName(&ty.namespace, &ty.name, &ty.generics, buffer),
425            Type::AttributeEnum => buffer.push(0x55),
426        }
427    }
428
429    fn TypeName(&mut self, namespace: &str, name: &str, generics: &[Type], buffer: &mut Vec<u8>) {
430        if !generics.is_empty() {
431            buffer.push(ELEMENT_TYPE_GENERICINST);
432        }
433
434        let pos = self.TypeRef(namespace, name);
435        // Technically this should be ELEMENT_TYPE_CLASS if the type is not a value type but that requires more contextual information.
436        // TODO: we could replace Type::Name with Type::Value and Type::Class to provide this context if needed.
437        buffer.push(ELEMENT_TYPE_VALUETYPE);
438        buffer.write_compressed(TypeDefOrRef::TypeRef(pos).encode() as usize);
439
440        if !generics.is_empty() {
441            buffer.write_compressed(generics.len());
442
443            for ty in generics {
444                self.Type(ty, buffer);
445            }
446        }
447    }
448
449    /// Writes the `Type` into a `FileSig` buffer and stores it in the file, returning the blob offset.
450    fn FieldSig(&mut self, ty: &Type) -> id::BlobId {
451        let mut buffer = vec![0x6]; // FIELD
452        self.Type(ty, &mut buffer);
453        self.blobs.insert(&buffer)
454    }
455
456    /// Writes the method signature into a `MethodDefSig` buffer and stores it in the file, returning the blob offset.
457    fn MethodDefSig(&mut self, signature: &Signature) -> id::BlobId {
458        let mut buffer = vec![signature.flags.0];
459        buffer.write_compressed(signature.types.len());
460        self.Type(&signature.return_type, &mut buffer);
461
462        for ty in &signature.types {
463            self.Type(ty, &mut buffer);
464        }
465
466        self.blobs.insert(&buffer)
467    }
468
469    fn ConstantValue(&mut self, value: &Value) -> id::BlobId {
470        let mut buffer = vec![];
471        buffer.write_value(value);
472        self.blobs.insert(&buffer)
473    }
474
475    fn AttributeValue(&mut self, values: &[(String, Value)]) -> id::BlobId {
476        let mut buffer = vec![];
477        buffer.write_u16(1); // prolog
478
479        let mut count = 0;
480
481        for (name, value) in values {
482            if name.is_empty() {
483                count += 1;
484                buffer.write_value(value);
485            } else {
486                break;
487            }
488        }
489
490        buffer.write_u16((values.len() - count).try_into().unwrap());
491
492        for (name, value) in &values[count..] {
493            buffer.push(0x53); // field=0x53 property=0x54
494            buffer.push(value.ty().code());
495
496            if let Value::AttributeEnum(type_name, _) = value {
497                buffer.write_compressed(type_name.len());
498                buffer.extend_from_slice(type_name.as_bytes());
499            }
500
501            buffer.write_compressed(name.len());
502            buffer.extend_from_slice(name.as_bytes());
503            buffer.write_value(value);
504        }
505
506        self.blobs.insert(&buffer)
507    }
508}