Skip to main content

dotnetdll/resolution/
read.rs

1use super::{
2    AssemblyRefIndex, EntryPoint, ExportedTypeIndex, FieldIndex, FileIndex, MethodIndex, MethodMemberIndex,
3    MethodRefIndex, ModuleRefIndex, PropertyIndex, Resolution, TypeIndex, TypeRefIndex, lazy,
4};
5use crate::binary::{heap::*, metadata};
6use crate::convert::{self, TypeKind};
7use crate::dll::{DLL, DLLError, ParseError, ResolveError, Result, ValidityError};
8use crate::prelude::generic::{Constraint, Generic, SpecialConstraint, Variance};
9use crate::resolved::{types::MemberType, *};
10use rustc_hash::FxHashMap as HashMap;
11use scroll::Pread;
12use std::{borrow::Cow, sync::Arc};
13use tracing::{debug, warn};
14
15/// A dictionary of options for [`Resolution::parse`] and [`DLL::resolve`].
16#[derive(Debug, Default, Copy, Clone)]
17pub struct Options {
18    /// If this flag is set, [`Resolution::parse`] and [`DLL::resolve`] will not resolve the bodies of class methods,
19    /// meaning [`Method::body`](members::Method::body) will always be `None`.
20    ///
21    /// [`Default`] value of `false`.
22    pub skip_method_bodies: bool,
23
24    /// If this flag is set, method bodies are decoded on first access via
25    /// [`Resolution::method_body`] rather than eagerly during parsing.
26    ///
27    /// In lazy mode [`Method::body`](members::Method::body) is always `None` regardless of
28    /// whether the method is abstract. Use [`Resolution::method_body`] to retrieve a body;
29    /// check [`Method::abstract_member`](members::Method::abstract_member) to determine whether
30    /// a body exists at all.
31    ///
32    /// Decoding errors (malformed IL) are deferred to the first call of
33    /// [`Resolution::method_body`] rather than failing the initial parse.
34    ///
35    /// [`Default`] value of `false`.
36    pub lazy_method_bodies: bool,
37
38    /// If this flag is set, method signatures are decoded on first access via
39    /// [`Resolution::method_signature`] / [`Resolution::method_ref_signature`] rather than
40    /// eagerly during parsing.
41    ///
42    /// In lazy mode [`Method::signature`](members::Method::signature) and
43    /// [`ExternalMethodReference::signature`](members::ExternalMethodReference::signature) hold a
44    /// placeholder value (`ManagedMethod::default()`). Use [`Resolution::method_signature`] and
45    /// [`Resolution::method_ref_signature`] to retrieve the real decoded signature.
46    ///
47    /// Decoding errors are deferred to the first accessor call for that method and are not cached
48    /// — retried on the next call.
49    ///
50    /// This option is independent of `lazy_method_bodies` and can be combined with it.
51    ///
52    /// [`Default`] value of `false`.
53    pub lazy_method_signatures: bool,
54
55    /// If this flag is set, property signatures are decoded on first access via
56    /// [`Resolution::property_signature`] rather than eagerly during parsing.
57    ///
58    /// In lazy mode [`Property::property_type`](members::Property::property_type) and
59    /// [`Property::parameters`](members::Property::parameters) hold placeholder values.
60    /// Use [`Resolution::property_signature`] to retrieve the decoded signature.
61    ///
62    /// Decoding errors are deferred to the first accessor call for that property and are not
63    /// cached — retried on the next call.
64    ///
65    /// This option is independent of all other lazy options and can be combined freely.
66    ///
67    /// [`Default`] value of `false`.
68    pub lazy_property_signatures: bool,
69
70    /// If this flag is set, custom attributes are not distributed to their parent elements during
71    /// parsing. Instead, they are resolved on demand via the accessor methods
72    /// [`Resolution::type_attributes`], [`Resolution::method_attributes`],
73    /// [`Resolution::field_attributes`], and [`Resolution::assembly_attributes`].
74    ///
75    /// **What changes in lazy mode:**
76    /// - `TypeDefinition::attributes`, `Method::attributes`, `Field::attributes`, and
77    ///   `Assembly::attributes` are always **empty** — reads will silently return no attributes.
78    /// - Use the accessor methods above to retrieve attributes for any element.
79    /// - Accessor methods always return `Vec<Attribute>` (owned). In eager mode they clone from the
80    ///   already-populated field; in lazy mode they resolve and allocate on each call, but avoid the
81    ///   upfront cost of distributing all attributes at parse time.
82    ///
83    /// **What does not change:**
84    /// - Attribute *values* (the serialized blob) are already lazy-decoded in both modes — calling
85    ///   [`Attribute::instantiation_data`](crate::resolved::attribute::Attribute::instantiation_data)
86    ///   is always deferred until explicitly requested. This flag only defers the *distribution*
87    ///   (iterating the table and routing each attribute to its parent element), not the value decode.
88    /// - Elements not covered by the four accessors (e.g. `TypeReference::attributes`,
89    ///   `ExternalMethodReference::attributes`, parameter attributes, generic parameter attributes)
90    ///   are still distributed eagerly — only the four high-traffic targets are lazy.
91    ///
92    /// **Performance note:** skipping distribution saves ~8–10 % of total parse time for large
93    /// assemblies (e.g. `System.Private.CoreLib`) when only a small subset of elements are
94    /// inspected. If a workload touches attributes on most elements the savings are minimal.
95    ///
96    /// This option is independent of all other lazy options and can be combined freely.
97    ///
98    /// [`Default`] value of `false`.
99    pub lazy_attributes: bool,
100}
101
102macro_rules! heap_idx {
103    ($heap:ident, $idx:expr) => {
104        Cow::Borrowed($heap.at_index($idx)?)
105    };
106}
107
108macro_rules! optional_idx {
109    ($heap:ident, $idx:expr) => {
110        if $idx.is_null() {
111            None
112        } else {
113            Some(heap_idx!($heap, $idx))
114        }
115    };
116}
117
118macro_rules! build_version {
119    ($src:ident) => {
120        Version {
121            major: $src.major_version,
122            minor: $src.minor_version,
123            build: $src.build_number,
124            revision: $src.revision_number,
125        }
126    };
127}
128
129// this allows us to initialize the Vec out of order
130// we consider it safe because we assert that the body will fully initialize everything
131// it's much simpler and more efficient than trying to use a HashMap or something
132macro_rules! build_vec {
133    ($name:ident = $t:ty[$len:expr], $body:expr) => {
134        let mut $name = vec![std::mem::MaybeUninit::uninit(); $len];
135        $body;
136        // see transmute docs: this makes sure the original vector is not dropped
137        let mut $name = std::mem::ManuallyDrop::new($name);
138        // SAFETY: MaybeUninit<T> has the same layout as T, so the following pointer cast is legal
139        // this is just to avoid copying the Vec<MaybeUninit<T>> version into a new Vec
140        #[allow(unused_mut)]
141        let mut $name = unsafe { Vec::from_raw_parts($name.as_mut_ptr().cast::<$t>(), $name.len(), $name.capacity()) };
142    };
143}
144
145macro_rules! stage_start {
146    ($start:ident) => {
147        #[cfg(feature = "stage-timing")]
148        let $start = std::time::Instant::now();
149    };
150}
151
152macro_rules! stage_end {
153    ($start:ident, $name:literal) => {
154        #[cfg(feature = "stage-timing")]
155        {
156            let elapsed = $start.elapsed();
157            debug!(
158                stage = $name,
159                elapsed_ns = elapsed.as_nanos() as u64,
160                elapsed = ?elapsed,
161                "read_impl stage timing"
162            );
163        }
164    };
165}
166
167/// Minimum number of items per rayon task. Tables smaller than this do not split, avoiding
168/// fork/join and work-stealing overhead for the many small framework assemblies that dominate
169/// a typical cold-start working set. Large assemblies (e.g. corlib) still parallelize freely.
170const MIN_PAR_LEN: usize = 256;
171
172#[inline]
173fn resolve_oob(kind: &'static str, index: usize, max: usize) -> DLLError {
174    ResolveError::IndexOutOfRange { kind, index, max }.into()
175}
176
177#[inline]
178fn resolve_bad_target(context: &'static str) -> DLLError {
179    ResolveError::BadTokenTarget { context }.into()
180}
181
182#[inline]
183fn resolve_missing_row(table: &'static str, index: usize) -> DLLError {
184    ResolveError::MissingRow { table, index }.into()
185}
186
187#[inline]
188fn resolve_index_arithmetic(context: &'static str) -> DLLError {
189    ResolveError::IndexArithmetic { context }.into()
190}
191
192#[inline]
193fn validity_bad_flags(context: &'static str, flags: u32) -> DLLError {
194    ValidityError::BadFlags { context, flags }.into()
195}
196
197// since we're dealing with raw indices and not references, we have to think about what the other indices are pointing to
198// if we remove an element, all the indices above it need to be adjusted accordingly for future iterations
199/// # Panics
200/// Panics only if the crate's own `MethodIndex` invariant is violated and a non-method member is
201/// passed to this helper (internal invariant).
202fn extract_method<'a>(
203    parent: &mut types::TypeDefinition<'a>,
204    idx: MethodIndex,
205    methods: &mut [MethodIndex],
206    tables: &metadata::table::Tables,
207) -> std::result::Result<members::Method<'a>, DLLError> {
208    let MethodMemberIndex::Method(internal_idx) = idx.member else {
209        // Invariant: `extract_method` is only called with method-member indices.
210        debug_assert!(false, "unreachable extract_method called with non-method member index");
211        unreachable!()
212    };
213
214    if let Ok(start_idx) = methods.binary_search_by_key(&idx.parent_type, |m| m.parent_type) {
215        // first element is the index into methods, second element is the internal index
216        let mut max_internal: Option<(usize, usize)> = None;
217
218        // look for the maximum internal index for all methods in the same type
219        let mut find_max = |start: usize, inc: isize, stop: usize| -> Result<()> {
220            let mut current_index = start;
221            while methods[current_index].parent_type == idx.parent_type {
222                if let MethodMemberIndex::Method(i) = methods[current_index].member {
223                    match &max_internal {
224                        Some((_, max_i)) if i <= *max_i => {}
225                        _ => {
226                            max_internal = Some((current_index, i));
227                        }
228                    }
229                }
230                if current_index == stop {
231                    break;
232                }
233                current_index = current_index.checked_add_signed(inc).ok_or(ResolveError::IndexArithmetic {
234                    context: "extract_method walk",
235                })?;
236            }
237            Ok(())
238        };
239
240        // since we only sorted on parent_type, we could land anywhere in the group with the same parent
241        // so we need to iterate in both directions to make sure we don't miss anything
242        find_max(start_idx, 1, tables.method_def.len() - 1)?;
243        if start_idx != 0 {
244            find_max(start_idx - 1, -1, 0)?;
245        }
246
247        // once we have the maximum internal index, this corresponds to the last method in the type
248        // since we're about to swap_remove, change this method's internal index to where it's going to be put
249        if let Some((max_index, _)) = max_internal {
250            methods[max_index].member = MethodMemberIndex::Method(internal_idx);
251        }
252    }
253
254    Ok(parent.methods.swap_remove(internal_idx))
255}
256
257fn make_generic<'a, T: TypeKind>(
258    name: Cow<'a, str>,
259    p: &metadata::table::GenericParam,
260    param_idx: usize,
261    constraint_map: &mut HashMap<usize, (usize, usize)>,
262    tables: &metadata::table::Tables,
263    ctx: &convert::read::Context<'_, 'a>,
264) -> Result<Generic<'a, T>> {
265    Ok(Generic {
266        attributes: vec![],
267        variance: match p.flags & 0x3 {
268            0x0 => Variance::Invariant,
269            0x1 => Variance::Covariant,
270            0x2 => Variance::Contravariant,
271            _ => dll_bail!(ValidityError::BadVarianceFlags { flags: p.flags }),
272        },
273        name,
274        special_constraint: SpecialConstraint {
275            reference_type: check_bitmask!(p.flags, 0x04),
276            value_type: check_bitmask!(p.flags, 0x08),
277            has_default_constructor: check_bitmask!(p.flags, 0x10),
278        },
279        type_constraints: {
280            let constraints = &tables.generic_param_constraint;
281            let owner = param_idx + 1;
282            let start = constraints.partition_point(|c| c.owner.0 < owner);
283
284            let mut type_constraints = Vec::new();
285            let mut c_idx = start;
286
287            while let Some(c) = constraints.get(c_idx) {
288                if c.owner.0 != owner {
289                    break;
290                }
291
292                let (cmod, ty) = convert::read::idx_with_mod(c.constraint, ctx)?;
293                let internal = type_constraints.len();
294                constraint_map.insert(c_idx, (param_idx, internal));
295                type_constraints.push(Constraint {
296                    attributes: vec![],
297                    custom_modifiers: cmod,
298                    constraint_type: ty,
299                });
300
301                c_idx += 1;
302            }
303
304            type_constraints
305        },
306    })
307}
308
309fn member_accessibility(flags: u16) -> Result<members::Accessibility> {
310    use Accessibility::*;
311    use members::Accessibility::*;
312
313    Ok(match flags & 0x7 {
314        0x0 => CompilerControlled,
315        0x1 => Access(Private),
316        0x2 => Access(FamilyANDAssembly),
317        0x3 => Access(Assembly),
318        0x4 => Access(Family),
319        0x5 => Access(FamilyORAssembly),
320        0x6 => Access(Public),
321        bad => dll_bail!(validity_bad_flags("member accessibility", u32::from(bad))),
322    })
323}
324
325fn decode_assembly<'a>(
326    tables: &metadata::table::Tables,
327    strings: &StringsReader<'a>,
328    blobs: &BlobReader<'a>,
329) -> Result<Option<assembly::Assembly<'a>>> {
330    let mut assembly = None;
331    if let Some(a) = tables.assembly.first() {
332        use assembly::*;
333
334        assembly = Some(Assembly {
335            attributes: vec![],
336            hash_algorithm: match a.hash_alg_id {
337                0x0000 => HashAlgorithm::None,
338                0x8003 => HashAlgorithm::ReservedMD5,
339                0x8004 => HashAlgorithm::SHA1,
340                _other => dll_bail!(ParseError::BadStructure("unrecognized assembly hash algorithm")),
341            },
342            version: build_version!(a),
343            flags: Flags::new(a.flags),
344            public_key: optional_idx!(blobs, a.public_key),
345            name: heap_idx!(strings, a.name),
346            culture: optional_idx!(strings, a.culture),
347            security: None,
348        });
349    }
350
351    Ok(assembly)
352}
353
354fn decode_assembly_refs<'a>(
355    tables: &metadata::table::Tables,
356    strings: &StringsReader<'a>,
357    blobs: &BlobReader<'a>,
358) -> Result<Vec<assembly::ExternalAssemblyReference<'a>>> {
359    use rayon::prelude::*;
360
361    let refs = tables
362        .assembly_ref
363        .par_iter()
364        .with_min_len(MIN_PAR_LEN)
365        .map(|a| {
366            use assembly::*;
367            Ok(ExternalAssemblyReference {
368                attributes: vec![],
369                version: build_version!(a),
370                has_full_public_key: check_bitmask!(a.flags, 0x0001),
371                public_key_or_token: optional_idx!(blobs, a.public_key_or_token),
372                name: heap_idx!(strings, a.name),
373                culture: optional_idx!(strings, a.culture),
374                hash_value: optional_idx!(blobs, a.hash_value),
375            })
376        })
377        .collect::<Vec<Result<_>>>();
378
379    refs.into_iter().collect::<Result<Vec<_>>>()
380}
381
382/// # Panics
383/// Panics only if the crate's own layout-flag masking logic becomes inconsistent with the
384/// handled layout values (internal invariant).
385fn decode_type_definitions<'a>(
386    tables: &metadata::table::Tables,
387    strings: &StringsReader<'a>,
388    ctx: &convert::read::Context<'_, 'a>,
389) -> Result<Vec<types::TypeDefinition<'a>>> {
390    let mut types = Vec::with_capacity(tables.type_def.len());
391    for (idx, t) in tables.type_def.iter().enumerate() {
392        use types::*;
393
394        let layout_flags = t.flags & 0x18;
395        let name = heap_idx!(strings, t.type_name);
396
397        types.push(TypeDefinition {
398            attributes: vec![],
399            flags: TypeFlags::from_mask(
400                t.flags,
401                if layout_flags == 0x00 {
402                    Layout::Automatic
403                } else {
404                    let layout = {
405                        let pos = tables.class_layout.partition_point(|c| c.parent.0 - 1 < idx);
406                        tables.class_layout.get(pos).filter(|c| c.parent.0 - 1 == idx)
407                    };
408
409                    match layout_flags {
410                        0x08 => Layout::Sequential(layout.map(|l| SequentialLayout {
411                            packing_size: l.packing_size as usize,
412                            class_size: l.class_size as usize,
413                        })),
414                        0x10 => Layout::Explicit(layout.map(|l| ExplicitLayout {
415                            class_size: l.class_size as usize,
416                        })),
417                        _ => {
418                            // Invariant: `layout_flags` is masked by `t.flags & 0x18` above.
419                            debug_assert!(false, "unreachable type layout flags outside 0x00/0x08/0x10");
420                            unreachable!()
421                        },
422                    }
423                },
424            ),
425            name,
426            namespace: optional_idx!(strings, t.type_namespace),
427            fields: vec![],
428            properties: vec![],
429            methods: vec![],
430            events: vec![],
431            encloser: None,
432            overrides: vec![],
433            extends: if t.extends.is_null() {
434                None
435            } else {
436                Some(convert::read::type_source(t.extends, ctx)?)
437            },
438            implements: vec![],
439            generic_parameters: vec![],
440            security: None,
441        });
442    }
443
444    Ok(types)
445}
446
447fn decode_files<'a>(
448    tables: &metadata::table::Tables,
449    strings: &StringsReader<'a>,
450    blobs: &BlobReader<'a>,
451) -> Result<Vec<module::File<'a>>> {
452    use rayon::prelude::*;
453
454    let files = tables
455        .file
456        .par_iter()
457        .with_min_len(MIN_PAR_LEN)
458        .map(|f| {
459            Ok(module::File {
460                attributes: vec![],
461                has_metadata: !check_bitmask!(f.flags, 0x0001),
462                name: heap_idx!(strings, f.name),
463                hash_value: heap_idx!(blobs, f.hash_value),
464            })
465        })
466        .collect::<Vec<Result<_>>>();
467
468    files.into_iter().collect::<Result<Vec<_>>>()
469}
470
471fn decode_resources<'a>(
472    dll: &DLL<'a>,
473    tables: &metadata::table::Tables,
474    strings: &StringsReader<'a>,
475    files: &[module::File<'a>],
476    assembly_refs: &[assembly::ExternalAssemblyReference<'a>],
477) -> Result<Vec<resource::ManifestResource<'a>>> {
478    use rayon::prelude::*;
479
480    let resources = tables
481        .manifest_resource
482        .par_iter()
483        .with_min_len(MIN_PAR_LEN)
484        .map(|r| {
485            use metadata::index::Implementation as BinImpl;
486            use resource::*;
487
488            let name = heap_idx!(strings, r.name);
489            let mut offset = r.offset as usize;
490
491            Ok(ManifestResource {
492                attributes: vec![],
493                visibility: match r.flags & 0x7 {
494                    0x1 => Visibility::Public,
495                    0x2 => Visibility::Private,
496                    bad => dll_bail!(validity_bad_flags("manifest resource visibility", bad)),
497                },
498                implementation: match r.implementation {
499                    BinImpl::File(f) => {
500                        let idx = f - 1;
501                        if idx < files.len() {
502                            Implementation::File {
503                                location: FileIndex(idx),
504                                offset,
505                            }
506                        } else {
507                            dll_bail!(resolve_oob(
508                                "manifest resource file",
509                                idx,
510                                files.len().saturating_sub(1),
511                            ));
512                        }
513                    }
514                    BinImpl::AssemblyRef(a) => {
515                        let idx = a - 1;
516
517                        if idx < assembly_refs.len() {
518                            Implementation::Assembly {
519                                location: AssemblyRefIndex(idx),
520                                offset,
521                            }
522                        } else {
523                            dll_bail!(resolve_oob(
524                                "manifest resource assembly reference",
525                                idx,
526                                assembly_refs.len().saturating_sub(1),
527                            ));
528                        }
529                    }
530                    BinImpl::ExportedType(_) => {
531                        dll_bail!(resolve_bad_target(
532                            "manifest resource implementation cannot target ExportedType",
533                        ));
534                    }
535                    BinImpl::Null => {
536                        let rva_data = dll.at_rva(&dll.cli.resources)?;
537                        let len: u32 = rva_data.gread_with(&mut offset, scroll::LE)?;
538                        Implementation::CurrentFile(rva_data[offset..offset + (len as usize)].into())
539                    }
540                },
541                name,
542            })
543        })
544        .collect::<Vec<Result<_>>>();
545
546    resources.into_iter().collect::<Result<Vec<_>>>()
547}
548
549fn decode_exported_types<'a>(
550    tables: &metadata::table::Tables,
551    strings: &StringsReader<'a>,
552    files: &[module::File<'a>],
553    assembly_refs: &[assembly::ExternalAssemblyReference<'a>],
554) -> Result<Vec<types::ExportedType<'a>>> {
555    use rayon::prelude::*;
556
557    let exported_types = tables
558        .exported_type
559        .par_iter()
560        .with_min_len(MIN_PAR_LEN)
561        .map(|e| {
562            use metadata::index::Implementation;
563            use types::*;
564
565            let name = heap_idx!(strings, e.type_name);
566            Ok(ExportedType {
567                attributes: vec![],
568                flags: TypeFlags::from_mask(e.flags, Layout::Automatic),
569                namespace: optional_idx!(strings, e.type_namespace),
570                implementation: match e.implementation {
571                    Implementation::File(f) => {
572                        let idx = f - 1;
573                        let t_idx = e.type_def_id as usize;
574
575                        if idx < files.len() {
576                            TypeImplementation::ModuleFile {
577                                type_def: if t_idx < tables.type_def.len() {
578                                    TypeIndex(t_idx)
579                                } else {
580                                    dll_bail!(resolve_oob(
581                                        "exported type definition",
582                                        t_idx,
583                                        tables.type_def.len().saturating_sub(1),
584                                    ));
585                                },
586                                file: FileIndex(idx),
587                            }
588                        } else {
589                            dll_bail!(resolve_oob("exported type file", idx, files.len().saturating_sub(1)));
590                        }
591                    }
592                    Implementation::AssemblyRef(a) => {
593                        let idx = a - 1;
594
595                        if idx < assembly_refs.len() {
596                            TypeImplementation::TypeForwarder(AssemblyRefIndex(idx))
597                        } else {
598                            dll_bail!(resolve_oob(
599                                "exported type assembly reference",
600                                idx,
601                                assembly_refs.len().saturating_sub(1),
602                            ));
603                        }
604                    }
605                    Implementation::ExportedType(t) => {
606                        let idx = t - 1;
607                        if idx < tables.exported_type.len() {
608                            TypeImplementation::Nested(ExportedTypeIndex(idx))
609                        } else {
610                            dll_bail!(resolve_oob(
611                                "exported type nested exported type",
612                                idx,
613                                tables.exported_type.len().saturating_sub(1),
614                            ));
615                        }
616                    }
617                    Implementation::Null => {
618                        dll_bail!(resolve_bad_target("exported type implementation cannot be null"));
619                    }
620                },
621                name,
622            })
623        })
624        .collect::<Vec<Result<_>>>();
625
626    exported_types.into_iter().collect::<Result<Vec<_>>>()
627}
628
629fn decode_module<'a>(
630    tables: &metadata::table::Tables,
631    strings: &StringsReader<'a>,
632    guids: &GUIDReader<'a>,
633) -> Result<module::Module<'a>> {
634    let module_row = tables
635        .module
636        .first()
637        .ok_or(ParseError::BadStructure("missing required module metadata table"))?;
638    Ok(module::Module {
639        attributes: vec![],
640        name: heap_idx!(strings, module_row.name),
641        mvid: guids.at_index(module_row.mvid)?,
642    })
643}
644
645fn decode_module_refs<'a>(
646    tables: &metadata::table::Tables,
647    strings: &StringsReader<'a>,
648) -> Result<Vec<module::ExternalModuleReference<'a>>> {
649    use rayon::prelude::*;
650
651    let module_refs = tables
652        .module_ref
653        .par_iter()
654        .with_min_len(MIN_PAR_LEN)
655        .map(|r| {
656            Ok(module::ExternalModuleReference {
657                attributes: vec![],
658                name: heap_idx!(strings, r.name),
659            })
660        })
661        .collect::<Vec<Result<_>>>();
662
663    module_refs.into_iter().collect::<Result<Vec<_>>>()
664}
665
666fn decode_type_refs<'a>(
667    tables: &metadata::table::Tables,
668    strings: &StringsReader<'a>,
669    module_refs: &[module::ExternalModuleReference<'a>],
670    assembly_refs: &[assembly::ExternalAssemblyReference<'a>],
671) -> Result<Vec<types::ExternalTypeReference<'a>>> {
672    use rayon::prelude::*;
673
674    let type_refs = tables
675        .type_ref
676        .par_iter()
677        .with_min_len(MIN_PAR_LEN)
678        .map(|r| {
679            use metadata::index::ResolutionScope as BinRS;
680            use types::*;
681
682            let name = heap_idx!(strings, r.type_name);
683            let namespace = optional_idx!(strings, r.type_namespace);
684
685            Ok(ExternalTypeReference {
686                attributes: vec![],
687                namespace,
688                scope: match r.resolution_scope {
689                    BinRS::Module(_) => ResolutionScope::CurrentModule,
690                    BinRS::ModuleRef(m) => {
691                        let idx = m - 1;
692                        if idx < module_refs.len() {
693                            ResolutionScope::ExternalModule(ModuleRefIndex(idx))
694                        } else {
695                            dll_bail!(resolve_oob(
696                                "type reference module reference",
697                                idx,
698                                module_refs.len().saturating_sub(1),
699                            ));
700                        }
701                    }
702                    BinRS::AssemblyRef(a) => {
703                        let idx = a - 1;
704
705                        if idx < assembly_refs.len() {
706                            ResolutionScope::Assembly(AssemblyRefIndex(idx))
707                        } else {
708                            dll_bail!(resolve_oob(
709                                "type reference assembly reference",
710                                idx,
711                                assembly_refs.len().saturating_sub(1),
712                            ));
713                        }
714                    }
715                    BinRS::TypeRef(t) => {
716                        let idx = t - 1;
717                        if idx < tables.type_ref.len() {
718                            ResolutionScope::Nested(TypeRefIndex(idx))
719                        } else {
720                            dll_bail!(resolve_oob(
721                                "type reference nested type reference",
722                                idx,
723                                tables.type_ref.len().saturating_sub(1),
724                            ));
725                        }
726                    }
727                    BinRS::Null => ResolutionScope::Exported,
728                },
729                name,
730            })
731        })
732        .collect::<Vec<Result<_>>>();
733
734    type_refs.into_iter().collect::<Result<Vec<_>>>()
735}
736
737fn decode_interfaces<'a>(
738    tables: &metadata::table::Tables,
739    types: &mut [types::TypeDefinition<'a>],
740    ctx: &convert::read::Context<'_, 'a>,
741) -> Result<Vec<(usize, usize)>> {
742    let mut interface_idxs = Vec::with_capacity(tables.interface_impl.len());
743    for i in &tables.interface_impl {
744        let idx = i.class.0 - 1;
745        match types.get_mut(idx) {
746            Some(t) => {
747                t.implements
748                    .push((vec![], convert::read::type_source(i.interface, ctx)?));
749                interface_idxs.push((idx, t.implements.len() - 1));
750            }
751            None => {
752                dll_bail!(resolve_oob(
753                    "interface implementation parent type",
754                    idx,
755                    types.len().saturating_sub(1),
756                ));
757            }
758        }
759    }
760    Ok(interface_idxs)
761}
762
763fn decode_fields<'a>(
764    types: &mut [types::TypeDefinition<'a>],
765    tables: &metadata::table::Tables,
766    strings: &StringsReader<'a>,
767    blobs: &BlobReader<'a>,
768    ctx: &convert::read::Context<'_, 'a>,
769) -> Result<Vec<FieldIndex>> {
770    use rayon::prelude::*;
771
772    let owned_fields = tables
773        .type_def
774        .iter()
775        .enumerate()
776        .map(|(type_idx, t)| {
777            let start = t.field_list.0 - 1;
778            let end = match tables.type_def.get(type_idx + 1) {
779                Some(next) => next.field_list.0,
780                None => tables.field.len() + 1,
781            } - 1;
782
783            (type_idx, start, end)
784        })
785        .collect::<Vec<_>>();
786
787    let decoded_fields = owned_fields
788        .par_iter()
789        .with_min_len(MIN_PAR_LEN)
790        .map(|&(type_idx, start, end)| {
791            use crate::binary::signature::kinds::FieldSig;
792            use members::*;
793
794            let Some(type_fields) = tables.field.get(start..end) else {
795                dll_bail!(ParseError::BadStructure("invalid field range in TypeDef row"));
796            };
797
798            let mut fields = Vec::with_capacity(type_fields.len());
799            let mut field_idxs = Vec::with_capacity(type_fields.len());
800
801            for (offset, f) in type_fields.iter().enumerate() {
802                let f_idx = start + offset;
803
804                let FieldSig {
805                    custom_modifiers: cmod,
806                    field_type: t,
807                    by_ref,
808                } = heap_idx!(blobs, f.signature).pread(0)?;
809
810                fields.push(Field {
811                    attributes: vec![],
812                    name: heap_idx!(strings, f.name),
813                    type_modifiers: if cmod.is_empty() {
814                        vec![]
815                    } else {
816                        cmod.into_iter()
817                            .map(|c| convert::read::custom_modifier(c, ctx))
818                            .collect::<Result<_>>()?
819                    },
820                    by_ref,
821                    return_type: MemberType::from_sig(t, ctx)?,
822                    accessibility: member_accessibility(f.flags)?,
823                    static_member: check_bitmask!(f.flags, 0x10),
824                    init_only: check_bitmask!(f.flags, 0x20),
825                    literal: check_bitmask!(f.flags, 0x40),
826                    default: None,
827                    not_serialized: check_bitmask!(f.flags, 0x80),
828                    special_name: check_bitmask!(f.flags, 0x200),
829                    pinvoke: None,
830                    runtime_special_name: check_bitmask!(f.flags, 0x400),
831                    offset: None,
832                    marshal: None,
833                    initial_value: None,
834                });
835
836                field_idxs.push((
837                    f_idx,
838                    FieldIndex {
839                        parent_type: TypeIndex(type_idx),
840                        field: fields.len() - 1,
841                    },
842                ));
843            }
844
845            Ok((type_idx, fields, field_idxs))
846        })
847        .collect::<Vec<Result<_>>>()
848        .into_iter()
849        .collect::<Result<Vec<_>>>()?;
850
851    build_vec!(fields = FieldIndex[tables.field.len()], {
852        for (type_idx, decoded_type_fields, decoded_type_field_idxs) in decoded_fields {
853            let parent_fields = &mut types[type_idx].fields;
854            let field_offset = parent_fields.len();
855            parent_fields.reserve(decoded_type_fields.len());
856            parent_fields.extend(decoded_type_fields);
857
858            for (f_idx, mut field_idx) in decoded_type_field_idxs {
859                field_idx.field += field_offset;
860                fields[f_idx].write(field_idx);
861            }
862        }
863    });
864
865    Ok(fields)
866}
867
868struct DecodedMethods {
869    methods: Vec<MethodIndex>,
870    owned_params: Vec<(usize, usize, usize)>,
871    sig_pending_def: Vec<(crate::binary::metadata::index::Blob, bool)>,
872}
873
874/// # Panics
875/// Panics only if internal flag/variant narrowing assumptions in method decoding are violated
876/// by inconsistent crate logic (internal invariant).
877fn decode_methods<'a>(
878    types: &mut [types::TypeDefinition<'a>],
879    tables: &metadata::table::Tables,
880    strings: &StringsReader<'a>,
881    blobs: &BlobReader<'a>,
882    ctx: &convert::read::Context<'_, 'a>,
883    opts: Options,
884) -> Result<DecodedMethods> {
885    use rayon::prelude::*;
886
887    let owned_methods = tables
888        .type_def
889        .iter()
890        .enumerate()
891        .map(|(type_idx, t)| {
892            let start = t.method_list.0 - 1;
893            let end = match tables.type_def.get(type_idx + 1) {
894                Some(next) => next.method_list.0,
895                None => tables.method_def.len() + 1,
896            } - 1;
897
898            (type_idx, start, end)
899        })
900        .collect::<Vec<_>>();
901
902    let decoded_methods = owned_methods
903        .par_iter()
904        .with_min_len(MIN_PAR_LEN)
905        .map(|&(type_idx, start, end)| {
906            use members::*;
907
908            let Some(type_methods) = tables.method_def.get(start..end) else {
909                dll_bail!(ParseError::BadStructure("invalid method range in TypeDef row"));
910            };
911
912            let mut methods = Vec::with_capacity(type_methods.len());
913            let mut method_idxs = Vec::with_capacity(type_methods.len());
914            let mut param_ranges = Vec::with_capacity(type_methods.len());
915
916            for (offset, m) in type_methods.iter().enumerate() {
917                let m_idx = start + offset;
918
919                let name = heap_idx!(strings, m.name);
920
921                let sig = if opts.lazy_method_signatures {
922                    Default::default()
923                } else {
924                    let mut sig = convert::read::managed_method(heap_idx!(blobs, m.signature).pread(0)?, ctx)?;
925                    if check_bitmask!(m.flags, 0x10) {
926                        sig.instance = false;
927                    }
928                    sig
929                };
930
931                methods.push(Method {
932                    attributes: vec![],
933                    body: None,
934                    signature: sig,
935                    accessibility: member_accessibility(m.flags)?,
936                    generic_parameters: vec![],
937                    return_type_metadata: None,
938                    parameter_metadata: vec![],
939                    sealed: check_bitmask!(m.flags, 0x20),
940                    virtual_member: check_bitmask!(m.flags, 0x40),
941                    hide_by_sig: check_bitmask!(m.flags, 0x80),
942                    vtable_layout: match m.flags & 0x100 {
943                        0x000 => VtableLayout::ReuseSlot,
944                        0x100 => VtableLayout::NewSlot,
945                        _ => {
946                            // Invariant: `m.flags & 0x100` can only be 0x000 or 0x100.
947                            debug_assert!(false, "unreachable method vtable layout flags");
948                            unreachable!()
949                        }
950                    },
951                    strict: check_bitmask!(m.flags, 0x200),
952                    abstract_member: check_bitmask!(m.flags, 0x400),
953                    special_name: check_bitmask!(m.flags, 0x800),
954                    pinvoke: None,
955                    runtime_special_name: check_bitmask!(m.flags, 0x1000),
956                    security: None,
957                    require_sec_object: check_bitmask!(m.flags, 0x8000),
958                    body_format: match m.impl_flags & 0x3 {
959                        0x0 => BodyFormat::IL,
960                        0x1 => BodyFormat::Native,
961                        0x2 => {
962                            dll_bail!(validity_bad_flags("method implementation code type (OPTIL)", 0x2));
963                        }
964                        0x3 => BodyFormat::Runtime,
965                        _ => {
966                            // Invariant: `m.impl_flags & 0x3` can only yield 0x0..=0x3.
967                            debug_assert!(false, "unreachable method body format flags");
968                            unreachable!()
969                        }
970                    },
971                    name,
972                    body_management: match m.impl_flags & 0x4 {
973                        0x0 => BodyManagement::Unmanaged,
974                        0x4 => BodyManagement::Managed,
975                        _ => {
976                            // Invariant: `m.impl_flags & 0x4` can only yield 0x0 or 0x4.
977                            debug_assert!(false, "unreachable method body-management flags");
978                            unreachable!()
979                        }
980                    },
981                    forward_ref: check_bitmask!(m.impl_flags, 0x10),
982                    preserve_sig: check_bitmask!(m.impl_flags, 0x80),
983                    internal_call: check_bitmask!(m.impl_flags, 0x1000),
984                    synchronized: check_bitmask!(m.impl_flags, 0x20),
985                    no_inlining: check_bitmask!(m.impl_flags, 0x8),
986                    no_optimization: check_bitmask!(m.impl_flags, 0x40),
987                });
988
989                method_idxs.push((
990                    m_idx,
991                    MethodIndex {
992                        parent_type: TypeIndex(type_idx),
993                        member: MethodMemberIndex::Method(methods.len() - 1),
994                    },
995                ));
996
997                let param_start = m.param_list.0 - 1;
998                let param_end = match tables.method_def.get(m_idx + 1) {
999                    Some(next) => next.param_list.0,
1000                    None => tables.param.len() + 1,
1001                } - 1;
1002
1003                if tables.param.get(param_start..param_end).is_none() {
1004                    dll_bail!(ParseError::BadStructure("invalid parameter range in MethodDef row"));
1005                }
1006
1007                if param_start != param_end {
1008                    param_ranges.push((m_idx, param_start, param_end));
1009                }
1010            }
1011
1012            Ok((type_idx, methods, method_idxs, param_ranges))
1013        })
1014        .collect::<Vec<Result<_>>>()
1015        .into_iter()
1016        .collect::<Result<Vec<_>>>()?;
1017
1018    let mut owned_params = Vec::with_capacity(tables.param.len());
1019    let sig_pending_def: Vec<(crate::binary::metadata::index::Blob, bool)> = if opts.lazy_method_signatures {
1020        tables
1021            .method_def
1022            .iter()
1023            .map(|m| (m.signature, check_bitmask!(m.flags, 0x10)))
1024            .collect()
1025    } else {
1026        vec![]
1027    };
1028
1029    build_vec!(methods = MethodIndex[tables.method_def.len()], {
1030        for (type_idx, decoded_type_methods, decoded_type_method_idxs, decoded_type_param_ranges) in decoded_methods {
1031            let parent_methods = &mut types[type_idx].methods;
1032            let method_offset = parent_methods.len();
1033            parent_methods.reserve(decoded_type_methods.len());
1034            parent_methods.extend(decoded_type_methods);
1035
1036            for (m_idx, mut method_idx) in decoded_type_method_idxs {
1037                let MethodMemberIndex::Method(member_idx) = &mut method_idx.member else {
1038                    // Invariant: `decode_type_methods` produces only `Method` member variants here.
1039                    debug_assert!(false, "unreachable decoded method index contained non-method member variant");
1040                    unreachable!()
1041                };
1042                *member_idx += method_offset;
1043                methods[m_idx].write(method_idx);
1044            }
1045
1046            owned_params.extend(decoded_type_param_ranges);
1047        }
1048    });
1049
1050    Ok(DecodedMethods {
1051        methods,
1052        owned_params,
1053        sig_pending_def,
1054    })
1055}
1056
1057fn get_field_mut<'a, 'r>(types: &'r mut [types::TypeDefinition<'a>], idx: FieldIndex) -> &'r mut members::Field<'a> {
1058    &mut types[idx.parent_type.0].fields[idx.field]
1059}
1060
1061/// # Panics
1062/// Panics only if callers violate the crate invariant that this helper is invoked with
1063/// `MethodMemberIndex::Method` (internal invariant).
1064fn get_method_mut<'a, 'r>(types: &'r mut [types::TypeDefinition<'a>], idx: MethodIndex) -> &'r mut members::Method<'a> {
1065    let MethodMemberIndex::Method(member_idx) = idx.member else {
1066        // Invariant: callers pass method indices that still reference method members.
1067        debug_assert!(false, "unreachable get_method_mut called with non-method member index");
1068        unreachable!()
1069    };
1070    &mut types[idx.parent_type.0].methods[member_idx]
1071}
1072
1073fn decode_params<'a>(
1074    types: &mut [types::TypeDefinition<'a>],
1075    tables: &metadata::table::Tables,
1076    strings: &StringsReader<'a>,
1077    methods: &[MethodIndex],
1078    owned_params: Vec<(usize, usize, usize)>,
1079) -> Result<Vec<(usize, usize)>> {
1080    use rayon::prelude::*;
1081
1082    struct DecodedParam<'a> {
1083        p_idx: usize,
1084        sequence: usize,
1085        metadata: Option<members::ParameterMetadata<'a>>,
1086    }
1087
1088    let decoded_params = owned_params
1089        .par_iter()
1090        .with_min_len(MIN_PAR_LEN)
1091        .map(|&(m_idx, start, end)| {
1092            use members::*;
1093
1094            let mut decoded = Vec::with_capacity(end - start);
1095
1096            for p_idx in start..end {
1097                let param = &tables.param[p_idx];
1098                let sequence = param.sequence as usize;
1099
1100                decoded.push(DecodedParam {
1101                    p_idx,
1102                    sequence,
1103                    metadata: Some(ParameterMetadata {
1104                        attributes: vec![],
1105                        name: optional_idx!(strings, param.name),
1106                        is_in: check_bitmask!(param.flags, 0x1),
1107                        is_out: check_bitmask!(param.flags, 0x2),
1108                        optional: check_bitmask!(param.flags, 0x10),
1109                        default: None,
1110                        marshal: None,
1111                    }),
1112                });
1113            }
1114
1115            Ok((m_idx, decoded))
1116        })
1117        .collect::<Vec<Result<_>>>()
1118        .into_iter()
1119        .collect::<Result<Vec<_>>>()?;
1120
1121    build_vec!(params = (usize, usize)[tables.param.len()], {
1122        for (m_idx, decoded_method_params) in decoded_params {
1123            let method = get_method_mut(types, methods[m_idx]);
1124
1125            let max_sequence = decoded_method_params
1126                .iter()
1127                .map(|param| param.sequence)
1128                .max()
1129                .unwrap_or(0);
1130            if max_sequence > 0 && method.parameter_metadata.len() < max_sequence {
1131                method.parameter_metadata.resize(max_sequence, None);
1132            }
1133
1134            for param in decoded_method_params {
1135                if param.sequence == 0 {
1136                    method.return_type_metadata = param.metadata;
1137                } else {
1138                    method.parameter_metadata[param.sequence - 1] = param.metadata;
1139                }
1140
1141                params[param.p_idx].write((m_idx, param.sequence));
1142            }
1143        }
1144    });
1145
1146    Ok(params)
1147}
1148
1149struct DecodedProperties {
1150    properties: Vec<(usize, usize)>,
1151    sig_pending_property: Vec<crate::binary::metadata::index::Blob>,
1152}
1153
1154fn decode_properties<'a>(
1155    types: &mut [types::TypeDefinition<'a>],
1156    tables: &metadata::table::Tables,
1157    strings: &StringsReader<'a>,
1158    blobs: &BlobReader<'a>,
1159    ctx: &convert::read::Context<'_, 'a>,
1160    opts: Options,
1161) -> Result<DecodedProperties> {
1162    use rayon::prelude::*;
1163
1164    let owned_properties = tables
1165        .property_map
1166        .iter()
1167        .enumerate()
1168        .map(|(map_idx, map)| {
1169            let type_idx = map.parent.0 - 1;
1170            if type_idx >= types.len() {
1171                dll_bail!(resolve_oob(
1172                    "property map parent type",
1173                    type_idx,
1174                    types.len().saturating_sub(1),
1175                ));
1176            }
1177
1178            let start = map.property_list.0 - 1;
1179            let end = match tables.property_map.get(map_idx + 1) {
1180                Some(next) => next.property_list.0,
1181                None => tables.property.len() + 1,
1182            } - 1;
1183
1184            Ok((type_idx, start, end, map_idx))
1185        })
1186        .collect::<Vec<Result<_>>>()
1187        .into_iter()
1188        .collect::<Result<Vec<_>>>()?;
1189
1190    let decoded_properties = owned_properties
1191        .par_iter()
1192        .with_min_len(MIN_PAR_LEN)
1193        .map(|&(type_idx, start, end, _map_idx)| {
1194            use crate::binary::signature::kinds::PropertySig;
1195            use members::*;
1196
1197            let Some(props) = tables.property.get(start..end) else {
1198                dll_bail!(ParseError::BadStructure("invalid property range in PropertyMap row"));
1199            };
1200
1201            let mut properties = Vec::with_capacity(props.len());
1202            let mut property_idxs = Vec::with_capacity(props.len());
1203
1204            for (offset, prop) in props.iter().enumerate() {
1205                let p_idx = start + offset;
1206
1207                let (static_member, property_type, parameters) = if opts.lazy_property_signatures {
1208                    (
1209                        false,
1210                        crate::resolved::signature::Parameter::value(MemberType::from_base(
1211                            crate::resolved::types::BaseType::Object,
1212                        )),
1213                        vec![],
1214                    )
1215                } else {
1216                    let sig = heap_idx!(blobs, prop.property_type).pread::<PropertySig>(0)?;
1217                    (!sig.has_this, convert::read::parameter(sig.property_type, ctx)?, {
1218                        let mut ps = Vec::with_capacity(sig.params.len());
1219                        for p in sig.params {
1220                            ps.push(convert::read::parameter(p, ctx)?);
1221                        }
1222                        ps
1223                    })
1224                };
1225
1226                properties.push(Property {
1227                    attributes: vec![],
1228                    name: heap_idx!(strings, prop.name),
1229                    getter: None,
1230                    setter: None,
1231                    other: vec![],
1232                    static_member,
1233                    property_type,
1234                    parameters,
1235                    special_name: check_bitmask!(prop.flags, 0x200),
1236                    runtime_special_name: check_bitmask!(prop.flags, 0x1000),
1237                    default: None,
1238                });
1239
1240                property_idxs.push((p_idx, (type_idx, properties.len() - 1)));
1241            }
1242
1243            Ok((type_idx, properties, property_idxs))
1244        })
1245        .collect::<Vec<Result<_>>>()
1246        .into_iter()
1247        .collect::<Result<Vec<_>>>()?;
1248
1249    build_vec!(properties = (usize, usize)[tables.property.len()], {
1250        for (type_idx, decoded_type_properties, decoded_type_property_idxs) in decoded_properties {
1251            let parent_properties = &mut types[type_idx].properties;
1252            let property_offset = parent_properties.len();
1253            parent_properties.reserve(decoded_type_properties.len());
1254            parent_properties.extend(decoded_type_properties);
1255
1256            for (p_idx, (prop_type_idx, mut prop_idx)) in decoded_type_property_idxs {
1257                debug_assert_eq!(type_idx, prop_type_idx);
1258                prop_idx += property_offset;
1259                properties[p_idx].write((prop_type_idx, prop_idx));
1260            }
1261        }
1262    });
1263
1264    let sig_pending_property = if opts.lazy_property_signatures {
1265        tables.property.iter().map(|p| p.property_type).collect()
1266    } else {
1267        vec![]
1268    };
1269
1270    Ok(DecodedProperties {
1271        properties,
1272        sig_pending_property,
1273    })
1274}
1275
1276fn decode_events<'a>(
1277    types: &mut [types::TypeDefinition<'a>],
1278    tables: &metadata::table::Tables,
1279    strings: &StringsReader<'a>,
1280    ctx: &convert::read::Context<'_, 'a>,
1281    sem_by_event: &mut HashMap<usize, Vec<(u16, usize)>>,
1282    methods: &mut [MethodIndex],
1283) -> Result<Vec<(usize, usize)>> {
1284    build_vec!(events = (usize, usize)[tables.event.len()], {
1285        for (map_idx, map) in tables.event_map.iter().enumerate() {
1286            let type_idx = map.parent.0 - 1;
1287
1288            let max_type_idx = types.len().saturating_sub(1);
1289            let Some(parent) = types.get_mut(type_idx) else {
1290                dll_bail!(resolve_oob("event map parent type", type_idx, max_type_idx));
1291            };
1292
1293            let start = map.event_list.0 - 1;
1294            let end = match tables.event_map.get(map_idx + 1) {
1295                Some(next) => next.event_list.0,
1296                None => tables.event.len() + 1,
1297            } - 1;
1298
1299            let Some(type_events) = tables.event.get(start..end) else {
1300                dll_bail!(ParseError::BadStructure("invalid event range in EventMap row"));
1301            };
1302
1303            for (offset, event) in type_events.iter().enumerate() {
1304                use members::*;
1305
1306                let e_idx = start + offset;
1307                let name = heap_idx!(strings, event.name);
1308
1309                let internal_idx = parent.events.len();
1310
1311                macro_rules! get_listener {
1312                    ($l_name:literal, $flag:literal, $variant:ident) => {{
1313                        let Some(entries) = sem_by_event.get_mut(&e_idx) else {
1314                            dll_bail!(resolve_missing_row("event listener", e_idx));
1315                        };
1316                        let Some(position) = entries
1317                            .iter()
1318                            .position(|(semantics, _)| check_bitmask!(*semantics, $flag))
1319                        else {
1320                            dll_bail!(resolve_missing_row("event listener", e_idx));
1321                        };
1322                        let (_, m_idx) = entries.swap_remove(position);
1323                        if m_idx < tables.method_def.len() {
1324                            let method = extract_method(parent, methods[m_idx], methods, tables)?;
1325                            methods[m_idx].member = MethodMemberIndex::$variant(internal_idx);
1326                            method
1327                        } else {
1328                            dll_bail!(resolve_oob(
1329                                "event listener method",
1330                                m_idx,
1331                                tables.method_def.len().saturating_sub(1),
1332                            ));
1333                        }
1334                    }};
1335                }
1336
1337                let add_listener = get_listener!("add", 0x8, EventAdd);
1338                let remove_listener = get_listener!("remove", 0x10, EventRemove);
1339
1340                parent.events.push(Event {
1341                    attributes: vec![],
1342                    delegate_type: convert::read::type_idx(event.event_type, ctx)?,
1343                    add_listener,
1344                    remove_listener,
1345                    name,
1346                    raise_event: None,
1347                    other: vec![],
1348                    special_name: check_bitmask!(event.event_flags, 0x200),
1349                    runtime_special_name: check_bitmask!(event.event_flags, 0x400),
1350                });
1351                events[e_idx].write((type_idx, internal_idx));
1352            }
1353        }
1354    });
1355
1356    Ok(events)
1357}
1358
1359#[derive(Copy, Clone)]
1360enum SemanticsAssociation {
1361    Event(usize),
1362    Property(usize),
1363}
1364
1365#[derive(Copy, Clone)]
1366struct SemanticsAction {
1367    raw_method_idx: usize,
1368    semantics: u16,
1369    association: SemanticsAssociation,
1370}
1371
1372struct TypeSemanticsWork {
1373    parent_type: usize,
1374    method_start: usize,
1375    method_end: usize,
1376    actions: Vec<SemanticsAction>,
1377}
1378
1379fn apply_method_semantics_for_type<'a>(
1380    parent_type: usize,
1381    parent: &mut types::TypeDefinition<'a>,
1382    methods_for_type: &mut [MethodIndex],
1383    method_start: usize,
1384    actions: &[SemanticsAction],
1385    properties: &[(usize, usize)],
1386    events: &[(usize, usize)],
1387) -> Result<()> {
1388    let mut raw_by_internal = vec![usize::MAX; parent.methods.len()];
1389    for (local_raw_idx, method_idx) in methods_for_type.iter().enumerate() {
1390        if let MethodMemberIndex::Method(internal_idx) = method_idx.member {
1391            let Some(slot) = raw_by_internal.get_mut(internal_idx) else {
1392                dll_bail!(resolve_oob(
1393                    "method semantics internal method",
1394                    internal_idx,
1395                    raw_by_internal.len().saturating_sub(1),
1396                ));
1397            };
1398            *slot = local_raw_idx;
1399        }
1400    }
1401
1402    if raw_by_internal.contains(&usize::MAX) {
1403        dll_bail!(resolve_missing_row("method semantics mapping", parent_type));
1404    }
1405
1406    let mut extraction_order = Vec::with_capacity(actions.len());
1407    for (action_idx, action) in actions.iter().enumerate() {
1408        let Some(local_raw_idx) = action
1409            .raw_method_idx
1410            .checked_sub(method_start)
1411            .filter(|idx| *idx < methods_for_type.len())
1412        else {
1413            dll_bail!(resolve_oob(
1414                "method semantics method",
1415                action.raw_method_idx,
1416                method_start + methods_for_type.len().saturating_sub(1),
1417            ));
1418        };
1419
1420        let MethodMemberIndex::Method(internal_idx) = methods_for_type[local_raw_idx].member else {
1421            dll_bail!(resolve_bad_target("method semantics expects method member"));
1422        };
1423
1424        extraction_order.push((internal_idx, action_idx, local_raw_idx));
1425    }
1426    extraction_order.sort_unstable_by_key(|(internal_idx, _, _)| std::cmp::Reverse(*internal_idx));
1427
1428    let mut extracted = Vec::with_capacity(actions.len());
1429
1430    for (internal_idx, action_idx, expected_local_raw_idx) in extraction_order {
1431        let Some(&removed_local_raw_idx) = raw_by_internal.get(internal_idx) else {
1432            dll_bail!(resolve_oob(
1433                "method semantics internal method",
1434                internal_idx,
1435                raw_by_internal.len().saturating_sub(1),
1436            ));
1437        };
1438
1439        if removed_local_raw_idx != expected_local_raw_idx {
1440            dll_bail!(resolve_index_arithmetic("method semantics extraction order mismatch"));
1441        }
1442
1443        let moved_local_raw_idx = raw_by_internal[raw_by_internal.len() - 1];
1444
1445        raw_by_internal.swap_remove(internal_idx);
1446        let method = parent.methods.swap_remove(internal_idx);
1447
1448        if internal_idx < raw_by_internal.len() {
1449            methods_for_type[moved_local_raw_idx].member = MethodMemberIndex::Method(internal_idx);
1450        }
1451
1452        extracted.push((action_idx, method));
1453    }
1454
1455    extracted.sort_unstable_by_key(|(action_idx, _)| *action_idx);
1456
1457    for ((expected_action_idx, action), (action_idx, new_meth)) in actions.iter().enumerate().zip(extracted) {
1458        debug_assert_eq!(expected_action_idx, action_idx);
1459
1460        let Some(local_raw_idx) = action
1461            .raw_method_idx
1462            .checked_sub(method_start)
1463            .filter(|idx| *idx < methods_for_type.len())
1464        else {
1465            dll_bail!(resolve_oob(
1466                "method semantics method",
1467                action.raw_method_idx,
1468                method_start + methods_for_type.len().saturating_sub(1),
1469            ));
1470        };
1471
1472        let member_idx = &mut methods_for_type[local_raw_idx].member;
1473
1474        match action.association {
1475            SemanticsAssociation::Event(idx) => {
1476                let &(_, internal_idx) = events
1477                    .get(idx)
1478                    .ok_or_else(|| resolve_oob("method semantics event", idx, events.len().saturating_sub(1)))?;
1479                let event = &mut parent.events[internal_idx];
1480
1481                if check_bitmask!(action.semantics, 0x20) {
1482                    event.raise_event = Some(new_meth);
1483                    *member_idx = MethodMemberIndex::EventRaise(internal_idx);
1484                } else if check_bitmask!(action.semantics, 0x4) {
1485                    event.other.push(new_meth);
1486                    *member_idx = MethodMemberIndex::EventOther {
1487                        event: internal_idx,
1488                        other: event.other.len() - 1,
1489                    };
1490                }
1491            }
1492            SemanticsAssociation::Property(idx) => {
1493                let &(_, internal_idx) = properties.get(idx).ok_or_else(|| {
1494                    resolve_oob(
1495                        "method semantics property",
1496                        idx,
1497                        properties.len().saturating_sub(1),
1498                    )
1499                })?;
1500                let property = &mut parent.properties[internal_idx];
1501
1502                if check_bitmask!(action.semantics, 0x1) {
1503                    property.setter = Some(new_meth);
1504                    *member_idx = MethodMemberIndex::PropertySetter(internal_idx);
1505                } else if check_bitmask!(action.semantics, 0x2) {
1506                    property.getter = Some(new_meth);
1507                    *member_idx = MethodMemberIndex::PropertyGetter(internal_idx);
1508                } else if check_bitmask!(action.semantics, 0x4) {
1509                    property.other.push(new_meth);
1510                    *member_idx = MethodMemberIndex::PropertyOther {
1511                        property: internal_idx,
1512                        other: property.other.len() - 1,
1513                    };
1514                }
1515            }
1516        }
1517    }
1518
1519    Ok(())
1520}
1521
1522fn apply_method_semantics_work<'a>(
1523    types: &mut [types::TypeDefinition<'a>],
1524    methods: &mut [MethodIndex],
1525    works: &[TypeSemanticsWork],
1526    type_base: usize,
1527    method_base: usize,
1528    properties: &[(usize, usize)],
1529    events: &[(usize, usize)],
1530) -> Result<()> {
1531    if works.is_empty() {
1532        return Ok(());
1533    }
1534
1535    const SEQUENTIAL_WORK_THRESHOLD: usize = 8;
1536
1537    if works.len() <= SEQUENTIAL_WORK_THRESHOLD {
1538        for work in works {
1539            let Some(type_local_idx) = work.parent_type.checked_sub(type_base).filter(|idx| *idx < types.len()) else {
1540                dll_bail!(resolve_oob(
1541                    "method semantics parent type",
1542                    work.parent_type,
1543                    type_base + types.len().saturating_sub(1),
1544                ));
1545            };
1546
1547            let Some(method_start) = work
1548                .method_start
1549                .checked_sub(method_base)
1550                .filter(|idx| *idx <= methods.len())
1551            else {
1552                dll_bail!(resolve_oob(
1553                    "method semantics start index",
1554                    work.method_start,
1555                    method_base + methods.len(),
1556                ));
1557            };
1558
1559            let Some(method_end) = work
1560                .method_end
1561                .checked_sub(method_base)
1562                .filter(|idx| *idx <= methods.len())
1563            else {
1564                dll_bail!(resolve_oob(
1565                    "method semantics end index",
1566                    work.method_end,
1567                    method_base + methods.len(),
1568                ));
1569            };
1570
1571            let parent = &mut types[type_local_idx];
1572            let methods_for_type = methods
1573                .get_mut(method_start..method_end)
1574                .ok_or_else(|| resolve_index_arithmetic("invalid method range for method semantics"))?;
1575
1576            apply_method_semantics_for_type(
1577                work.parent_type,
1578                parent,
1579                methods_for_type,
1580                work.method_start,
1581                &work.actions,
1582                properties,
1583                events,
1584            )?;
1585        }
1586
1587        return Ok(());
1588    }
1589
1590    let mid = works.len() / 2;
1591    let split_type = works[mid]
1592        .parent_type
1593        .checked_sub(type_base)
1594        .ok_or_else(|| resolve_index_arithmetic("invalid type split for method semantics"))?;
1595    let split_method = works[mid]
1596        .method_start
1597        .checked_sub(method_base)
1598        .ok_or_else(|| resolve_index_arithmetic("invalid method split for method semantics"))?;
1599
1600    let (left_types, right_types) = types.split_at_mut(split_type);
1601    let (left_methods, right_methods) = methods.split_at_mut(split_method);
1602    let (left_works, right_works) = works.split_at(mid);
1603
1604    let (left_result, right_result) = rayon::join(
1605        || {
1606            apply_method_semantics_work(
1607                left_types,
1608                left_methods,
1609                left_works,
1610                type_base,
1611                method_base,
1612                properties,
1613                events,
1614            )
1615        },
1616        || {
1617            apply_method_semantics_work(
1618                right_types,
1619                right_methods,
1620                right_works,
1621                type_base + split_type,
1622                method_base + split_method,
1623                properties,
1624                events,
1625            )
1626        },
1627    );
1628
1629    left_result?;
1630    right_result
1631}
1632
1633fn apply_method_semantics<'a>(
1634    types: &mut [types::TypeDefinition<'a>],
1635    tables: &metadata::table::Tables,
1636    methods: &mut [MethodIndex],
1637    properties: &[(usize, usize)],
1638    events: &[(usize, usize)],
1639    sem_by_event: &HashMap<usize, Vec<(u16, usize)>>,
1640    sem_by_property: &HashMap<usize, Vec<(u16, usize)>>,
1641) -> Result<()> {
1642    // Batch semantics extraction by parent type so each type's method-index map is built once.
1643    // Within a type, extract in descending internal-index order so swap_remove does not
1644    // invalidate any method positions that are still waiting to be removed.
1645    let mut remaining_event_counts: HashMap<(usize, u16, usize), usize> =
1646        HashMap::with_capacity_and_hasher(tables.method_semantics.len(), Default::default());
1647    let mut remaining_property_counts: HashMap<(usize, u16, usize), usize> =
1648        HashMap::with_capacity_and_hasher(tables.method_semantics.len(), Default::default());
1649
1650    for (&event_idx, entries) in sem_by_event {
1651        for &(semantics, raw_method_idx) in entries {
1652            *remaining_event_counts
1653                .entry((event_idx, semantics, raw_method_idx))
1654                .or_insert(0) += 1;
1655        }
1656    }
1657
1658    for (&property_idx, entries) in sem_by_property {
1659        for &(semantics, raw_method_idx) in entries {
1660            *remaining_property_counts
1661                .entry((property_idx, semantics, raw_method_idx))
1662                .or_insert(0) += 1;
1663        }
1664    }
1665
1666    let mut semantics_by_type: HashMap<usize, Vec<SemanticsAction>> =
1667        HashMap::with_capacity_and_hasher(types.len(), Default::default());
1668
1669    for s in &tables.method_semantics {
1670        use metadata::index::HasSemantics;
1671
1672        let raw_method_idx = s.method.0 - 1;
1673
1674        let (association, include) = match s.association {
1675            HasSemantics::Event(i) => {
1676                let idx = i - 1;
1677                let key = (idx, s.semantics, raw_method_idx);
1678                let include = match remaining_event_counts.get_mut(&key) {
1679                    Some(count) if *count > 0 => {
1680                        *count -= 1;
1681                        true
1682                    }
1683                    _ => false,
1684                };
1685                (SemanticsAssociation::Event(idx), include)
1686            }
1687            HasSemantics::Property(i) => {
1688                let idx = i - 1;
1689                let key = (idx, s.semantics, raw_method_idx);
1690                let include = match remaining_property_counts.get_mut(&key) {
1691                    Some(count) if *count > 0 => {
1692                        *count -= 1;
1693                        true
1694                    }
1695                    _ => false,
1696                };
1697                (SemanticsAssociation::Property(idx), include)
1698            }
1699            HasSemantics::Null => {
1700                dll_bail!(resolve_bad_target("method semantics association cannot be null"));
1701            }
1702        };
1703
1704        if !include {
1705            continue;
1706        }
1707
1708        let Some(&method_idx) = methods.get(raw_method_idx) else {
1709            dll_bail!(resolve_oob(
1710                "method semantics method",
1711                raw_method_idx,
1712                methods.len().saturating_sub(1),
1713            ));
1714        };
1715
1716        semantics_by_type
1717            .entry(method_idx.parent_type.0)
1718            .or_default()
1719            .push(SemanticsAction {
1720                raw_method_idx,
1721                semantics: s.semantics,
1722                association,
1723            });
1724    }
1725
1726    let mut work_by_type = semantics_by_type
1727        .into_iter()
1728        .map(|(parent_type, actions)| {
1729            let type_idx = TypeIndex(parent_type);
1730            let method_start = methods.partition_point(|m| m.parent_type < type_idx);
1731            let method_end = methods.partition_point(|m| m.parent_type <= type_idx);
1732
1733            TypeSemanticsWork {
1734                parent_type,
1735                method_start,
1736                method_end,
1737                actions,
1738            }
1739        })
1740        .collect::<Vec<_>>();
1741    work_by_type.sort_unstable_by_key(|work| work.parent_type);
1742
1743    apply_method_semantics_work(types, methods, &work_by_type, 0, 0, properties, events)
1744}
1745
1746struct DecodedMemberRefs<'a> {
1747    field_refs: Vec<members::ExternalFieldReference<'a>>,
1748    field_map: HashMap<usize, usize>,
1749    method_refs: Vec<members::ExternalMethodReference<'a>>,
1750    method_map: HashMap<usize, usize>,
1751    sig_pending_ref: Vec<crate::binary::metadata::index::Blob>,
1752}
1753
1754enum DecodedMemberRef<'a> {
1755    Field(members::ExternalFieldReference<'a>),
1756    Method {
1757        method_ref: members::ExternalMethodReference<'a>,
1758        blob_idx: Option<crate::binary::metadata::index::Blob>,
1759    },
1760    Skip,
1761}
1762
1763/// # Panics
1764/// Panics only if internal metadata-routing invariants are broken (for example, impossible null
1765/// generic/security owners after prior validation), indicating inconsistent crate logic.
1766fn decode_member_refs<'a>(
1767    tables: &metadata::table::Tables,
1768    strings: &StringsReader<'a>,
1769    blobs: &BlobReader<'a>,
1770    module_refs: &[module::ExternalModuleReference<'a>],
1771    methods: &[MethodIndex],
1772    ctx: &convert::read::Context<'_, 'a>,
1773    opts: Options,
1774) -> Result<DecodedMemberRefs<'a>> {
1775    use rayon::prelude::*;
1776
1777    let decoded = tables
1778        .member_ref
1779        .par_iter()
1780        .with_min_len(MIN_PAR_LEN)
1781        .map(|r| {
1782            use members::*;
1783            use metadata::index::{MemberRefParent, TypeDefOrRef};
1784
1785            let name = strings.at_index(r.name)?.into();
1786            let sig_blob = blobs.at_index(r.signature)?;
1787
1788            let Some(&sig_kind) = sig_blob.first() else {
1789                return Ok(DecodedMemberRef::Skip);
1790            };
1791
1792            if sig_kind == 0x06 {
1793                use crate::binary::signature::kinds::FieldSig;
1794
1795                // NOTE: discarding errors means wasted allocation of formatted messages
1796                let field_sig: FieldSig = match sig_blob.pread(0) {
1797                    Ok(s) => s,
1798                    Err(_) => return Ok(DecodedMemberRef::Skip),
1799                };
1800
1801                let parent = match r.class {
1802                    MemberRefParent::TypeDef(i) => {
1803                        FieldReferenceParent::Type(convert::read::type_idx(TypeDefOrRef::TypeDef(i), ctx)?)
1804                    }
1805                    MemberRefParent::TypeRef(i) => {
1806                        FieldReferenceParent::Type(convert::read::type_idx(TypeDefOrRef::TypeRef(i), ctx)?)
1807                    }
1808                    MemberRefParent::TypeSpec(i) => {
1809                        FieldReferenceParent::Type(convert::read::type_idx(TypeDefOrRef::TypeSpec(i), ctx)?)
1810                    }
1811                    MemberRefParent::ModuleRef(i) => {
1812                        let idx = i - 1;
1813                        if idx < module_refs.len() {
1814                            FieldReferenceParent::Module(ModuleRefIndex(idx))
1815                        } else {
1816                            dll_bail!(resolve_oob(
1817                                "field reference module reference",
1818                                idx,
1819                                module_refs.len().saturating_sub(1),
1820                            ));
1821                        }
1822                    }
1823                    _ => return Ok(DecodedMemberRef::Skip),
1824                };
1825
1826                let field_type = MemberType::from_sig(field_sig.field_type, ctx)?;
1827                let mut custom_modifiers = Vec::with_capacity(field_sig.custom_modifiers.len());
1828                for c in field_sig.custom_modifiers {
1829                    custom_modifiers.push(convert::read::custom_modifier(c, ctx)?);
1830                }
1831
1832                Ok(DecodedMemberRef::Field(ExternalFieldReference {
1833                    attributes: vec![],
1834                    parent,
1835                    name,
1836                    custom_modifiers,
1837                    field_type,
1838                }))
1839            } else {
1840                use crate::binary::signature::kinds::{CallingConvention, MethodRefSig};
1841
1842                let (signature, blob_idx) = if opts.lazy_method_signatures {
1843                    (Default::default(), Some(r.signature))
1844                } else {
1845                    // NOTE: discarding errors means wasted allocation of formatted messages
1846                    let ref_sig: MethodRefSig = match sig_blob.pread(0) {
1847                        Ok(s) => s,
1848                        Err(_) => return Ok(DecodedMemberRef::Skip),
1849                    };
1850
1851                    let mut sig = convert::read::managed_method(ref_sig.method_def, ctx)?;
1852                    if sig.calling_convention == CallingConvention::Vararg {
1853                        let mut varargs = Vec::with_capacity(ref_sig.varargs.len());
1854                        for p in ref_sig.varargs {
1855                            varargs.push(convert::read::parameter(p, ctx)?);
1856                        }
1857                        sig.varargs = Some(varargs);
1858                    }
1859                    (sig, None)
1860                };
1861
1862                let parent = match r.class {
1863                    MemberRefParent::TypeDef(i) => {
1864                        MethodReferenceParent::Type(convert::read::type_idx(TypeDefOrRef::TypeDef(i), ctx)?)
1865                    }
1866                    MemberRefParent::TypeRef(i) => {
1867                        MethodReferenceParent::Type(convert::read::type_idx(TypeDefOrRef::TypeRef(i), ctx)?)
1868                    }
1869                    MemberRefParent::TypeSpec(i) => {
1870                        MethodReferenceParent::Type(convert::read::type_idx(TypeDefOrRef::TypeSpec(i), ctx)?)
1871                    }
1872                    MemberRefParent::ModuleRef(i) => {
1873                        let idx = i - 1;
1874                        if idx < module_refs.len() {
1875                            MethodReferenceParent::Module(ModuleRefIndex(idx))
1876                        } else {
1877                            dll_bail!(resolve_oob(
1878                                "method reference module reference",
1879                                idx,
1880                                module_refs.len().saturating_sub(1),
1881                            ));
1882                        }
1883                    }
1884                    MemberRefParent::MethodDef(i) => {
1885                        let idx = i - 1;
1886                        match methods.get(idx) {
1887                            Some(&m) => MethodReferenceParent::VarargMethod(m),
1888                            None => {
1889                                dll_bail!(resolve_oob(
1890                                    "method reference method definition",
1891                                    idx,
1892                                    methods.len().saturating_sub(1),
1893                                ));
1894                            }
1895                        }
1896                    }
1897                    MemberRefParent::Null => {
1898                        dll_bail!(resolve_bad_target("method reference parent cannot be null"));
1899                    }
1900                };
1901
1902                Ok(DecodedMemberRef::Method {
1903                    method_ref: ExternalMethodReference {
1904                        attributes: vec![],
1905                        parent,
1906                        name,
1907                        signature,
1908                    },
1909                    blob_idx,
1910                })
1911            }
1912        })
1913        .collect::<Vec<Result<_>>>();
1914    let decoded = decoded.into_iter().collect::<Result<Vec<_>>>()?;
1915
1916    let member_ref_len = tables.member_ref.len();
1917    let mut field_refs = Vec::with_capacity(member_ref_len);
1918    let mut field_map = HashMap::with_capacity_and_hasher(member_ref_len, Default::default());
1919    let mut method_refs = Vec::with_capacity(member_ref_len);
1920    let mut method_map = HashMap::with_capacity_and_hasher(member_ref_len, Default::default());
1921    let mut sig_pending_ref: Vec<crate::binary::metadata::index::Blob> = if opts.lazy_method_signatures {
1922        Vec::with_capacity(member_ref_len)
1923    } else {
1924        vec![]
1925    };
1926
1927    for (orig_idx, decoded_ref) in decoded.into_iter().enumerate() {
1928        match decoded_ref {
1929            DecodedMemberRef::Field(field_ref) => {
1930                let current_idx = field_refs.len();
1931                field_map.insert(orig_idx, current_idx);
1932                field_refs.push(field_ref);
1933            }
1934            DecodedMemberRef::Method { method_ref, blob_idx } => {
1935                let current_idx = method_refs.len();
1936                method_map.insert(orig_idx, current_idx);
1937                if let Some(blob_idx) = blob_idx {
1938                    sig_pending_ref.push(blob_idx);
1939                }
1940                method_refs.push(method_ref);
1941            }
1942            DecodedMemberRef::Skip => {}
1943        }
1944    }
1945
1946    Ok(DecodedMemberRefs {
1947        field_refs,
1948        field_map,
1949        method_refs,
1950        method_map,
1951        sig_pending_ref,
1952    })
1953}
1954
1955#[allow(clippy::too_many_lines, clippy::nonminimal_bool)]
1956pub(crate) fn read_impl<'a>(dll: &DLL<'a>, opts: Options) -> Result<Resolution<'a>> {
1957    let (strings, blobs, guids, userstrings, logical) = dll.get_all_streams()?;
1958    let tables = logical.tables;
1959
1960    let def_len = tables.type_def.len();
1961    let ref_len = tables.type_ref.len();
1962    let ctx = convert::read::Context {
1963        def_len,
1964        ref_len,
1965        specs: &tables.type_spec,
1966        sigs: &tables.stand_alone_sig,
1967        blobs: &blobs,
1968        userstrings: &userstrings,
1969    };
1970
1971    stage_start!(stage_timer);
1972    debug!("assembly");
1973    let mut assembly = decode_assembly(&tables, &strings, &blobs)?;
1974    stage_end!(stage_timer, "assembly");
1975
1976    stage_start!(stage_timer);
1977    debug!("assembly refs");
1978    let assembly_refs = decode_assembly_refs(&tables, &strings, &blobs)?;
1979    stage_end!(stage_timer, "assembly refs");
1980
1981    stage_start!(stage_timer);
1982    debug!("type definitions");
1983    let mut types = decode_type_definitions(&tables, &strings, &ctx)?;
1984    stage_end!(stage_timer, "type definitions");
1985
1986    debug!("nested types");
1987
1988    for n in &tables.nested_class {
1989        let nest_idx = n.nested_class.0 - 1;
1990        match types.get_mut(nest_idx) {
1991            Some(t) => {
1992                let enclose_idx = n.enclosing_class.0 - 1;
1993                if enclose_idx < tables.type_def.len() {
1994                    t.encloser = Some(TypeIndex(enclose_idx));
1995                } else {
1996                    dll_bail!(resolve_oob(
1997                        "nested class enclosing type",
1998                        enclose_idx,
1999                        tables.type_def.len().saturating_sub(1),
2000                    ));
2001                }
2002            }
2003            None => {
2004                dll_bail!(resolve_oob(
2005                    "nested class type",
2006                    nest_idx,
2007                    types.len().saturating_sub(1),
2008                ));
2009            }
2010        }
2011    }
2012
2013    debug!("files");
2014    let files = decode_files(&tables, &strings, &blobs)?;
2015
2016    debug!("resources");
2017    let resources = decode_resources(dll, &tables, &strings, &files, &assembly_refs)?;
2018
2019    debug!("exported types");
2020    let exports = decode_exported_types(&tables, &strings, &files, &assembly_refs)?;
2021
2022    let module = decode_module(&tables, &strings, &guids)?;
2023
2024    debug!("resolving module {}", module.name);
2025
2026    let module_refs = decode_module_refs(&tables, &strings)?;
2027
2028    debug!("type refs");
2029    let type_refs = decode_type_refs(&tables, &strings, &module_refs, &assembly_refs)?;
2030
2031    debug!("interfaces");
2032    let interface_idxs = decode_interfaces(&tables, &mut types, &ctx)?;
2033
2034    stage_start!(stage_timer);
2035    debug!("fields");
2036    let fields = decode_fields(&mut types, &tables, &strings, &blobs, &ctx)?;
2037    stage_end!(stage_timer, "fields");
2038
2039    debug!("field layout");
2040
2041    {
2042        use rayon::prelude::*;
2043
2044        let field_layout_updates = tables
2045            .field_layout
2046            .par_iter()
2047            .with_min_len(MIN_PAR_LEN)
2048            .map(|layout| {
2049                let idx = layout.field.0 - 1;
2050                match fields.get(idx) {
2051                    Some(&field) => Ok((field, layout.offset as usize)),
2052                    None => Err(resolve_oob(
2053                        "field layout parent field",
2054                        idx,
2055                        fields.len().saturating_sub(1),
2056                    )),
2057                }
2058            })
2059            .collect::<Vec<Result<_>>>()
2060            .into_iter()
2061            .collect::<Result<Vec<_>>>()?;
2062
2063        for (field, offset) in field_layout_updates {
2064            get_field_mut(&mut types, field).offset = Some(offset);
2065        }
2066    }
2067
2068    debug!("field rva");
2069
2070    {
2071        use rayon::prelude::*;
2072
2073        let field_rva_updates = tables
2074            .field_rva
2075            .par_iter()
2076            .with_min_len(MIN_PAR_LEN)
2077            .map(|rva| {
2078                let idx = rva.field.0 - 1;
2079                match fields.get(idx) {
2080                    Some(&field) => Ok((field, dll.raw_rva(rva.rva)?.into())),
2081                    None => Err(resolve_oob(
2082                        "field RVA parent field",
2083                        idx,
2084                        fields.len().saturating_sub(1),
2085                    )),
2086                }
2087            })
2088            .collect::<Vec<Result<_>>>()
2089            .into_iter()
2090            .collect::<Result<Vec<_>>>()?;
2091
2092        for (field, initial_value) in field_rva_updates {
2093            get_field_mut(&mut types, field).initial_value = Some(initial_value);
2094        }
2095    }
2096
2097    stage_start!(stage_timer);
2098    debug!("methods");
2099    let DecodedMethods {
2100        mut methods,
2101        owned_params,
2102        sig_pending_def,
2103    } = decode_methods(&mut types, &tables, &strings, &blobs, &ctx, opts)?;
2104    stage_end!(stage_timer, "methods");
2105
2106    // only should be used before the event/method semantics phase
2107    // since before then we know member index is a Method(usize)
2108    macro_rules! get_method {
2109        ($unwrap:expr) => {{
2110            let MethodIndex { parent_type, member } = $unwrap;
2111            &mut types[parent_type.0].methods[match member {
2112                MethodMemberIndex::Method(i) => i,
2113                _ => {
2114                    // Invariant: this helper is only used before semantics rewriting, while
2115                    // all entries are still `MethodMemberIndex::Method`.
2116                    debug_assert!(false, "unreachable get_method! saw non-method member variant");
2117                    unreachable!()
2118                }
2119            }]
2120        }};
2121    }
2122
2123    debug!("pinvoke");
2124
2125    {
2126        use members::*;
2127        use metadata::index::MemberForwarded;
2128        use rayon::prelude::*;
2129
2130        enum PInvokeTarget {
2131            Field(FieldIndex),
2132            Method(MethodIndex),
2133        }
2134
2135        let pinvoke_updates = tables
2136            .impl_map
2137            .par_iter()
2138            .with_min_len(MIN_PAR_LEN)
2139            .map(|i| {
2140                let name = heap_idx!(strings, i.import_name);
2141
2142                let value = PInvoke {
2143                    no_mangle: check_bitmask!(i.mapping_flags, 0x1),
2144                    character_set: match i.mapping_flags & 0x6 {
2145                        0x0 => CharacterSet::NotSpecified,
2146                        0x2 => CharacterSet::Ansi,
2147                        0x4 => CharacterSet::Unicode,
2148                        0x6 => CharacterSet::Auto,
2149                        bad => {
2150                            dll_bail!(validity_bad_flags("PInvoke character set specifier", u32::from(bad)));
2151                        }
2152                    },
2153                    supports_last_error: check_bitmask!(i.mapping_flags, 0x40),
2154                    calling_convention: match i.mapping_flags & 0x700 {
2155                        0x100 => UnmanagedCallingConvention::Platformapi,
2156                        0x200 => UnmanagedCallingConvention::Cdecl,
2157                        0x300 => UnmanagedCallingConvention::Stdcall,
2158                        0x400 => UnmanagedCallingConvention::Thiscall,
2159                        0x500 => UnmanagedCallingConvention::Fastcall,
2160                        bad => {
2161                            dll_bail!(validity_bad_flags(
2162                                "PInvoke calling convention specifier",
2163                                u32::from(bad),
2164                            ));
2165                        }
2166                    },
2167                    import_name: name.clone(),
2168                    import_scope: {
2169                        let idx = i.import_scope.0 - 1;
2170
2171                        if idx < module_refs.len() {
2172                            ModuleRefIndex(idx)
2173                        } else {
2174                            dll_bail!(resolve_oob(
2175                                "PInvoke import scope module reference",
2176                                idx,
2177                                module_refs.len().saturating_sub(1),
2178                            ));
2179                        }
2180                    },
2181                };
2182
2183                let target = match i.member_forwarded {
2184                    MemberForwarded::Field(i) => {
2185                        let idx = i - 1;
2186
2187                        match fields.get(idx) {
2188                            Some(&i) => PInvokeTarget::Field(i),
2189                            None => {
2190                                dll_bail!(resolve_oob(
2191                                    "PInvoke forwarded field",
2192                                    idx,
2193                                    fields.len().saturating_sub(1),
2194                                ));
2195                            }
2196                        }
2197                    }
2198                    MemberForwarded::MethodDef(i) => {
2199                        let idx = i - 1;
2200
2201                        match methods.get(idx) {
2202                            Some(&m) => PInvokeTarget::Method(m),
2203                            None => {
2204                                dll_bail!(resolve_oob(
2205                                    "PInvoke forwarded method",
2206                                    idx,
2207                                    methods.len().saturating_sub(1),
2208                                ));
2209                            }
2210                        }
2211                    }
2212                    MemberForwarded::Null => {
2213                        dll_bail!(resolve_bad_target("PInvoke member_forwarded cannot be null"));
2214                    }
2215                };
2216
2217                Ok((target, value))
2218            })
2219            .collect::<Vec<Result<_>>>()
2220            .into_iter()
2221            .collect::<Result<Vec<_>>>()?;
2222
2223        for (target, value) in pinvoke_updates {
2224            match target {
2225                PInvokeTarget::Field(field) => get_field_mut(&mut types, field).pinvoke = Some(value),
2226                PInvokeTarget::Method(method) => get_method!(method).pinvoke = Some(value),
2227            }
2228        }
2229    }
2230
2231    debug!("security");
2232
2233    for s in &tables.decl_security {
2234        use attribute::*;
2235        use metadata::index::HasDeclSecurity;
2236
2237        let parent = match s.parent {
2238            HasDeclSecurity::TypeDef(t) => {
2239                let t_idx = t - 1;
2240                match types.get_mut(t_idx) {
2241                    Some(t) => &mut t.security,
2242                    None => {
2243                        dll_bail!(resolve_oob(
2244                            "security declaration type parent",
2245                            t_idx,
2246                            types.len().saturating_sub(1),
2247                        ));
2248                    }
2249                }
2250            }
2251            HasDeclSecurity::MethodDef(m) => {
2252                let m_idx = m - 1;
2253                match methods.get(m_idx) {
2254                    Some(&m) => &mut get_method!(m).security,
2255                    None => {
2256                        dll_bail!(resolve_oob(
2257                            "security declaration method parent",
2258                            m_idx,
2259                            methods.len().saturating_sub(1),
2260                        ));
2261                    }
2262                }
2263            }
2264            HasDeclSecurity::Assembly(_) => match &mut assembly {
2265                Some(a) => &mut a.security,
2266                None => dll_bail!(resolve_missing_row("Assembly", 0)),
2267            },
2268            HasDeclSecurity::Null => {
2269                dll_bail!(resolve_bad_target("security declaration parent cannot be null"));
2270            }
2271        };
2272
2273        *parent = Some(SecurityDeclaration {
2274            attributes: vec![],
2275            action: s.action,
2276            value: heap_idx!(blobs, s.permission_set),
2277        });
2278    }
2279
2280    debug!("generic parameters");
2281
2282    let mut constraint_map =
2283        HashMap::with_capacity_and_hasher(tables.generic_param_constraint.len(), Default::default());
2284
2285    // this table is supposed to be sorted by owner and number (ECMA-335, II.22, page 210)
2286    // thus no need to sort the generics by sequence after the fact
2287    for (param_idx, p) in tables.generic_param.iter().enumerate() {
2288        use metadata::index::TypeOrMethodDef;
2289
2290        let name = heap_idx!(strings, p.name);
2291
2292        match p.owner {
2293            TypeOrMethodDef::TypeDef(i) => {
2294                let idx = i - 1;
2295                match types.get_mut(idx) {
2296                    Some(t) => {
2297                        t.generic_parameters.push(make_generic(
2298                            name,
2299                            p,
2300                            param_idx,
2301                            &mut constraint_map,
2302                            &tables,
2303                            &ctx,
2304                        )?);
2305                    }
2306                    None => {
2307                        dll_bail!(resolve_oob(
2308                            "generic parameter type owner",
2309                            idx,
2310                            types.len().saturating_sub(1),
2311                        ));
2312                    }
2313                }
2314            }
2315            TypeOrMethodDef::MethodDef(i) => {
2316                let idx = i - 1;
2317                let method = match methods.get(idx) {
2318                    Some(&m) => get_method!(m),
2319                    None => {
2320                        dll_bail!(resolve_oob(
2321                            "generic parameter method owner",
2322                            idx,
2323                            methods.len().saturating_sub(1),
2324                        ));
2325                    }
2326                };
2327
2328                method
2329                    .generic_parameters
2330                    .push(make_generic(name, p, param_idx, &mut constraint_map, &tables, &ctx)?);
2331            }
2332            TypeOrMethodDef::Null => {
2333                dll_bail!(resolve_bad_target("generic parameter owner cannot be null"));
2334            }
2335        }
2336    }
2337
2338    stage_start!(stage_timer);
2339    debug!("params");
2340    let params = decode_params(&mut types, &tables, &strings, &methods, owned_params)?;
2341    stage_end!(stage_timer, "params");
2342
2343    debug!("field marshal");
2344
2345    {
2346        use crate::binary::{metadata::index::HasFieldMarshal, signature::kinds::MarshalSpec};
2347        use rayon::prelude::*;
2348
2349        let field_marshal_updates = tables
2350            .field_marshal
2351            .par_iter()
2352            .with_min_len(MIN_PAR_LEN)
2353            .filter_map(|marshal| match marshal.parent {
2354                HasFieldMarshal::Field(_) => Some(marshal),
2355                _ => None,
2356            })
2357            .map(|marshal| {
2358                let idx = match marshal.parent {
2359                    HasFieldMarshal::Field(i) => i - 1,
2360                    _ => {
2361                        // Invariant: the iterator is filtered to `HasFieldMarshal::Field` above.
2362                        debug_assert!(false, "unreachable field_marshal update with non-field parent");
2363                        unreachable!()
2364                    }
2365                };
2366                match fields.get(idx) {
2367                    Some(&field) => Ok((field, heap_idx!(blobs, marshal.native_type).pread::<MarshalSpec>(0)?)),
2368                    None => Err(resolve_oob("field marshal field", idx, fields.len().saturating_sub(1))),
2369                }
2370            })
2371            .collect::<Vec<Result<_>>>()
2372            .into_iter()
2373            .collect::<Result<Vec<_>>>()?;
2374
2375        for (field, marshal) in field_marshal_updates {
2376            get_field_mut(&mut types, field).marshal = Some(marshal);
2377        }
2378
2379        for marshal in &tables.field_marshal {
2380            match marshal.parent {
2381                HasFieldMarshal::Field(_) => {}
2382                HasFieldMarshal::Param(i) => {
2383                    let value = Some(heap_idx!(blobs, marshal.native_type).pread::<MarshalSpec>(0)?);
2384                    let idx = i - 1;
2385                    match params.get(idx) {
2386                        Some(&(m_idx, p_idx)) => {
2387                            let method = get_method!(methods[m_idx]);
2388
2389                            let param_meta = if p_idx == 0 {
2390                                &mut method.return_type_metadata
2391                            } else {
2392                                &mut method.parameter_metadata[p_idx - 1]
2393                            };
2394
2395                            param_meta
2396                                .as_mut()
2397                                .ok_or(ResolveError::MissingRow {
2398                                    table: "Param",
2399                                    index: idx,
2400                                })?
2401                                .marshal = value;
2402                        }
2403                        None => {
2404                            dll_bail!(resolve_oob(
2405                                "field marshal parameter",
2406                                idx,
2407                                params.len().saturating_sub(1),
2408                            ));
2409                        }
2410                    }
2411                }
2412                HasFieldMarshal::Null => {
2413                    dll_bail!(resolve_bad_target("field marshal parent cannot be null"));
2414                }
2415            }
2416        }
2417    }
2418
2419    stage_start!(stage_timer);
2420    debug!("properties");
2421    let DecodedProperties {
2422        properties,
2423        sig_pending_property,
2424    } = decode_properties(&mut types, &tables, &strings, &blobs, &ctx, opts)?;
2425    stage_end!(stage_timer, "properties");
2426
2427    debug!("constants");
2428
2429    for c in &tables.constant {
2430        use crate::binary::signature::encoded::*;
2431        use members::Constant::*;
2432        use metadata::index::HasConstant;
2433
2434        let blob = heap_idx!(blobs, c.value);
2435
2436        let value = Some(match c.constant_type {
2437            ELEMENT_TYPE_BOOLEAN => Boolean(blob.pread_with::<u8>(0, scroll::LE)? == 1),
2438            ELEMENT_TYPE_CHAR => Char(blob.pread_with(0, scroll::LE)?),
2439            ELEMENT_TYPE_I1 => Int8(blob.pread_with(0, scroll::LE)?),
2440            ELEMENT_TYPE_U1 => UInt8(blob.pread_with(0, scroll::LE)?),
2441            ELEMENT_TYPE_I2 => Int16(blob.pread_with(0, scroll::LE)?),
2442            ELEMENT_TYPE_U2 => UInt16(blob.pread_with(0, scroll::LE)?),
2443            ELEMENT_TYPE_I4 => Int32(blob.pread_with(0, scroll::LE)?),
2444            ELEMENT_TYPE_U4 => UInt32(blob.pread_with(0, scroll::LE)?),
2445            ELEMENT_TYPE_I8 => Int64(blob.pread_with(0, scroll::LE)?),
2446            ELEMENT_TYPE_U8 => UInt64(blob.pread_with(0, scroll::LE)?),
2447            ELEMENT_TYPE_R4 => Float32(blob.pread_with(0, scroll::LE)?),
2448            ELEMENT_TYPE_R8 => Float64(blob.pread_with(0, scroll::LE)?),
2449            ELEMENT_TYPE_STRING => {
2450                let num_utf16 = blob.len() / 2;
2451                let mut offset = 0;
2452                let chars = (0..num_utf16)
2453                    .map(|_| blob.gread_with(&mut offset, scroll::LE))
2454                    .collect::<scroll::Result<Vec<_>>>()?;
2455                String(chars)
2456            }
2457            ELEMENT_TYPE_CLASS => {
2458                let t: u32 = blob.pread_with(0, scroll::LE)?;
2459                if t == 0 {
2460                    Null
2461                } else {
2462                    dll_bail!(
2463                        ParseError::BadStructure("constant ELEMENT_TYPE_CLASS payload must be null reference"),
2464                    );
2465                }
2466            }
2467            bad => dll_bail!(ParseError::BadElementType { tag: bad }),
2468        });
2469
2470        match c.parent {
2471            HasConstant::Field(i) => {
2472                let f_idx = i - 1;
2473
2474                match fields.get(f_idx) {
2475                    Some(&i) => get_field_mut(&mut types, i).default = value,
2476                    None => {
2477                        dll_bail!(resolve_oob(
2478                            "constant field parent",
2479                            f_idx,
2480                            fields.len().saturating_sub(1),
2481                        ));
2482                    }
2483                }
2484            }
2485            HasConstant::Param(i) => {
2486                let p_idx = i - 1;
2487
2488                match params.get(p_idx) {
2489                    Some(&(parent, internal)) => {
2490                        let method = get_method!(methods[parent]);
2491
2492                        let param_meta = if internal == 0 {
2493                            &mut method.return_type_metadata
2494                        } else {
2495                            &mut method.parameter_metadata[internal - 1]
2496                        };
2497
2498                        param_meta
2499                            .as_mut()
2500                            .ok_or(ResolveError::MissingRow {
2501                                table: "Param",
2502                                index: p_idx,
2503                            })?
2504                            .default = value;
2505                    }
2506                    None => {
2507                        dll_bail!(resolve_oob(
2508                            "constant parameter parent",
2509                            p_idx,
2510                            params.len().saturating_sub(1),
2511                        ));
2512                    }
2513                }
2514            }
2515            HasConstant::Property(i) => {
2516                let f_idx = i - 1;
2517
2518                match properties.get(f_idx) {
2519                    Some(&(parent, internal)) => {
2520                        types[parent].properties[internal].default = value;
2521                    }
2522                    None => {
2523                        dll_bail!(resolve_oob(
2524                            "constant property parent",
2525                            f_idx,
2526                            properties.len().saturating_sub(1),
2527                        ));
2528                    }
2529                }
2530            }
2531            HasConstant::Null => {
2532                dll_bail!(resolve_bad_target("constant parent cannot be null"));
2533            }
2534        }
2535    }
2536
2537    // sorts in preparation for the binary search in extract_method
2538    methods.sort_unstable_by_key(|m| m.parent_type);
2539
2540    let mut sem_by_event: HashMap<usize, Vec<(u16, usize)>> =
2541        HashMap::with_capacity_and_hasher(tables.event.len(), Default::default());
2542    let mut sem_by_property: HashMap<usize, Vec<(u16, usize)>> =
2543        HashMap::with_capacity_and_hasher(tables.property.len(), Default::default());
2544
2545    for s in &tables.method_semantics {
2546        use metadata::index::HasSemantics;
2547
2548        let method_idx = s.method.0 - 1;
2549        match s.association {
2550            HasSemantics::Event(e) => sem_by_event.entry(e - 1).or_default().push((s.semantics, method_idx)),
2551            HasSemantics::Property(p) => sem_by_property
2552                .entry(p - 1)
2553                .or_default()
2554                .push((s.semantics, method_idx)),
2555            HasSemantics::Null => {}
2556        }
2557    }
2558
2559    stage_start!(stage_timer);
2560    debug!("events");
2561    let events = decode_events(&mut types, &tables, &strings, &ctx, &mut sem_by_event, &mut methods)?;
2562    stage_end!(stage_timer, "events");
2563
2564    stage_start!(stage_timer);
2565    debug!("method semantics");
2566    apply_method_semantics(
2567        &mut types,
2568        &tables,
2569        &mut methods,
2570        &properties,
2571        &events,
2572        &sem_by_event,
2573        &sem_by_property,
2574    )?;
2575    stage_end!(stage_timer, "method semantics");
2576
2577    stage_start!(stage_timer);
2578    debug!("field refs / method refs");
2579    let DecodedMemberRefs {
2580        field_refs,
2581        field_map,
2582        method_refs,
2583        method_map,
2584        sig_pending_ref,
2585    } = decode_member_refs(&tables, &strings, &blobs, &module_refs, &methods, &ctx, opts)?;
2586    stage_end!(stage_timer, "field refs / method refs");
2587
2588    let m_ctx = convert::read::MethodContext {
2589        field_map: &field_map,
2590        field_indices: &fields,
2591        method_specs: &tables.method_spec,
2592        method_indices: &methods,
2593        method_map: &method_map,
2594    };
2595
2596    debug!("method impl");
2597
2598    for i in &tables.method_impl {
2599        use types::*;
2600
2601        let idx = i.class.0 - 1;
2602        match types.get_mut(idx) {
2603            Some(t) => t.overrides.push(MethodOverride {
2604                implementation: convert::read::user_method(i.method_body, &m_ctx)?,
2605                declaration: convert::read::user_method(i.method_declaration, &m_ctx)?,
2606            }),
2607            None => {
2608                dll_bail!(resolve_oob(
2609                    "method override parent type",
2610                    idx,
2611                    types.len().saturating_sub(1),
2612                ));
2613            }
2614        }
2615    }
2616
2617    use metadata::{
2618        index::{Token, TokenTarget},
2619        table::Kind,
2620    };
2621
2622    let entry_token = dll.cli.entry_point_token.to_le_bytes().pread::<Token>(0)?;
2623
2624    let mut res = Resolution {
2625        assembly,
2626        assembly_references: assembly_refs,
2627        entry_point: if entry_token.index == 0 {
2628            None
2629        } else {
2630            let entry_idx = entry_token.index - 1;
2631            Some(match entry_token.target {
2632                TokenTarget::Table(Kind::MethodDef) => match methods.get(entry_idx) {
2633                    Some(&m) => EntryPoint::Method(m),
2634                    None => {
2635                        dll_bail!(resolve_oob(
2636                            "entry point method",
2637                            entry_idx,
2638                            methods.len().saturating_sub(1),
2639                        ));
2640                    }
2641                },
2642                TokenTarget::Table(Kind::File) => {
2643                    if entry_idx < files.len() {
2644                        EntryPoint::File(FileIndex(entry_idx))
2645                    } else {
2646                        dll_bail!(resolve_oob(
2647                            "entry point file",
2648                            entry_idx,
2649                            files.len().saturating_sub(1),
2650                        ));
2651                    }
2652                }
2653                _ => dll_bail!(resolve_bad_target("entry point token must target MethodDef or File")),
2654            })
2655        },
2656        exported_types: exports,
2657        field_references: field_refs,
2658        files,
2659        manifest_resources: resources,
2660        method_references: method_refs,
2661        module,
2662        module_references: module_refs,
2663        type_definitions: types,
2664        type_references: type_refs,
2665        lazy_state: None,
2666    };
2667
2668    stage_start!(stage_timer);
2669    debug!("custom attributes");
2670
2671    if !opts.lazy_attributes {
2672        for (idx, a) in tables.custom_attribute.iter().enumerate() {
2673            use attribute::*;
2674            use members::UserMethod;
2675            use metadata::index::{CustomAttributeType, HasCustomAttribute::*};
2676
2677            let attr = Attribute {
2678                constructor: match a.attr_type {
2679                    CustomAttributeType::MethodDef(i) => {
2680                        let m_idx = i - 1;
2681                        match methods.get(m_idx) {
2682                            Some(&m) => UserMethod::Definition(m),
2683                            None => {
2684                                dll_bail!(resolve_oob(
2685                                    "custom attribute constructor method",
2686                                    m_idx,
2687                                    methods.len().saturating_sub(1),
2688                                ));
2689                            }
2690                        }
2691                    }
2692                    CustomAttributeType::MemberRef(i) => {
2693                        let r_idx = i - 1;
2694                        match method_map.get(&r_idx) {
2695                            Some(&m_idx) => UserMethod::Reference(MethodRefIndex(m_idx)),
2696                            None => {
2697                                dll_bail!(resolve_oob(
2698                                    "custom attribute constructor member reference",
2699                                    r_idx,
2700                                    method_map.len().saturating_sub(1),
2701                                ));
2702                            }
2703                        }
2704                    }
2705                    CustomAttributeType::Null => {
2706                        dll_bail!(resolve_bad_target("custom attribute constructor cannot be null"));
2707                    }
2708                },
2709                value: optional_idx!(blobs, a.value),
2710            };
2711
2712            // panicking indexers after the indexes from the attribute are okay here,
2713            // since they've already been checked during resolution
2714
2715            macro_rules! do_at_generic {
2716                ($g:expr, |$capt:ident| $do:expr) => {{
2717                    use metadata::index::TypeOrMethodDef;
2718                    let g = $g;
2719                    match g.owner {
2720                        TypeOrMethodDef::TypeDef(t) => {
2721                            let $capt = &mut res.type_definitions[t - 1].generic_parameters[g.number as usize];
2722                            $do;
2723                        }
2724                        TypeOrMethodDef::MethodDef(m) => {
2725                            let $capt = &mut res[methods[m - 1]].generic_parameters[g.number as usize];
2726                            $do;
2727                        }
2728                        TypeOrMethodDef::Null => {
2729                            // Invariant: GenericParam owner rows are resolved to concrete type/method
2730                            // definitions before this point.
2731                            debug_assert!(false, "unreachable custom attribute generic owner is null");
2732                            unreachable!()
2733                        },
2734                    }
2735                }};
2736            }
2737
2738            match a.parent {
2739                MethodDef(i) => {
2740                    let m_idx = i - 1;
2741                    match methods.get(m_idx) {
2742                        Some(&m) => res[m].attributes.push(attr),
2743                        None => {
2744                            dll_bail!(resolve_oob(
2745                                "custom attribute parent method",
2746                                m_idx,
2747                                methods.len().saturating_sub(1),
2748                            ));
2749                        }
2750                    }
2751                }
2752                Field(i) => {
2753                    let f_idx = i - 1;
2754                    match fields.get(f_idx) {
2755                        Some(&i) => res[i].attributes.push(attr),
2756                        None => {
2757                            dll_bail!(resolve_oob(
2758                                "custom attribute parent field",
2759                                f_idx,
2760                                fields.len().saturating_sub(1),
2761                            ));
2762                        }
2763                    }
2764                }
2765                TypeRef(i) => {
2766                    let r_idx = i - 1;
2767                    match res.type_references.get_mut(r_idx) {
2768                        Some(r) => r.attributes.push(attr),
2769                        None => {
2770                            dll_bail!(resolve_oob(
2771                                "custom attribute parent type reference",
2772                                r_idx,
2773                                res.type_references.len().saturating_sub(1),
2774                            ));
2775                        }
2776                    }
2777                }
2778                TypeDef(i) => {
2779                    let t_idx = i - 1;
2780                    match res.type_definitions.get_mut(t_idx) {
2781                        Some(t) => t.attributes.push(attr),
2782                        None => {
2783                            dll_bail!(resolve_oob(
2784                                "custom attribute parent type definition",
2785                                t_idx,
2786                                res.type_definitions.len().saturating_sub(1),
2787                            ));
2788                        }
2789                    }
2790                }
2791                Param(i) => {
2792                    let p_idx = i - 1;
2793                    match params.get(p_idx) {
2794                        Some(&(parent, internal)) => {
2795                            let method = &mut res[methods[parent]];
2796
2797                            let param_meta = if internal == 0 {
2798                                &mut method.return_type_metadata
2799                            } else {
2800                                &mut method.parameter_metadata[internal - 1]
2801                            };
2802
2803                            param_meta
2804                                .as_mut()
2805                                .ok_or(ResolveError::MissingRow {
2806                                    table: "Param",
2807                                    index: p_idx,
2808                                })?
2809                                .attributes
2810                                .push(attr);
2811                        }
2812                        None => {
2813                            dll_bail!(resolve_oob(
2814                                "custom attribute parent parameter",
2815                                p_idx,
2816                                params.len().saturating_sub(1),
2817                            ));
2818                        }
2819                    }
2820                }
2821                InterfaceImpl(i) => {
2822                    let i_idx = i - 1;
2823
2824                    match interface_idxs.get(i_idx) {
2825                        Some(&(parent, internal)) => res.type_definitions[parent].implements[internal].0.push(attr),
2826                        None => {
2827                            dll_bail!(resolve_oob(
2828                                "custom attribute parent interface implementation",
2829                                i_idx,
2830                                interface_idxs.len().saturating_sub(1),
2831                            ));
2832                        }
2833                    }
2834                }
2835                MemberRef(i) => {
2836                    let m_idx = i - 1;
2837
2838                    match field_map.get(&m_idx) {
2839                        Some(&f) => res.field_references[f].attributes.push(attr),
2840                        None => match method_map.get(&m_idx) {
2841                            Some(&m) => res.method_references[m].attributes.push(attr),
2842                            None => {
2843                                dll_bail!(resolve_oob(
2844                                    "custom attribute parent member reference",
2845                                    m_idx,
2846                                    tables.member_ref.len().saturating_sub(1),
2847                                ));
2848                            }
2849                        },
2850                    }
2851                }
2852                Module(_) => res.module.attributes.push(attr),
2853                DeclSecurity(i) => {
2854                    use metadata::index::HasDeclSecurity;
2855
2856                    let s_idx = i - 1;
2857
2858                    match tables.decl_security.get(s_idx) {
2859                        Some(s) => match s.parent {
2860                            HasDeclSecurity::TypeDef(t) => res.type_definitions[t - 1]
2861                                .security
2862                                .as_mut()
2863                                .ok_or(ResolveError::MissingRow {
2864                                    table: "DeclSecurity",
2865                                    index: s_idx,
2866                                })?
2867                                .attributes
2868                                .push(attr),
2869                            HasDeclSecurity::MethodDef(m) => res[methods[m - 1]]
2870                                .security
2871                                .as_mut()
2872                                .ok_or(ResolveError::MissingRow {
2873                                    table: "DeclSecurity",
2874                                    index: s_idx,
2875                                })?
2876                                .attributes
2877                                .push(attr),
2878                            HasDeclSecurity::Assembly(_) => res
2879                                .assembly
2880                                .as_mut()
2881                                .and_then(|a| a.security.as_mut())
2882                                .ok_or(ResolveError::MissingRow {
2883                                    table: "DeclSecurity",
2884                                    index: s_idx,
2885                                })?
2886                                .attributes
2887                                .push(attr),
2888                            HasDeclSecurity::Null => {
2889                                // Invariant: custom attributes over DeclSecurity rows must target a
2890                                // concrete security owner (TypeDef/MethodDef/Assembly).
2891                                debug_assert!(false, "unreachable custom attribute DeclSecurity owner is null");
2892                                unreachable!()
2893                            },
2894                        },
2895                        None => {
2896                            dll_bail!(resolve_oob(
2897                                "custom attribute parent security declaration",
2898                                s_idx,
2899                                tables.decl_security.len().saturating_sub(1),
2900                            ));
2901                        }
2902                    }
2903                }
2904                Property(i) => {
2905                    let p_idx = i - 1;
2906
2907                    match properties.get(p_idx) {
2908                        Some(&(parent, internal)) => {
2909                            res.type_definitions[parent].properties[internal].attributes.push(attr)
2910                        }
2911                        None => {
2912                            dll_bail!(resolve_oob(
2913                                "custom attribute parent property",
2914                                p_idx,
2915                                properties.len().saturating_sub(1),
2916                            ));
2917                        }
2918                    }
2919                }
2920                Event(i) => {
2921                    let e_idx = i - 1;
2922
2923                    match events.get(e_idx) {
2924                        Some(&(parent, internal)) => {
2925                            res.type_definitions[parent].events[internal].attributes.push(attr)
2926                        }
2927                        None => {
2928                            dll_bail!(resolve_oob(
2929                                "custom attribute parent event",
2930                                e_idx,
2931                                events.len().saturating_sub(1),
2932                            ));
2933                        }
2934                    }
2935                }
2936                ModuleRef(i) => {
2937                    let m_idx = i - 1;
2938
2939                    match res.module_references.get_mut(m_idx) {
2940                        Some(m) => m.attributes.push(attr),
2941                        None => {
2942                            dll_bail!(resolve_oob(
2943                                "custom attribute parent module reference",
2944                                m_idx,
2945                                res.module_references.len().saturating_sub(1),
2946                            ));
2947                        }
2948                    }
2949                }
2950                Assembly(_) => match res.assembly.as_mut() {
2951                    Some(a) => a.attributes.push(attr),
2952                    None => dll_bail!(resolve_missing_row("Assembly", 0)),
2953                },
2954                AssemblyRef(i) => {
2955                    let r_idx = i - 1;
2956
2957                    match res.assembly_references.get_mut(r_idx) {
2958                        Some(a) => a.attributes.push(attr),
2959                        None => {
2960                            dll_bail!(resolve_oob(
2961                                "custom attribute parent assembly reference",
2962                                r_idx,
2963                                res.assembly_references.len().saturating_sub(1),
2964                            ));
2965                        }
2966                    }
2967                }
2968                File(i) => {
2969                    let f_idx = i - 1;
2970
2971                    match res.files.get_mut(f_idx) {
2972                        Some(f) => f.attributes.push(attr),
2973                        None => {
2974                            dll_bail!(resolve_oob(
2975                                "custom attribute parent file",
2976                                f_idx,
2977                                res.files.len().saturating_sub(1),
2978                            ));
2979                        }
2980                    }
2981                }
2982                ExportedType(i) => {
2983                    let e_idx = i - 1;
2984
2985                    match res.exported_types.get_mut(e_idx) {
2986                        Some(e) => e.attributes.push(attr),
2987                        None => {
2988                            dll_bail!(resolve_oob(
2989                                "custom attribute parent exported type",
2990                                e_idx,
2991                                res.exported_types.len().saturating_sub(1),
2992                            ));
2993                        }
2994                    }
2995                }
2996                ManifestResource(i) => {
2997                    let r_idx = i - 1;
2998
2999                    match res.manifest_resources.get_mut(r_idx) {
3000                        Some(r) => r.attributes.push(attr),
3001                        None => {
3002                            dll_bail!(resolve_oob(
3003                                "custom attribute parent manifest resource",
3004                                r_idx,
3005                                res.manifest_resources.len().saturating_sub(1),
3006                            ));
3007                        }
3008                    }
3009                }
3010                GenericParam(i) => {
3011                    let g_idx = i - 1;
3012
3013                    match tables.generic_param.get(g_idx) {
3014                        Some(g) => do_at_generic!(g, |rg| rg.attributes.push(attr)),
3015                        None => {
3016                            dll_bail!(resolve_oob(
3017                                "custom attribute parent generic parameter",
3018                                g_idx,
3019                                tables.generic_param.len().saturating_sub(1),
3020                            ));
3021                        }
3022                    }
3023                }
3024                GenericParamConstraint(i) => {
3025                    let g_idx = i - 1;
3026
3027                    match constraint_map.get(&g_idx) {
3028                        Some(&(generic, internal)) => do_at_generic!(tables.generic_param[generic], |g| g
3029                            .type_constraints[internal]
3030                            .attributes
3031                            .push(attr)),
3032                        None => {
3033                            dll_bail!(resolve_oob(
3034                                "custom attribute parent generic parameter constraint",
3035                                g_idx,
3036                                tables.generic_param_constraint.len().saturating_sub(1),
3037                            ));
3038                        }
3039                    }
3040                }
3041                MethodSpec(_) => {
3042                    warn!(
3043                        "custom attribute {} has a MethodSpec parent, this is not supported by dotnetdll",
3044                        idx
3045                    );
3046                }
3047                StandAloneSig(_) => {
3048                    warn!(
3049                        "custom attribute {} has a StandAloneSig parent, this is not supported by dotnetdll",
3050                        idx
3051                    );
3052                }
3053                TypeSpec(_) => {
3054                    warn!(
3055                        "custom attribute {} has a TypeSpec parent, this is not supported by dotnetdll",
3056                        idx
3057                    );
3058                }
3059                Null => {
3060                    dll_bail!(resolve_bad_target("custom attribute parent cannot be null"));
3061                }
3062            }
3063        }
3064    } // end if !opts.lazy_attributes
3065    stage_end!(stage_timer, "custom attributes");
3066
3067    if opts.lazy_method_bodies || opts.lazy_method_signatures || opts.lazy_property_signatures || opts.lazy_attributes {
3068        stage_start!(stage_timer);
3069        debug!("building lazy parse state");
3070
3071        // Build sparse attribute maps in one pass before the LazyParseState literal.
3072        let (attr_by_type, attr_by_method, attr_by_field, attr_by_assembly) = if opts.lazy_attributes {
3073            use crate::binary::metadata::index::{CustomAttributeType, HasCustomAttribute};
3074            use crate::resolved::members::UserMethod;
3075
3076            let mut by_type: HashMap<usize, Vec<lazy::AttrRaw>> = HashMap::default();
3077            let mut by_method: HashMap<usize, Vec<lazy::AttrRaw>> = HashMap::default();
3078            let mut by_field: HashMap<usize, Vec<lazy::AttrRaw>> = HashMap::default();
3079            let mut by_asm: Vec<lazy::AttrRaw> = Vec::new();
3080
3081            for a in &tables.custom_attribute {
3082                let constructor = match a.attr_type {
3083                    CustomAttributeType::MethodDef(i) => UserMethod::Definition(methods[i - 1]),
3084                    CustomAttributeType::MemberRef(i) => {
3085                        let m_idx = match method_map.get(&(i - 1)) {
3086                            Some(&m) => m,
3087                            None => continue,
3088                        };
3089                        UserMethod::Reference(MethodRefIndex(m_idx))
3090                    }
3091                    CustomAttributeType::Null => continue,
3092                };
3093                let blob_idx = if a.value.is_null() { None } else { Some(a.value) };
3094                let raw = (constructor, blob_idx);
3095                match a.parent {
3096                    HasCustomAttribute::TypeDef(i) => by_type.entry(i - 1).or_default().push(raw),
3097                    HasCustomAttribute::MethodDef(i) => by_method.entry(i - 1).or_default().push(raw),
3098                    HasCustomAttribute::Field(i) => by_field.entry(i - 1).or_default().push(raw),
3099                    HasCustomAttribute::Assembly(_) => by_asm.push(raw),
3100                    _ => {}
3101                }
3102            }
3103            (by_type, by_method, by_field, by_asm)
3104        } else {
3105            (HashMap::default(), HashMap::default(), HashMap::default(), Vec::new())
3106        };
3107
3108        let n = tables.method_def.len();
3109        let (pending, method_idx_to_def, body_cache) = if opts.lazy_method_bodies {
3110            debug!("  lazy method bodies");
3111            let mut pending = Vec::with_capacity(n);
3112            let mut method_idx_to_def: HashMap<MethodIndex, usize> =
3113                HashMap::with_capacity_and_hasher(n, Default::default());
3114            let mut body_cache = Vec::with_capacity(n);
3115
3116            for (def_idx, m) in tables.method_def.iter().enumerate() {
3117                if m.rva != 0 {
3118                    let (bytes, offset) = dll.method_bytes(m)?;
3119                    pending.push(Some(lazy::MethodPendingRaw { bytes, offset }));
3120                    method_idx_to_def.insert(methods[def_idx], def_idx);
3121                } else {
3122                    pending.push(None);
3123                }
3124                body_cache.push(std::sync::OnceLock::new());
3125            }
3126            (pending, method_idx_to_def, body_cache)
3127        } else {
3128            (vec![], HashMap::with_capacity_and_hasher(0, Default::default()), vec![])
3129        };
3130
3131        let (
3132            method_spec_table,
3133            field_map_for_bodies,
3134            field_indices_for_bodies,
3135            method_indices_for_bodies,
3136            method_map_for_bodies,
3137        ) = if opts.lazy_method_bodies {
3138            (
3139                tables.method_spec.clone(),
3140                field_map.clone(),
3141                fields.clone(),
3142                methods.clone(),
3143                method_map.clone(),
3144            )
3145        } else {
3146            (
3147                vec![],
3148                HashMap::with_capacity_and_hasher(0, Default::default()),
3149                vec![],
3150                vec![],
3151                HashMap::with_capacity_and_hasher(0, Default::default()),
3152            )
3153        };
3154
3155        // Signature MethodIndex -> method_def_idx map must be built AFTER method semantics,
3156        // because semantics may rewrite MethodIndex::member variants. Defer map construction
3157        // to first `method_signature` access; keep def-order MethodIndex rows as source data.
3158        let sig_method_indices = if opts.lazy_method_signatures && !opts.lazy_method_bodies {
3159            methods.clone()
3160        } else {
3161            vec![]
3162        };
3163
3164        let sig_property_indices = if opts.lazy_property_signatures {
3165            properties
3166                .iter()
3167                .map(|&(parent, property)| PropertyIndex {
3168                    parent_type: TypeIndex(parent),
3169                    property,
3170                })
3171                .collect()
3172        } else {
3173            vec![]
3174        };
3175
3176        res.lazy_state = Some(Arc::new(lazy::LazyParseState {
3177            lazy_bodies: opts.lazy_method_bodies,
3178            lazy_signatures: opts.lazy_method_signatures,
3179            lazy_property_signatures: opts.lazy_property_signatures,
3180            lazy_attributes: opts.lazy_attributes,
3181            def_len,
3182            ref_len,
3183            specs: tables.type_spec.clone(),
3184            sigs: tables.stand_alone_sig.clone(),
3185            method_spec_table,
3186            blobs,
3187            userstrings,
3188            field_map: field_map_for_bodies,
3189            field_indices: field_indices_for_bodies,
3190            method_indices: method_indices_for_bodies,
3191            method_map: method_map_for_bodies,
3192            pending,
3193            method_idx_to_def,
3194            body_cache,
3195            sig_method_indices,
3196            sig_method_idx_to_def: std::sync::OnceLock::new(),
3197            sig_pending_def,
3198            sig_cache_def: std::sync::OnceLock::new(),
3199            sig_pending_ref,
3200            sig_cache_ref: std::sync::OnceLock::new(),
3201            sig_property_indices,
3202            sig_property_idx_to_def: std::sync::OnceLock::new(),
3203            sig_pending_property,
3204            sig_cache_property: std::sync::OnceLock::new(),
3205            attr_by_type,
3206            attr_by_method,
3207            attr_by_field,
3208            attr_by_assembly,
3209            attr_method_idx_to_def: if opts.lazy_attributes {
3210                methods.iter().enumerate().map(|(i, &m)| (m, i)).collect()
3211            } else {
3212                rustc_hash::FxHashMap::default()
3213            },
3214            attr_field_idx_to_def: if opts.lazy_attributes {
3215                fields.iter().enumerate().map(|(i, &f)| (f, i)).collect()
3216            } else {
3217                rustc_hash::FxHashMap::default()
3218            },
3219        }));
3220        stage_end!(stage_timer, "lazy state construction");
3221    }
3222
3223    if !opts.lazy_method_bodies && !opts.skip_method_bodies {
3224        stage_start!(stage_timer);
3225        debug!("method bodies");
3226
3227        use rayon::prelude::*;
3228
3229        let bodies: Vec<(MethodIndex, body::Method)> = tables
3230            .method_def
3231            .par_iter()
3232            .with_min_len(MIN_PAR_LEN)
3233            .enumerate()
3234            .filter(|(_, m)| m.rva != 0)
3235            .map(|(idx, m)| -> Result<(MethodIndex, body::Method)> {
3236                let raw_body = dll.get_method(m)?;
3237                let body = lazy::decode_body_with_ctx(raw_body, &ctx, &m_ctx)?;
3238                Ok((methods[idx], body))
3239            })
3240            .collect::<Vec<Result<_>>>()
3241            .into_iter()
3242            .collect::<Result<Vec<_>>>()?;
3243
3244        for (method_idx, body) in bodies {
3245            res[method_idx].body = Some(body);
3246        }
3247        stage_end!(stage_timer, "method bodies");
3248    } // end if !opts.lazy_method_bodies && !opts.skip_method_bodies
3249
3250    debug!("resolved module {}", res.module.name);
3251
3252    Ok(res)
3253}