Skip to main content

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 FieldLayout(&mut self, field: id::Field, offset: u32) {
321        self.records.FieldLayout.push(rec::FieldLayout {
322            Offset: offset,
323            Field: field.0,
324        })
325    }
326
327    pub fn NestedClass(&mut self, inner: id::TypeDef, outer: id::TypeDef) {
328        debug_assert!(inner.0 > outer.0);
329
330        self.records.NestedClass.push(rec::NestedClass {
331            NestedClass: inner.0,
332            EnclosingClass: outer.0,
333        })
334    }
335
336    pub fn InterfaceImpl(&mut self, class: id::TypeDef, interface: &Type) -> id::InterfaceImpl {
337        let Type::Name(interface) = interface else {
338            panic!("invalid interfae type");
339        };
340
341        let interface = if interface.generics.is_empty() {
342            TypeDefOrRef::TypeRef(self.TypeRef(&interface.namespace, &interface.name))
343        } else {
344            TypeDefOrRef::TypeSpec(self.TypeSpec(
345                &interface.namespace,
346                &interface.name,
347                &interface.generics,
348            ))
349        };
350
351        id::InterfaceImpl(self.records.InterfaceImpl.push_pos(rec::InterfaceImpl {
352            Class: class,
353            Interface: interface,
354        }))
355    }
356
357    /// Encodes the `Type` in the buffer. Any required `TypeRef` rows will be added to the file, returning the blob offset.
358    fn Type(&mut self, ty: &Type, buffer: &mut Vec<u8>) {
359        match ty {
360            Type::Void => buffer.push(ELEMENT_TYPE_VOID),
361            Type::Bool => buffer.push(ELEMENT_TYPE_BOOLEAN),
362            Type::Char => buffer.push(ELEMENT_TYPE_CHAR),
363            Type::I8 => buffer.push(ELEMENT_TYPE_I1),
364            Type::U8 => buffer.push(ELEMENT_TYPE_U1),
365            Type::I16 => buffer.push(ELEMENT_TYPE_I2),
366            Type::U16 => buffer.push(ELEMENT_TYPE_U2),
367            Type::I32 => buffer.push(ELEMENT_TYPE_I4),
368            Type::U32 => buffer.push(ELEMENT_TYPE_U4),
369            Type::I64 => buffer.push(ELEMENT_TYPE_I8),
370            Type::U64 => buffer.push(ELEMENT_TYPE_U8),
371            Type::F32 => buffer.push(ELEMENT_TYPE_R4),
372            Type::F64 => buffer.push(ELEMENT_TYPE_R8),
373            Type::ISize => buffer.push(ELEMENT_TYPE_I),
374            Type::USize => buffer.push(ELEMENT_TYPE_U),
375            Type::String => buffer.push(ELEMENT_TYPE_STRING),
376            Type::Object => buffer.push(ELEMENT_TYPE_OBJECT),
377
378            Type::Array(ty) => {
379                buffer.push(ELEMENT_TYPE_SZARRAY);
380                self.Type(ty, buffer);
381            }
382
383            Type::ArrayRef(ty) => {
384                buffer.push(ELEMENT_TYPE_BYREF);
385                buffer.push(ELEMENT_TYPE_SZARRAY);
386                self.Type(ty, buffer);
387            }
388
389            Type::RefMut(ty) => {
390                buffer.push(ELEMENT_TYPE_BYREF);
391                self.Type(ty, buffer);
392            }
393
394            Type::RefConst(ty) => {
395                buffer.write_compressed(ELEMENT_TYPE_CMOD_REQD as usize);
396                let pos = self.TypeRef("System.Runtime.CompilerServices", "IsConst");
397                buffer.write_compressed(TypeDefOrRef::TypeRef(pos).encode() as usize);
398                buffer.push(ELEMENT_TYPE_BYREF);
399                self.Type(ty, buffer);
400            }
401
402            Type::PtrMut(ty, pointers) => {
403                for _ in 0..*pointers {
404                    buffer.write_compressed(ELEMENT_TYPE_PTR as usize);
405                }
406
407                self.Type(ty, buffer);
408            }
409
410            Type::PtrConst(ty, pointers) => {
411                buffer.write_compressed(ELEMENT_TYPE_CMOD_REQD as usize);
412                let pos = self.TypeRef("System.Runtime.CompilerServices", "IsConst");
413                buffer.write_compressed(TypeDefOrRef::TypeRef(pos).encode() as usize);
414
415                for _ in 0..*pointers {
416                    buffer.write_compressed(ELEMENT_TYPE_PTR as usize);
417                }
418
419                self.Type(ty, buffer);
420            }
421
422            Type::ArrayFixed(ty, len) => {
423                // See II.23.2.13 ArrayShape
424                buffer.push(ELEMENT_TYPE_ARRAY);
425                self.Type(ty, buffer);
426                buffer.write_compressed(1); // rank
427                buffer.write_compressed(1); // num_sizes
428                buffer.write_compressed(*len); // size
429                buffer.write_compressed(0); // num_lo_bounds
430            }
431
432            Type::Generic(number) => {
433                buffer.push(ELEMENT_TYPE_VAR);
434                buffer.write_compressed((*number) as usize);
435            }
436
437            Type::Name(ty) => self.TypeName(&ty.namespace, &ty.name, &ty.generics, buffer),
438            Type::AttributeEnum => buffer.push(0x55),
439        }
440    }
441
442    fn TypeName(&mut self, namespace: &str, name: &str, generics: &[Type], buffer: &mut Vec<u8>) {
443        if !generics.is_empty() {
444            buffer.push(ELEMENT_TYPE_GENERICINST);
445        }
446
447        let pos = self.TypeRef(namespace, name);
448        // Technically this should be ELEMENT_TYPE_CLASS if the type is not a value type but that requires more contextual information.
449        // TODO: we could replace Type::Name with Type::Value and Type::Class to provide this context if needed.
450        buffer.push(ELEMENT_TYPE_VALUETYPE);
451        buffer.write_compressed(TypeDefOrRef::TypeRef(pos).encode() as usize);
452
453        if !generics.is_empty() {
454            buffer.write_compressed(generics.len());
455
456            for ty in generics {
457                self.Type(ty, buffer);
458            }
459        }
460    }
461
462    /// Writes the `Type` into a `FileSig` buffer and stores it in the file, returning the blob offset.
463    fn FieldSig(&mut self, ty: &Type) -> id::BlobId {
464        let mut buffer = vec![0x6]; // FIELD
465        self.Type(ty, &mut buffer);
466        self.blobs.insert(&buffer)
467    }
468
469    /// Writes the method signature into a `MethodDefSig` buffer and stores it in the file, returning the blob offset.
470    fn MethodDefSig(&mut self, signature: &Signature) -> id::BlobId {
471        let mut buffer = vec![signature.flags.0];
472        buffer.write_compressed(signature.types.len());
473        self.Type(&signature.return_type, &mut buffer);
474
475        for ty in &signature.types {
476            self.Type(ty, &mut buffer);
477        }
478
479        self.blobs.insert(&buffer)
480    }
481
482    fn ConstantValue(&mut self, value: &Value) -> id::BlobId {
483        let mut buffer = vec![];
484        buffer.write_value(value);
485        self.blobs.insert(&buffer)
486    }
487
488    fn AttributeValue(&mut self, values: &[(String, Value)]) -> id::BlobId {
489        let mut buffer = vec![];
490        buffer.write_u16(1); // prolog
491
492        let mut count = 0;
493
494        for (name, value) in values {
495            if name.is_empty() {
496                count += 1;
497                buffer.write_value(value);
498            } else {
499                break;
500            }
501        }
502
503        buffer.write_u16((values.len() - count).try_into().unwrap());
504
505        for (name, value) in &values[count..] {
506            buffer.push(0x53); // field=0x53 property=0x54
507            buffer.push(value.ty().code());
508
509            if let Value::AttributeEnum(type_name, _) = value {
510                buffer.write_compressed(type_name.len());
511                buffer.extend_from_slice(type_name.as_bytes());
512            }
513
514            buffer.write_compressed(name.len());
515            buffer.extend_from_slice(name.as_bytes());
516            buffer.write_value(value);
517        }
518
519        self.blobs.insert(&buffer)
520    }
521}