1use super::*;
2use std::path::{Path, PathBuf};
3
4mod remap;
5pub use remap::Remapper;
6
7pub struct Error(String);
9
10impl Error {
11 fn new(message: impl Into<String>) -> Self {
12 Self(message.into())
13 }
14}
15
16impl std::error::Error for Error {}
17
18impl std::fmt::Debug for Error {
19 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
20 std::fmt::Display::fmt(self, f)
21 }
22}
23
24impl std::fmt::Display for Error {
25 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
26 write!(f, "\nerror: {}", self.0)
27 }
28}
29
30#[derive(Default)]
32pub struct Merger {
33 input: Vec<PathBuf>,
34 arch_inputs: Vec<(PathBuf, i32)>,
36 output: PathBuf,
37 union_enums: bool,
38}
39
40impl Merger {
41 pub fn new() -> Self {
43 Self::default()
44 }
45
46 pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
48 self.input.push(input.as_ref().to_path_buf());
49 self
50 }
51
52 pub fn inputs<I, S>(&mut self, inputs: I) -> &mut Self
54 where
55 I: IntoIterator<Item = S>,
56 S: AsRef<Path>,
57 {
58 for input in inputs {
59 self.input(input);
60 }
61 self
62 }
63
64 pub fn arch_input(&mut self, path: impl AsRef<Path>, arch: i32) -> &mut Self {
66 self.arch_inputs.push((path.as_ref().to_path_buf(), arch));
67 self
68 }
69
70 pub fn union_enums(&mut self) -> &mut Self {
77 self.union_enums = true;
78 self
79 }
80
81 pub fn output(&mut self, output: impl AsRef<Path>) -> &mut Self {
83 self.output = output.as_ref().to_path_buf();
84 self
85 }
86
87 pub fn merge(&self) -> Result<(), Error> {
92 if self.output.as_os_str().is_empty() {
93 return Err(Error::new("output is required"));
94 }
95
96 let name = self
97 .output
98 .file_stem()
99 .and_then(|s| s.to_str())
100 .ok_or_else(|| {
101 Error::new(format!("invalid output path `{}`", self.output.display()))
102 })?;
103
104 let files = read_inputs(&self.input)?;
105 let index = reader::Index::new(files);
106
107 let mut file = writer::File::new(name);
108
109 if self.union_enums {
110 let mut groups: BTreeMap<(String, String), Vec<reader::TypeDef<'_>>> = BTreeMap::new();
111 for ty in index.types() {
112 groups
113 .entry((ty.namespace().to_string(), ty.name().to_string()))
114 .or_default()
115 .push(ty);
116 }
117
118 for copies in groups.values() {
119 if copies
123 .iter()
124 .all(|ty| ty.category() == reader::TypeCategory::Class)
125 {
126 write_class_union(&mut file, &index, copies);
127 continue;
128 }
129
130 let mut by_arch: BTreeMap<i32, Vec<reader::TypeDef<'_>>> = BTreeMap::new();
134 for copy in copies {
135 by_arch
136 .entry(type_arch_bits(*copy))
137 .or_default()
138 .push(*copy);
139 }
140
141 for arch_copies in by_arch.values() {
142 if arch_copies
143 .iter()
144 .all(|ty| ty.category() == reader::TypeCategory::Enum)
145 {
146 write_enum_union(&mut file, arch_copies)?;
147 } else {
148 write_type(&mut file, &index, arch_copies[0], None, None);
152 }
153 }
154 }
155 } else {
156 let mut types: Vec<reader::TypeDef<'_>> = index.types().collect();
157 types.sort_by(|a, b| (a.namespace(), a.name()).cmp(&(b.namespace(), b.name())));
158
159 for ty in types {
160 write_type(&mut file, &index, ty, None, None);
161 }
162 }
163
164 if !self.arch_inputs.is_empty() {
165 let all_arches_mask: i32 = self.arch_inputs.iter().fold(0, |acc, (_, arch)| acc | arch);
166
167 let mut arch_groups: Vec<(reader::Index, i32)> =
168 Vec::with_capacity(self.arch_inputs.len());
169 for (path, arch_bits) in &self.arch_inputs {
170 let files = read_inputs(std::slice::from_ref(path))?;
171 arch_groups.push((reader::Index::new(files), *arch_bits));
172 }
173
174 let mut groups: BTreeMap<
175 (String, String),
176 Vec<(&reader::Index, reader::TypeDef<'_>, i32)>,
177 > = BTreeMap::new();
178 for (idx, arch_bits) in &arch_groups {
179 for ty in idx.types() {
180 groups
181 .entry((ty.namespace().to_string(), ty.name().to_string()))
182 .or_default()
183 .push((idx, ty, *arch_bits));
184 }
185 }
186
187 for copies in groups.values() {
188 let (idx, ty, _) = copies[0];
189 if ty.category() == reader::TypeCategory::Class {
190 write_type_arch_merged(&mut file, idx, ty, copies, all_arches_mask);
192 } else if let Some(signature) = merge_native_sized_callback(copies) {
193 let bits = copies.iter().fold(0, |acc, (_, _, bits)| acc | bits);
194 let arch = if bits == all_arches_mask { 0 } else { bits };
195 write_type_with_signature(
196 &mut file,
197 idx,
198 ty,
199 None,
200 Some(arch),
201 Some(&signature),
202 );
203 } else {
204 let mut by_sig: Vec<(String, &reader::Index, reader::TypeDef, i32)> = vec![];
206 for (cidx, c, bits) in copies {
207 let sig = type_sig(cidx, *c);
208 if let Some(entry) = by_sig.iter_mut().find(|(s, ..)| *s == sig) {
209 entry.3 |= *bits;
210 } else {
211 by_sig.push((sig, cidx, *c, *bits));
212 }
213 }
214 for (_, cidx, c, bits) in &by_sig {
215 let arch = if *bits == all_arches_mask { 0 } else { *bits };
216 write_type(&mut file, cidx, *c, None, Some(arch));
217 }
218 }
219 }
220 }
221
222 let bytes = file.into_stream();
223 std::fs::write(&self.output, bytes)
224 .map_err(|e| Error::new(format!("failed to write `{}`: {e}", self.output.display())))
225 }
226}
227
228fn read_inputs(inputs: &[PathBuf]) -> Result<Vec<reader::File>, Error> {
229 let mut result = vec![];
230
231 for input in inputs {
232 if input.is_dir() {
233 let prev_len = result.len();
234
235 let entries = std::fs::read_dir(input).map_err(|e| {
236 Error::new(format!(
237 "failed to read directory `{}`: {e}",
238 input.display()
239 ))
240 })?;
241
242 for entry in entries.flatten() {
243 let entry_path = entry.path();
244
245 if entry_path.is_file()
246 && entry_path
247 .extension()
248 .is_some_and(|ext| ext.eq_ignore_ascii_case("winmd"))
249 {
250 let file = reader::File::read(&entry_path).ok_or_else(|| {
251 Error::new(format!("failed to read `{}`", entry_path.display()))
252 })?;
253 result.push(file);
254 }
255 }
256
257 if result.len() == prev_len {
258 return Err(Error::new(format!(
259 "no .winmd files found in directory `{}`",
260 input.display()
261 )));
262 }
263 } else {
264 let file = reader::File::read(input)
265 .ok_or_else(|| Error::new(format!("failed to read `{}`", input.display())))?;
266 result.push(file);
267 }
268 }
269
270 Ok(result)
271}
272
273fn write_type(
275 file: &mut writer::File,
276 index: &reader::Index,
277 def: reader::TypeDef,
278 outer: Option<writer::TypeDef>,
279 arch_override: Option<i32>,
280) {
281 write_type_with_signature(file, index, def, outer, arch_override, None);
282}
283
284fn write_type_with_signature(
285 file: &mut writer::File,
286 index: &reader::Index,
287 def: reader::TypeDef,
288 outer: Option<writer::TypeDef>,
289 arch_override: Option<i32>,
290 signature_override: Option<&Signature>,
291) {
292 let extends = def
293 .extends()
294 .map(|extends| {
295 writer::TypeDefOrRef::TypeRef(file.TypeRef(extends.namespace(), extends.name()))
296 })
297 .unwrap_or_default();
298
299 debug_assert!(
300 !def.flags().is_nested() || def.namespace().is_empty(),
301 "nested type should have empty namespace"
302 );
303 debug_assert!(
304 def.flags().is_nested() || !def.namespace().is_empty(),
305 "non-nested type should have non-empty namespace"
306 );
307
308 let type_def = file.TypeDef(def.namespace(), def.name(), extends, def.flags());
309
310 if let Some(outer) = outer {
311 file.NestedClass(type_def, outer);
312 }
313
314 for field in def.fields() {
315 write_field(file, field, None);
316 }
317
318 let generics: Vec<_> = def
319 .generic_params()
320 .map(|param| Type::Generic(param.name().to_string(), param.sequence()))
321 .collect();
322
323 write_attributes_with_arch(
324 file,
325 writer::HasAttribute::TypeDef(type_def),
326 def,
327 arch_override,
328 );
329
330 for map in def.interface_impls() {
331 let interface_impl = file.InterfaceImpl(type_def, &map.interface(&generics));
332 write_attributes(
333 file,
334 writer::HasAttribute::InterfaceImpl(interface_impl),
335 map,
336 );
337 }
338
339 for generic in def.generic_params() {
340 file.GenericParam(
341 generic.name(),
342 writer::TypeOrMethodDef::TypeDef(type_def),
343 generic.sequence(),
344 generic.flags(),
345 );
346 }
347
348 let is_winrt_class = def.category() == reader::TypeCategory::Class
349 && def.flags().contains(TypeAttributes::WindowsRuntime);
350
351 if !is_winrt_class {
352 for method in def.methods() {
353 write_method_with_signature(
354 file,
355 method,
356 &generics,
357 None,
358 signature_override.filter(|_| method.name() == "Invoke"),
359 );
360 }
361 }
362
363 if let Some(class_layout) = def.class_layout() {
364 file.ClassLayout(
365 type_def,
366 class_layout.packing_size(),
367 class_layout.class_size(),
368 );
369 }
370
371 for inner_def in index.nested(def) {
372 debug_assert!(inner_def.namespace().is_empty());
373 debug_assert!(inner_def.flags().is_nested());
374 write_type(file, index, inner_def, Some(type_def), arch_override);
375 }
376}
377
378fn write_field(file: &mut writer::File, field: reader::Field, arch_override: Option<i32>) {
379 let field_def = file.Field(field.name(), &field.ty(), field.flags());
380 if let Some(constant) = field.constant() {
381 file.Constant(writer::HasConstant::Field(field_def), &constant.value());
382 }
383 write_attributes_with_arch(
384 file,
385 writer::HasAttribute::Field(field_def),
386 field,
387 arch_override,
388 );
389}
390
391fn type_arch_bits(def: reader::TypeDef) -> i32 {
393 for attribute in def.attributes() {
394 let ty = attribute.ctor().parent();
395 if ty.namespace() == "Windows.Win32.Metadata"
396 && ty.name() == "SupportedArchitectureAttribute"
397 && let Some((_, Value::I32(bits))) = attribute.value().first()
398 {
399 return *bits;
400 }
401 }
402 0
403}
404
405fn enum_member_i64(value: &Value) -> Option<i64> {
407 match value {
408 Value::U8(v) => Some(*v as i64),
409 Value::I8(v) => Some(*v as i64),
410 Value::U16(v) => Some(*v as i64),
411 Value::I16(v) => Some(*v as i64),
412 Value::U32(v) => Some(*v as i64),
413 Value::I32(v) => Some(*v as i64),
414 Value::U64(v) => Some(*v as i64),
415 Value::I64(v) => Some(*v),
416 _ => None,
417 }
418}
419
420fn write_class_union(file: &mut writer::File, index: &reader::Index, copies: &[reader::TypeDef]) {
424 let def = copies[0];
425
426 let extends = def
427 .extends()
428 .map(|extends| {
429 writer::TypeDefOrRef::TypeRef(file.TypeRef(extends.namespace(), extends.name()))
430 })
431 .unwrap_or_default();
432 let type_def = file.TypeDef(def.namespace(), def.name(), extends, def.flags());
433
434 write_attributes_with_arch(file, writer::HasAttribute::TypeDef(type_def), def, None);
435
436 let generics: Vec<_> = def
437 .generic_params()
438 .map(|param| Type::Generic(param.name().to_string(), param.sequence()))
439 .collect();
440
441 let mut seen_fields: HashSet<String> = HashSet::new();
442 for copy in copies {
443 for field in copy.fields() {
444 let value = field
445 .constant()
446 .map(|c| format!("{:?}", c.value()))
447 .unwrap_or_default();
448 let key = format!("{}|{:?}|{value}", field.name(), field.ty());
449 if seen_fields.insert(key) {
450 write_field(file, field, None);
451 }
452 }
453 }
454
455 let mut seen_methods: HashSet<String> = HashSet::new();
456 for copy in copies {
457 for method in copy.methods() {
458 let key = format!("{}|{:?}", method.name(), method.signature(&generics));
459 if seen_methods.insert(key) {
460 write_method(file, method, &generics, None);
461 }
462 }
463 }
464
465 for inner_def in index.nested(def) {
466 write_type(file, index, inner_def, Some(type_def), None);
467 }
468}
469
470fn is_max_sentinel(name: &str) -> bool {
482 name.starts_with("Max") || name.ends_with("Maximum") || name.ends_with("MaximumInformation")
483}
484
485fn write_enum_union(file: &mut writer::File, copies: &[reader::TypeDef]) -> Result<(), Error> {
495 let base = *copies
496 .iter()
497 .max_by_key(|copy| copy.fields().count())
498 .unwrap();
499
500 let base_members: HashMap<String, Value> = base
501 .fields()
502 .filter_map(|field| {
503 field
504 .constant()
505 .map(|c| (field.name().to_string(), c.value()))
506 })
507 .collect();
508
509 let mut extra_order: Vec<String> = Vec::new();
511 let mut extras: HashMap<String, reader::Field> = HashMap::new();
512
513 let conflict = |field: reader::Field| {
514 Error::new(format!(
515 "enum `{}.{}` member `{}` has conflicting values across inputs",
516 base.namespace(),
517 base.name(),
518 field.name()
519 ))
520 };
521
522 for copy in copies {
523 for field in copy.fields() {
524 let Some(constant) = field.constant() else {
525 continue;
526 };
527 let value = constant.value();
528
529 if let Some(base_value) = base_members.get(field.name()) {
530 if *base_value == value {
531 continue;
532 }
533 let tolerated = is_max_sentinel(field.name())
536 && matches!(
537 (enum_member_i64(base_value), enum_member_i64(&value)),
538 (Some(b), Some(v)) if b >= v
539 );
540 if !tolerated {
541 return Err(conflict(field));
542 }
543 continue;
544 }
545
546 match extras.get(field.name()) {
547 None => {
548 extra_order.push(field.name().to_string());
549 extras.insert(field.name().to_string(), field);
550 }
551 Some(existing) => {
552 let existing_value = existing.constant().unwrap().value();
553 if existing_value == value {
554 continue;
555 }
556 let keep_larger = is_max_sentinel(field.name())
557 && matches!(
558 (enum_member_i64(&existing_value), enum_member_i64(&value)),
559 (Some(a), Some(b)) if a != b
560 );
561 if !keep_larger {
562 return Err(conflict(field));
563 }
564 if enum_member_i64(&value) > enum_member_i64(&existing_value) {
565 extras.insert(field.name().to_string(), field);
566 }
567 }
568 }
569 }
570 }
571
572 let extends = base
573 .extends()
574 .map(|extends| {
575 writer::TypeDefOrRef::TypeRef(file.TypeRef(extends.namespace(), extends.name()))
576 })
577 .unwrap_or_default();
578
579 let type_def = file.TypeDef(base.namespace(), base.name(), extends, base.flags());
580 write_attributes_with_arch(file, writer::HasAttribute::TypeDef(type_def), base, None);
581
582 for field in base.fields() {
583 write_field(file, field, None);
584 }
585 for name in &extra_order {
586 write_field(file, extras[name], None);
587 }
588
589 Ok(())
590}
591
592fn write_method(
593 file: &mut writer::File,
594 method: reader::MethodDef,
595 generics: &[Type],
596 arch_override: Option<i32>,
597) {
598 write_method_with_signature(file, method, generics, arch_override, None);
599}
600
601fn write_method_with_signature(
602 file: &mut writer::File,
603 method: reader::MethodDef,
604 generics: &[Type],
605 arch_override: Option<i32>,
606 signature_override: Option<&Signature>,
607) {
608 let signature;
609 let signature = if let Some(signature) = signature_override {
610 signature
611 } else {
612 signature = method.signature(generics);
613 &signature
614 };
615 let method_def = file.MethodDef(
616 method.name(),
617 signature,
618 method.flags(),
619 method.impl_flags(),
620 );
621 for param_def in method.params() {
622 let param = file.Param(param_def.name(), param_def.sequence(), param_def.flags());
623 write_attributes(file, writer::HasAttribute::Param(param), param_def);
624 }
625 write_attributes_with_arch(
626 file,
627 writer::HasAttribute::MethodDef(method_def),
628 method,
629 arch_override,
630 );
631 if let Some(impl_map) = method.impl_map() {
632 file.ImplMap(
633 method_def,
634 impl_map.flags(),
635 impl_map.import_name(),
636 impl_map.import_scope().name(),
637 );
638 }
639}
640
641fn merge_native_sized_callback(
647 copies: &[(&reader::Index, reader::TypeDef, i32)],
648) -> Option<Signature> {
649 if copies.len() < 2
650 || copies
651 .iter()
652 .any(|(_, def, _)| !is_unmanaged_callback(*def))
653 {
654 return None;
655 }
656
657 let first_def = copies[0].1;
658 if copies.iter().any(|(_, def, _)| {
659 def.flags() != first_def.flags()
660 || callback_attributes(*def) != callback_attributes(first_def)
661 }) {
662 return None;
663 }
664
665 let methods: Vec<_> = copies
666 .iter()
667 .map(|(_, def, bits)| {
668 let mut methods = def.methods();
669 let method = methods.next()?;
670 (method.name() == "Invoke" && methods.next().is_none()).then_some((method, *bits))
671 })
672 .collect::<Option<_>>()?;
673
674 let first = methods[0].0;
675 if methods.iter().any(|(method, _)| {
676 method.flags() != first.flags()
677 || method.impl_flags() != first.impl_flags()
678 || callback_attributes(*method) != callback_attributes(first)
679 || callback_params(*method) != callback_params(first)
680 }) {
681 return None;
682 }
683
684 let signatures: Vec<_> = methods
685 .iter()
686 .map(|(method, bits)| (method.signature(&[]), *bits))
687 .collect();
688 let flags = signatures[0].0.flags;
689 if signatures.iter().any(|(signature, _)| {
690 signature.flags != flags || signature.types.len() != signatures[0].0.types.len()
691 }) {
692 return None;
693 }
694
695 let (return_type, mut changed) = merge_native_sized_type(
696 &signatures
697 .iter()
698 .map(|(signature, bits)| (&signature.return_type, *bits))
699 .collect::<Vec<_>>(),
700 )?;
701
702 let mut types = Vec::with_capacity(signatures[0].0.types.len());
703 for index in 0..signatures[0].0.types.len() {
704 let (ty, position_changed) = merge_native_sized_type(
705 &signatures
706 .iter()
707 .map(|(signature, bits)| (&signature.types[index], *bits))
708 .collect::<Vec<_>>(),
709 )?;
710 changed |= position_changed;
711 types.push(ty);
712 }
713
714 changed.then_some(Signature {
715 flags,
716 return_type,
717 types,
718 })
719}
720
721fn is_unmanaged_callback(def: reader::TypeDef) -> bool {
722 def.category() == reader::TypeCategory::Delegate
723 && def.attributes().any(|attribute| {
724 let ty = attribute.ctor().parent();
725 ty.namespace() == "System.Runtime.InteropServices"
726 && ty.name() == "UnmanagedFunctionPointerAttribute"
727 })
728}
729
730fn callback_params(
731 method: reader::MethodDef,
732) -> Vec<(
733 String,
734 u16,
735 ParamAttributes,
736 Vec<(String, String, Vec<(String, Value)>)>,
737)> {
738 method
739 .params()
740 .map(|param| {
741 (
742 param.name().to_string(),
743 param.sequence(),
744 param.flags(),
745 callback_attributes(param),
746 )
747 })
748 .collect()
749}
750
751fn callback_attributes<'a, R: HasAttributes<'a>>(
752 row: R,
753) -> Vec<(String, String, Vec<(String, Value)>)> {
754 row.attributes()
755 .filter_map(|attribute| {
756 let ty = attribute.ctor().parent();
757 (!(ty.namespace() == "Windows.Win32.Metadata"
758 && ty.name() == "SupportedArchitectureAttribute"))
759 .then(|| {
760 (
761 ty.namespace().to_string(),
762 ty.name().to_string(),
763 attribute.value(),
764 )
765 })
766 })
767 .collect()
768}
769
770fn merge_native_sized_type(copies: &[(&Type, i32)]) -> Option<(Type, bool)> {
771 let first = copies.first()?.0;
772 if copies.iter().all(|(ty, _)| *ty == first) {
773 return Some((first.clone(), false));
774 }
775
776 if copies.iter().any(|(ty, _)| **ty == Type::ISize)
777 && copies
778 .iter()
779 .all(|(ty, bits)| native_signed_compatible(ty, *bits))
780 {
781 return Some((Type::ISize, true));
782 }
783 if copies.iter().any(|(ty, _)| **ty == Type::USize)
784 && copies
785 .iter()
786 .all(|(ty, bits)| native_unsigned_compatible(ty, *bits))
787 {
788 return Some((Type::USize, true));
789 }
790 None
791}
792
793fn native_signed_compatible(ty: &Type, bits: i32) -> bool {
794 matches!(ty, Type::ISize)
795 || matches!(
796 (ty, pointer_width(bits)),
797 (Type::I32, Some(32)) | (Type::I64, Some(64))
798 )
799}
800
801fn native_unsigned_compatible(ty: &Type, bits: i32) -> bool {
802 matches!(ty, Type::USize)
803 || matches!(
804 (ty, pointer_width(bits)),
805 (Type::U32, Some(32)) | (Type::U64, Some(64))
806 )
807}
808
809fn pointer_width(bits: i32) -> Option<u8> {
810 match bits {
811 1 => Some(32),
812 2 | 4 => Some(64),
813 _ => None,
814 }
815}
816
817fn write_type_arch_merged(
819 file: &mut writer::File,
820 index: &reader::Index,
821 def: reader::TypeDef,
822 copies: &[(&reader::Index, reader::TypeDef, i32)],
823 all_mask: i32,
824) {
825 let extends = def
826 .extends()
827 .map(|e| writer::TypeDefOrRef::TypeRef(file.TypeRef(e.namespace(), e.name())))
828 .unwrap_or_default();
829 let type_def = file.TypeDef(def.namespace(), def.name(), extends, def.flags());
830
831 let generics: Vec<_> = def
832 .generic_params()
833 .map(|p| Type::Generic(p.name().to_string(), p.sequence()))
834 .collect();
835
836 write_attributes_with_arch(file, writer::HasAttribute::TypeDef(type_def), def, Some(0));
837 for map in def.interface_impls() {
838 let interface_impl = file.InterfaceImpl(type_def, &map.interface(&generics));
839 write_attributes(
840 file,
841 writer::HasAttribute::InterfaceImpl(interface_impl),
842 map,
843 );
844 }
845 for generic in def.generic_params() {
846 file.GenericParam(
847 generic.name(),
848 writer::TypeOrMethodDef::TypeDef(type_def),
849 generic.sequence(),
850 generic.flags(),
851 );
852 }
853
854 let mut fields: BTreeMap<String, (reader::Field, i32)> = BTreeMap::new();
856 for (_, ty, bits) in copies {
857 for field in ty.fields() {
858 let val = field
859 .constant()
860 .map(|c| format!("{:?}", c.value()))
861 .unwrap_or_default();
862 let key = format!("{}|{:?}|{val}", field.name(), field.ty());
863 fields.entry(key).or_insert((field, 0)).1 |= bits;
864 }
865 }
866 for (field, bits) in fields.into_values() {
867 write_field(file, field, Some(if bits == all_mask { 0 } else { bits }));
868 }
869
870 let is_winrt_class = def.category() == reader::TypeCategory::Class
871 && def.flags().contains(TypeAttributes::WindowsRuntime);
872 if !is_winrt_class {
873 let mut methods: BTreeMap<String, (reader::MethodDef, i32)> = BTreeMap::new();
874 for (_, ty, bits) in copies {
875 for method in ty.methods() {
876 let key = format!("{}|{:?}", method.name(), method.signature(&generics));
877 methods.entry(key).or_insert((method, 0)).1 |= bits;
878 }
879 }
880 for (method, bits) in methods.into_values() {
881 write_method(
882 file,
883 method,
884 &generics,
885 Some(if bits == all_mask { 0 } else { bits }),
886 );
887 }
888 }
889
890 if let Some(class_layout) = def.class_layout() {
891 file.ClassLayout(
892 type_def,
893 class_layout.packing_size(),
894 class_layout.class_size(),
895 );
896 }
897
898 for inner_def in index.nested(def) {
899 write_type(file, index, inner_def, Some(type_def), Some(0));
900 }
901}
902
903fn type_sig(index: &reader::Index, def: reader::TypeDef) -> String {
906 let fields: Vec<String> = def
907 .fields()
908 .map(|f| {
909 let val = f
910 .constant()
911 .map(|c| format!("{:?}", c.value()))
912 .unwrap_or_default();
913 format!("{}:{:?}={val}", f.name(), f.ty())
914 })
915 .collect();
916 let methods: Vec<String> = def
917 .methods()
918 .map(|m| format!("{}:{:?}", m.name(), m.signature(&[])))
919 .collect();
920 let layout = def
921 .class_layout()
922 .map(|l| (l.packing_size(), l.class_size()));
923 let align = def
924 .find_attribute("AlignmentAttribute")
925 .map(|a| format!("{:?}", a.value()));
926 let nested: Vec<String> = index
928 .nested(def)
929 .map(|inner| format!("{}={}", inner.name(), type_sig(index, inner)))
930 .collect();
931 format!(
932 "{fields:?}|{methods:?}|{layout:?}|{align:?}|{:?}|{nested:?}",
933 def.flags()
934 )
935}
936
937fn write_attributes<'a, R: HasAttributes<'a>>(
938 file: &mut writer::File,
939 parent: writer::HasAttribute,
940 row: R,
941) {
942 write_attributes_with_arch(file, parent, row, None);
943}
944
945fn write_attributes_with_arch<'a, R: HasAttributes<'a>>(
947 file: &mut writer::File,
948 parent: writer::HasAttribute,
949 row: R,
950 arch_override: Option<i32>,
951) {
952 for attribute in row.attributes() {
953 let ctor = attribute.ctor();
954 let ty = ctor.parent();
955
956 if arch_override.is_some()
957 && ty.namespace() == "Windows.Win32.Metadata"
958 && ty.name() == "SupportedArchitectureAttribute"
959 {
960 continue;
961 }
962
963 let attribute_ref =
964 writer::MemberRefParent::TypeRef(file.TypeRef(ty.namespace(), ty.name()));
965
966 let ctor_ref = file.MemberRef(".ctor", &ctor.signature(&[]), attribute_ref);
967
968 file.Attribute(
969 parent,
970 writer::AttributeType::MemberRef(ctor_ref),
971 &attribute.value(),
972 );
973 }
974
975 if let Some(arch_bits) = arch_override
976 && arch_bits != 0
977 {
978 write_supported_architecture_attr(file, parent, arch_bits);
979 }
980}
981
982fn write_supported_architecture_attr(
983 file: &mut writer::File,
984 parent: writer::HasAttribute,
985 arch_bits: i32,
986) {
987 let ns = "Windows.Win32.Metadata";
988 let name = "SupportedArchitectureAttribute";
989
990 let type_ref = writer::MemberRefParent::TypeRef(file.TypeRef(ns, name));
991
992 let sig = Signature {
993 flags: MethodCallAttributes::HASTHIS,
994 return_type: Type::Void,
995 types: vec![Type::I32],
996 };
997
998 let ctor_ref = file.MemberRef(".ctor", &sig, type_ref);
999
1000 file.Attribute(
1001 parent,
1002 writer::AttributeType::MemberRef(ctor_ref),
1003 &[(String::new(), Value::I32(arch_bits))],
1004 );
1005}