dotscope 0.6.0

A high-performance, cross-platform framework for analyzing and reverse engineering .NET PE executables
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
//! Dependency analysis and extraction from .NET metadata.
//!
//! This module provides functionality to analyze .NET metadata tables and extract
//! dependency relationships that feed into the assembly dependency graph. It serves
//! as the bridge between raw metadata table entries and the structured dependency
//! representation used for multi-assembly analysis.

use std::sync::Arc;

use crate::{
    metadata::{
        dependencies::{
            AssemblyDependency, AssemblyDependencyGraph, DependencyResolutionState,
            DependencySource, DependencyType, VersionRequirement,
        },
        identity::{AssemblyIdentity, AssemblyVersion, Identity, ProcessorArchitecture},
        loader::LoaderContext,
        tables::{AssemblyRefRaw, File, FileRaw, ModuleRef, ModuleRefRaw},
    },
    Error, Result,
};

/// Perform dependency analysis on a loaded assembly context.
///
/// This function analyzes the metadata tables in a LoaderContext to extract
/// dependency relationships and populate a dependency graph. It should be called
/// after all metadata table loaders have completed successfully.
///
/// # Arguments
/// * `context` - The loader context containing loaded metadata tables
/// * `dependency_graph` - The dependency graph to populate with discovered relationships
///
/// # Returns
/// * `Ok(())` - Dependency analysis completed successfully
/// * `Err(_)` - Error occurred during dependency analysis
///
/// # Examples
///
/// ```rust,ignore
/// use dotscope::metadata::dependencies::{AssemblyDependencyGraph, perform_dependency_analysis};
/// use dotscope::metadata::loader::LoaderContext;
///
/// let dependency_graph = Arc::new(AssemblyDependencyGraph::new());
///
/// // After loading metadata tables...
/// perform_dependency_analysis(&context, dependency_graph.clone())?;
///
/// // Now analyze the dependency graph
/// if let Some(cycles) = dependency_graph.find_cycles()? {
///     println!("Circular dependencies detected: {:?}", cycles);
/// }
/// # Ok::<(), dotscope::Error>(())
/// ```
pub(crate) fn perform_dependency_analysis(
    context: &LoaderContext,
    dependency_graph: Arc<AssemblyDependencyGraph>,
) -> Result<()> {
    let analyzer = DependencyAnalyzer::new(dependency_graph);
    analyzer.analyze_all_dependencies(context)
}

/// Analyzer for extracting dependency relationships from .NET metadata.
///
/// This component processes metadata tables during assembly loading to identify
/// and extract dependency relationships. It works in coordination with the metadata
/// loader system to build the complete dependency graph as assemblies are processed.
///
/// # Integration with Loading Pipeline
///
/// The analyzer integrates with the metadata loading system:
/// 1. **Table Loaders** call analyzer methods as they process metadata
/// 2. **LoaderContext** provides access to metadata tables and storage
/// 3. **AssemblyDependencyGraph** receives the extracted dependencies
/// 4. **Concurrent Processing** ensures thread-safe dependency extraction
///
/// # Dependency Sources
///
/// The analyzer processes multiple metadata tables:
/// - **AssemblyRef**: External assembly references (primary source)
/// - **ModuleRef**: External module references (multi-module scenarios)
/// - **File**: File references (multi-file assemblies)
///
/// # Usage Patterns
///
/// ## Integration with Loaders
///
/// ```rust,ignore
/// // In a table loader (e.g., AssemblyRefLoader)
/// impl MetadataLoader for AssemblyRefLoader {
///     fn load(&self, context: &LoaderContext) -> Result<()> {
///         // ... existing loading logic ...
///         
///         // Extract dependencies for graph building
///         if let Some(analyzer) = &context.dependency_analyzer {
///             analyzer.analyze_assembly_references(context)?;
///         }
///         
///         Ok(())
///     }
/// }
/// ```
///
/// ## Standalone Analysis
///
/// ```rust,ignore
/// use dotscope::metadata::dependencies::{DependencyAnalyzer, AssemblyDependencyGraph};
///
/// let graph = AssemblyDependencyGraph::new();
/// let analyzer = DependencyAnalyzer::new(graph);
///
/// // Analyze specific metadata tables
/// analyzer.analyze_assembly_references(&context)?;
/// analyzer.analyze_module_references(&context)?;
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// The analyzer is designed for concurrent operation:
/// - **Stateless Design**: No internal mutable state
/// - **Arc-based Sharing**: Safe sharing across threads
/// - **Lock-free Operations**: Minimal synchronization overhead
/// - **Concurrent Graph Updates**: Thread-safe dependency graph modifications
pub struct DependencyAnalyzer {
    /// Reference to the dependency graph being populated
    ///
    /// The analyzer adds discovered dependencies to this graph as it
    /// processes metadata tables. The graph handles thread synchronization
    /// and concurrent access from multiple analyzer instances.
    graph: Arc<AssemblyDependencyGraph>,
}

impl DependencyAnalyzer {
    /// Create a new dependency analyzer for the specified graph.
    ///
    /// The analyzer will add all discovered dependencies to the provided
    /// graph. Multiple analyzers can share the same graph for concurrent
    /// processing of different assemblies or metadata tables.
    ///
    /// # Arguments
    /// * `graph` - The dependency graph to populate with discovered dependencies
    ///
    /// # Returns
    /// A new analyzer instance ready to process metadata tables
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use dotscope::metadata::dependencies::{DependencyAnalyzer, AssemblyDependencyGraph};
    /// use std::sync::Arc;
    ///
    /// let graph = Arc::new(AssemblyDependencyGraph::new());
    /// let analyzer = DependencyAnalyzer::new(graph);
    /// ```
    #[must_use]
    pub fn new(graph: Arc<AssemblyDependencyGraph>) -> Self {
        Self { graph }
    }

    /// Analyze AssemblyRef table entries to extract assembly dependencies.
    ///
    /// Processes all AssemblyRef entries in the metadata to identify external
    /// assembly dependencies. This is the primary source of dependency information
    /// for most .NET assemblies.
    ///
    /// # Arguments
    /// * `context` - Loader context with access to metadata tables and heaps
    ///
    /// # Returns
    /// * `Ok(())` - Analysis completed successfully
    /// * `Err(_)` - Error occurred during dependency analysis
    ///
    /// # Processing Logic
    ///
    /// For each AssemblyRef entry:
    /// 1. Extract target assembly identity (name, version, culture, strong name)
    /// 2. Determine dependency type (standard reference, friend assembly, etc.)
    /// 3. Create dependency relationship with source assembly context
    /// 4. Add to dependency graph for cycle detection and ordering
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// // Called from AssemblyRefLoader during metadata processing
    /// analyzer.analyze_assembly_references(&context)?;
    /// # Ok::<(), dotscope::Error>(())
    /// ```
    pub(crate) fn analyze_assembly_references(&self, context: &LoaderContext) -> Result<()> {
        if let (Some(header), Some(strings), Some(blobs)) =
            (context.meta, context.strings, context.blobs)
        {
            if let Some(table) = header.table::<AssemblyRefRaw>() {
                // Get the source assembly identity from the current context
                let source_identity = Self::extract_current_assembly_identity(context)?;

                for row in table {
                    // Convert raw AssemblyRef to owned representation
                    let assembly_ref = row.to_owned(strings, blobs)?;

                    // Extract target assembly identity
                    let target_identity = AssemblyIdentity::from_assembly_ref(&assembly_ref);

                    // Create dependency relationship
                    let dependency = AssemblyDependency {
                        source: DependencySource::AssemblyRef(assembly_ref),
                        target_identity,
                        dependency_type: DependencyType::Reference, // Standard assembly reference
                        version_requirement: VersionRequirement::Compatible, // Default policy
                        is_optional: false, // AssemblyRef dependencies are typically required
                        resolution_state: DependencyResolutionState::Unresolved, // Will be resolved lazily
                    };

                    // Add to dependency graph with explicit source identity
                    self.graph
                        .add_dependency_with_source(&source_identity, dependency)?;
                }
            }
        }

        Ok(())
    }

    /// Analyze ModuleRef table entries to extract module dependencies.
    ///
    /// Processes ModuleRef entries to identify external module dependencies.
    /// This implementation focuses on tracking dependencies that represent
    /// cross-assembly relationships, filtering out native library references
    /// which don't represent .NET assembly dependencies.
    ///
    /// # ModuleRef Categories
    ///
    /// - **Native Libraries**: System DLLs like kernel32.dll, user32.dll (filtered out)
    /// - **External .NET Modules**: Modules from other assemblies (tracked)
    /// - **Intra-Assembly Modules**: .netmodule files in multi-module assemblies (tracked for completeness)
    /// - **Mixed-Mode Components**: Modules containing both managed and native code (tracked)
    ///
    /// # Arguments
    /// * `context` - Loader context with access to metadata tables and heaps
    ///
    /// # Returns
    /// * `Ok(())` - Analysis completed successfully  
    /// * `Err(_)` - Error occurred during dependency analysis
    ///
    /// # Processing Logic
    ///
    /// For each ModuleRef entry:
    /// 1. Classify module type based on naming conventions
    /// 2. Filter out pure native library references (no .NET dependency)
    /// 3. For .NET modules, create placeholder assembly identity
    /// 4. Create dependency relationship with appropriate classification
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// // Called during dependency analysis
    /// analyzer.analyze_module_references(&context)?;
    /// # Ok::<(), dotscope::Error>(())
    /// ```
    pub(crate) fn analyze_module_references(&self, context: &LoaderContext) -> Result<()> {
        if let (Some(header), Some(strings)) = (context.meta, context.strings) {
            if let Some(table) = header.table::<ModuleRefRaw>() {
                let source_identity = Self::extract_current_assembly_identity(context)?;

                for row in table {
                    // Convert raw ModuleRef to owned representation
                    let module_ref = row.to_owned(strings)?;

                    // Classify module reference type
                    let module_classification = Self::classify_module_ref(&module_ref);

                    match module_classification {
                        ModuleRefType::NativeLibrary => {
                            // Native libraries (kernel32.dll, user32.dll, etc.) don't represent
                            // .NET assembly dependencies, so we skip them for dependency tracking
                        }
                        ModuleRefType::NetModule => {
                            // .netmodule files are part of multi-module assemblies
                            // Create dependency with module context
                            let target_identity =
                                Self::create_module_assembly_identity(&module_ref);

                            let dependency = AssemblyDependency {
                                source: DependencySource::ModuleRef(module_ref),
                                target_identity,
                                dependency_type: DependencyType::Reference,
                                version_requirement: VersionRequirement::Compatible,
                                is_optional: false,
                                resolution_state: DependencyResolutionState::Unresolved,
                            };

                            self.graph
                                .add_dependency_with_source(&source_identity, dependency)?;
                        }
                        ModuleRefType::ExternalAssemblyModule => {
                            // Module from external assembly - create cross-assembly dependency
                            let target_identity =
                                Self::create_module_assembly_identity(&module_ref);

                            let dependency = AssemblyDependency {
                                source: DependencySource::ModuleRef(module_ref),
                                target_identity,
                                dependency_type: DependencyType::Reference,
                                version_requirement: VersionRequirement::Compatible,
                                is_optional: false,
                                resolution_state: DependencyResolutionState::Unresolved,
                            };

                            self.graph
                                .add_dependency_with_source(&source_identity, dependency)?;
                        }
                        ModuleRefType::Unknown => {
                            // Unknown module type - create dependency for completeness with warning
                            let target_identity =
                                Self::create_module_assembly_identity(&module_ref);

                            let dependency = AssemblyDependency {
                                source: DependencySource::ModuleRef(module_ref),
                                target_identity,
                                dependency_type: DependencyType::Reference,
                                version_requirement: VersionRequirement::Any, // More flexible for unknown
                                is_optional: true, // Mark as optional since we're unsure
                                resolution_state: DependencyResolutionState::Unresolved,
                            };

                            self.graph
                                .add_dependency_with_source(&source_identity, dependency)?;
                        }
                    }
                }
            }
        }

        Ok(())
    }

    /// Analyze File table entries to extract file dependencies.
    ///
    /// Processes File table entries to identify files that are part of
    /// multi-file assemblies. While most File entries represent intra-assembly
    /// components, some may indicate external dependencies that should be tracked.
    ///
    /// # File Entry Types
    ///
    /// - **Executable Modules**: .netmodule files with CONTAINS_META_DATA flag
    /// - **Resource Files**: .resources files with CONTAINS_NO_META_DATA flag
    /// - **Native Libraries**: Unmanaged DLLs referenced by the assembly
    /// - **Documentation Files**: XML documentation and other metadata files
    ///
    /// # Arguments
    /// * `context` - Loader context with access to metadata tables and heaps
    ///
    /// # Returns
    /// * `Ok(())` - Analysis completed successfully
    /// * `Err(_)` - Error occurred during dependency analysis
    ///
    /// # Processing Logic
    ///
    /// For each File entry:
    /// 1. Classify file type based on flags and extension
    /// 2. Determine if file represents external dependency
    /// 3. For external files, create appropriate dependency relationship
    /// 4. Track significant files for multi-assembly analysis
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// // Called during dependency analysis
    /// analyzer.analyze_file_references(&context)?;
    /// # Ok::<(), dotscope::Error>(())
    /// ```
    pub(crate) fn analyze_file_references(&self, context: &LoaderContext) -> Result<()> {
        if let (Some(header), Some(strings), Some(blobs)) =
            (context.meta, context.strings, context.blobs)
        {
            if let Some(table) = header.table::<FileRaw>() {
                let source_identity = Self::extract_current_assembly_identity(context)?;

                for row in table {
                    // Convert raw File to owned representation
                    let file_ref = row.to_owned(blobs, strings)?;

                    // Classify file type based on flags and characteristics
                    let file_classification = Self::classify_file_ref(&file_ref);

                    match file_classification {
                        FileRefType::IntraAssemblyModule => {
                            // .netmodule file that's part of this assembly
                            // Track for multi-module assembly completeness
                            let target_identity = Self::create_file_assembly_identity(&file_ref);

                            let dependency = AssemblyDependency {
                                source: DependencySource::File(file_ref),
                                target_identity,
                                dependency_type: DependencyType::Reference,
                                version_requirement: VersionRequirement::Exact, // Same assembly, exact match
                                is_optional: false,
                                resolution_state: DependencyResolutionState::Unresolved,
                            };

                            self.graph
                                .add_dependency_with_source(&source_identity, dependency)?;
                        }
                        FileRefType::ResourceFile => {
                            // Resource files (.resources, .resx, etc.)
                            // Track as resource dependencies
                            let target_identity = Self::create_file_assembly_identity(&file_ref);

                            let dependency = AssemblyDependency {
                                source: DependencySource::File(file_ref),
                                target_identity,
                                dependency_type: DependencyType::Resource,
                                version_requirement: VersionRequirement::Compatible,
                                is_optional: true, // Resources are often optional
                                resolution_state: DependencyResolutionState::Unresolved,
                            };

                            self.graph
                                .add_dependency_with_source(&source_identity, dependency)?;
                        }
                        FileRefType::ExternalAssemblyFile => {
                            // File from external assembly
                            let target_identity = Self::create_file_assembly_identity(&file_ref);

                            let dependency = AssemblyDependency {
                                source: DependencySource::File(file_ref),
                                target_identity,
                                dependency_type: DependencyType::Reference,
                                version_requirement: VersionRequirement::Compatible,
                                is_optional: false,
                                resolution_state: DependencyResolutionState::Unresolved,
                            };

                            self.graph
                                .add_dependency_with_source(&source_identity, dependency)?;
                        }
                        FileRefType::DocumentationFile => {
                            // Documentation files (.xml, .pdb, etc.)
                            // These are typically optional and don't affect loading
                        }
                        FileRefType::Unknown => {
                            // Unknown file type - track with caution
                            let target_identity = Self::create_file_assembly_identity(&file_ref);

                            let dependency = AssemblyDependency {
                                source: DependencySource::File(file_ref),
                                target_identity,
                                dependency_type: DependencyType::Reference,
                                version_requirement: VersionRequirement::Any,
                                is_optional: true, // Unknown files are treated as optional
                                resolution_state: DependencyResolutionState::Unresolved,
                            };

                            self.graph
                                .add_dependency_with_source(&source_identity, dependency)?;
                        }
                    }
                }
            }
        }

        Ok(())
    }

    /// Get a reference to the dependency graph being populated.
    ///
    /// Provides access to the dependency graph for querying current state,
    /// performing analysis, or coordinating with other components.
    ///
    /// # Returns
    /// Shared reference to the dependency graph
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use dotscope::metadata::dependencies::{DependencyAnalyzer, AssemblyDependencyGraph};
    /// use std::sync::Arc;
    ///
    /// let graph = Arc::new(AssemblyDependencyGraph::new());
    /// let analyzer = DependencyAnalyzer::new(graph);
    /// let graph_ref = analyzer.dependency_graph();
    /// println!("Graph has {} dependencies", graph_ref.dependency_count());
    /// ```
    #[must_use]
    pub fn dependency_graph(&self) -> &Arc<AssemblyDependencyGraph> {
        &self.graph
    }

    /// Analyze all supported metadata tables for dependencies.
    ///
    /// Convenience method that runs analysis on all supported metadata tables
    /// in the appropriate order. This is useful for batch processing of
    /// complete assemblies.
    ///
    /// # Arguments
    /// * `context` - Loader context with access to metadata tables and heaps
    ///
    /// # Returns
    /// * `Ok(())` - All analyses completed successfully
    /// * `Err(_)` - Error occurred during dependency analysis
    ///
    /// # Processing Order
    ///
    /// 1. **AssemblyRef** - Primary external assembly dependencies
    /// 2. **ModuleRef** - External module dependencies  
    /// 3. **File** - File dependencies (mostly intra-assembly)
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// // Analyze all dependency sources for an assembly
    /// analyzer.analyze_all_dependencies(&context)?;
    /// # Ok::<(), dotscope::Error>(())
    /// ```
    pub(crate) fn analyze_all_dependencies(&self, context: &LoaderContext) -> Result<()> {
        // Process in order of importance and complexity
        self.analyze_assembly_references(context)?;
        self.analyze_module_references(context)?;
        self.analyze_file_references(context)?;

        Ok(())
    }

    /// Classify a ModuleRef entry to determine its dependency significance.
    ///
    /// Analyzes the module name and characteristics to determine whether it represents
    /// a significant dependency for cross-assembly analysis or should be filtered out.
    ///
    /// # Classification Logic
    ///
    /// - **Native Libraries**: Win32 system DLLs, C runtime libraries (filtered out)
    /// - **.NET Modules**: .netmodule files, managed assemblies (tracked)
    /// - **Mixed Modules**: Components with both managed and native code (tracked)
    /// - **Unknown**: Unrecognized patterns (tracked with caution)
    ///
    /// # Arguments
    /// * `module_ref` - The ModuleRef entry to classify
    ///
    /// # Returns
    /// Classification enum indicating the module type and tracking recommendation
    fn classify_module_ref(module_ref: &ModuleRef) -> ModuleRefType {
        let name = &module_ref.name;

        // Check for common native library patterns
        if Self::is_native_library(name) {
            return ModuleRefType::NativeLibrary;
        }

        // Check for .NET module patterns
        if name.ends_with(".netmodule") {
            return ModuleRefType::NetModule;
        }

        // Check for potential external assembly modules
        if std::path::Path::new(name)
            .extension()
            .is_some_and(|ext| ext.eq_ignore_ascii_case("dll") || ext.eq_ignore_ascii_case("exe"))
        {
            // This could be an external assembly module
            return ModuleRefType::ExternalAssemblyModule;
        }

        // Unknown pattern - track with caution
        ModuleRefType::Unknown
    }

    /// Check if a module name represents a native library rather than a .NET assembly.
    ///
    /// Uses common patterns and naming conventions to identify native system libraries
    /// that don't represent .NET assembly dependencies.
    ///
    /// # Arguments
    /// * `name` - Module name to check
    ///
    /// # Returns
    /// `true` if the module appears to be a native library, `false` otherwise
    fn is_native_library(name: &str) -> bool {
        // Common Windows system libraries
        const NATIVE_LIBRARIES: &[&str] = &[
            "kernel32.dll",
            "user32.dll",
            "gdi32.dll",
            "advapi32.dll",
            "ole32.dll",
            "oleaut32.dll",
            "shell32.dll",
            "comdlg32.dll",
            "comctl32.dll",
            "winmm.dll",
            "msvcrt.dll",
            "ntdll.dll",
            "ws2_32.dll",
            "wininet.dll",
            "crypt32.dll",
            "version.dll",
            "psapi.dll",
            "dbghelp.dll",
            "imagehlp.dll",
            "userenv.dll",
        ];
        const NATIVE_PREFIXES: &[&str] = &["msvcr", "msvcp", "vcruntime", "api-ms-", "ext-ms-"];

        // Convert to lowercase for case-insensitive matching
        let lower_name = name.to_lowercase();

        // Check exact matches first
        if NATIVE_LIBRARIES.iter().any(|&lib| lower_name == lib) {
            return true;
        }

        // Check common prefixes for system libraries
        if NATIVE_PREFIXES
            .iter()
            .any(|&prefix| lower_name.starts_with(prefix))
        {
            return true;
        }

        false
    }

    /// Create an assembly identity for a module reference.
    ///
    /// Since `ModuleRef` entries only contain module names without version information,
    /// this creates an assembly identity with [`AssemblyVersion::UNKNOWN`]. Use
    /// [`AssemblyVersion::is_unknown()`] to detect these dependencies that lack
    /// version information.
    ///
    /// # Arguments
    ///
    /// * `module_ref` - The ModuleRef entry to create identity for
    ///
    /// # Returns
    ///
    /// `AssemblyIdentity` with the module name and `UNKNOWN` version. The version
    /// can be checked with [`AssemblyVersion::is_unknown()`] to identify dependencies
    /// where version binding analysis may not be possible.
    fn create_module_assembly_identity(module_ref: &ModuleRef) -> AssemblyIdentity {
        let assembly_name = if let Some(name_without_ext) = module_ref.name.strip_suffix(".dll") {
            name_without_ext.to_string()
        } else if let Some(name_without_ext) = module_ref.name.strip_suffix(".exe") {
            name_without_ext.to_string()
        } else if let Some(name_without_ext) = module_ref.name.strip_suffix(".netmodule") {
            name_without_ext.to_string()
        } else {
            module_ref.name.clone()
        };

        AssemblyIdentity {
            name: assembly_name,
            version: AssemblyVersion::UNKNOWN,
            culture: None,
            strong_name: None,
            processor_architecture: None,
        }
    }

    /// Classify a File entry to determine its dependency significance.
    ///
    /// Analyzes the file flags, name, and characteristics to determine whether
    /// it represents a significant dependency for cross-assembly analysis.
    ///
    /// # Classification Logic
    ///
    /// - **Executable Modules**: Files with CONTAINS_META_DATA flag (.netmodule)
    /// - **Resource Files**: Files with CONTAINS_NO_META_DATA flag (.resources)
    /// - **External Files**: Files that may be from external assemblies
    /// - **Documentation**: Files that don't affect runtime behavior
    ///
    /// # Arguments
    /// * `file_ref` - The File entry to classify
    ///
    /// # Returns
    /// Classification enum indicating the file type and tracking recommendation
    fn classify_file_ref(file_ref: &File) -> FileRefType {
        // Check file attributes from the File table
        const CONTAINS_META_DATA: u32 = 0x0000;
        const CONTAINS_NO_META_DATA: u32 = 0x0001;

        let name = &file_ref.name;
        let flags = file_ref.flags;
        let path = std::path::Path::new(name);

        // Check for executable modules (.netmodule files)
        if flags == CONTAINS_META_DATA && name.ends_with(".netmodule") {
            return FileRefType::IntraAssemblyModule;
        }

        let Some(extension) = path.extension() else {
            return FileRefType::Unknown;
        };

        // Check for resource files
        if flags == CONTAINS_NO_META_DATA
            && (extension.eq_ignore_ascii_case("resources")
                || extension.eq_ignore_ascii_case("resx"))
        {
            return FileRefType::ResourceFile;
        }

        // Check for documentation files
        if extension.eq_ignore_ascii_case("xml")
            || extension.eq_ignore_ascii_case("pdb")
            || extension.eq_ignore_ascii_case("mdb")
        {
            return FileRefType::DocumentationFile;
        }

        // Check for potential external assembly files
        if extension.eq_ignore_ascii_case("dll") || extension.eq_ignore_ascii_case("exe") {
            return FileRefType::ExternalAssemblyFile;
        }

        // Unknown file type
        FileRefType::Unknown
    }

    /// Create an assembly identity for a file reference.
    ///
    /// Since `File` entries in multi-file assemblies only contain file names without
    /// version information, this creates an assembly identity with [`AssemblyVersion::UNKNOWN`].
    /// Use [`AssemblyVersion::is_unknown()`] to detect these dependencies.
    ///
    /// # Arguments
    ///
    /// * `file_ref` - The File entry to create identity for
    ///
    /// # Returns
    ///
    /// `AssemblyIdentity` with the file name (sans extension) and `UNKNOWN` version.
    /// For most File entries in multi-file assemblies, the actual version should match
    /// the containing assembly's version, but this information is not available in the
    /// File table itself.
    fn create_file_assembly_identity(file_ref: &File) -> AssemblyIdentity {
        let assembly_name = if let Some(name_without_ext) = file_ref.name.strip_suffix(".dll") {
            name_without_ext.to_string()
        } else if let Some(name_without_ext) = file_ref.name.strip_suffix(".exe") {
            name_without_ext.to_string()
        } else if let Some(name_without_ext) = file_ref.name.strip_suffix(".netmodule") {
            name_without_ext.to_string()
        } else if let Some(name_without_ext) = file_ref.name.strip_suffix(".resources") {
            name_without_ext.to_string()
        } else {
            file_ref.name.clone()
        };

        AssemblyIdentity {
            name: assembly_name,
            version: AssemblyVersion::UNKNOWN,
            culture: None,
            strong_name: None,
            processor_architecture: None,
        }
    }

    /// Extract source assembly identity from a loaded assembly context.
    ///
    /// Uses the assembly metadata in the LoaderContext to determine the identity
    /// of the current assembly being processed. This provides the source identity
    /// needed for dependency relationships.
    ///
    /// # Arguments
    ///
    /// * `context` - Loader context with loaded assembly metadata
    ///
    /// # Returns
    ///
    /// * `Ok(AssemblyIdentity)` - The identity of the assembly being analyzed
    /// * `Err(_)` - Assembly metadata not yet loaded in the context
    ///
    /// # Errors
    ///
    /// Returns an error if the assembly metadata has not been loaded into the context.
    /// This ensures that dependency analysis only proceeds when valid assembly information
    /// is available, preventing the creation of dependencies with incorrect source identities.
    fn extract_current_assembly_identity(context: &LoaderContext) -> Result<AssemblyIdentity> {
        let assembly_lock = context.assembly.get().ok_or_else(|| {
            Error::TypeError(
                "Cannot extract assembly identity: assembly metadata not yet loaded. \
                 Dependency analysis requires the Assembly table to be loaded first."
                    .to_string(),
            )
        })?;

        // Convert public key data to Identity if present
        let strong_name = if let Some(ref public_key_data) = assembly_lock.public_key {
            Some(Identity::from(public_key_data, true)?)
        } else {
            None
        };

        // Extract identity from the loaded assembly metadata
        #[allow(clippy::cast_possible_truncation)]
        Ok(AssemblyIdentity {
            name: assembly_lock.name.clone(),
            version: AssemblyVersion::new(
                assembly_lock.major_version as u16,
                assembly_lock.minor_version as u16,
                assembly_lock.build_number as u16,
                assembly_lock.revision_number as u16,
            ),
            culture: assembly_lock.culture.clone(),
            strong_name,
            // Note: This table is rarely present in modern .NET assemblies which use AnyCPU
            processor_architecture: context
                .assembly_processor
                .get()
                .and_then(|proc| ProcessorArchitecture::try_from(proc.processor).ok()),
        })
    }
}

/// Classification of ModuleRef entries for dependency tracking.
///
/// Categorizes module references based on their significance for
/// cross-assembly dependency analysis and resolution.
#[derive(Debug, Clone, PartialEq)]
enum ModuleRefType {
    /// Native library (Win32 DLL, C runtime, etc.)
    ///
    /// These don't represent .NET assembly dependencies and are
    /// typically filtered out of dependency tracking.
    NativeLibrary,

    /// .NET module file (.netmodule)
    ///
    /// Part of a multi-module assembly, should be tracked for
    /// completeness but may be intra-assembly rather than cross-assembly.
    NetModule,

    /// External assembly module
    ///
    /// Module from another .NET assembly that represents a
    /// cross-assembly dependency requiring resolution.
    ExternalAssemblyModule,

    /// Unknown module type
    ///
    /// Unrecognized pattern that should be tracked with caution
    /// and flexible resolution policies.
    Unknown,
}

/// Classification of File entries for dependency tracking.
///
/// Categorizes file references based on their significance for
/// cross-assembly dependency analysis and resolution.
#[derive(Debug, Clone, PartialEq)]
enum FileRefType {
    /// Intra-assembly module file (.netmodule)
    ///
    /// Module that's part of the current assembly in a multi-module
    /// assembly structure. Tracked for completeness.
    IntraAssemblyModule,

    /// Resource file (.resources, .resx)
    ///
    /// Files containing localized strings, images, or other
    /// non-executable resources. Tracked as resource dependencies.
    ResourceFile,

    /// External assembly file
    ///
    /// File from another assembly that represents a cross-assembly
    /// dependency requiring resolution.
    ExternalAssemblyFile,

    /// Documentation file (.xml, .pdb, .mdb)
    ///
    /// Files containing documentation or debug information that
    /// don't affect runtime behavior. Typically filtered out.
    DocumentationFile,

    /// Unknown file type
    ///
    /// Unrecognized file pattern that should be tracked with
    /// caution and flexible resolution policies.
    Unknown,
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test::helpers::dependencies::{create_test_file, create_test_module_ref};

    /// Helper function to create a test analyzer with default settings
    fn create_test_analyzer() -> DependencyAnalyzer {
        let graph = Arc::new(AssemblyDependencyGraph::new());
        DependencyAnalyzer::new(graph)
    }

    #[test]
    fn test_dependency_analyzer_creation() {
        let graph = Arc::new(AssemblyDependencyGraph::new());
        let analyzer = DependencyAnalyzer::new(graph.clone());
        assert_eq!(analyzer.dependency_graph().assembly_count(), 0);
    }

    #[test]
    fn test_classify_module_ref_native_library() {
        let kernel32 = create_test_module_ref("kernel32.dll");
        let classification = DependencyAnalyzer::classify_module_ref(&kernel32);
        assert_eq!(classification, ModuleRefType::NativeLibrary);

        let user32 = create_test_module_ref("user32.dll");
        let classification = DependencyAnalyzer::classify_module_ref(&user32);
        assert_eq!(classification, ModuleRefType::NativeLibrary);

        let msvcrt = create_test_module_ref("msvcrt.dll");
        let classification = DependencyAnalyzer::classify_module_ref(&msvcrt);
        assert_eq!(classification, ModuleRefType::NativeLibrary);
    }

    #[test]
    fn test_classify_module_ref_net_module() {
        let netmodule = create_test_module_ref("MyModule.netmodule");
        let classification = DependencyAnalyzer::classify_module_ref(&netmodule);
        assert_eq!(classification, ModuleRefType::NetModule);
    }

    #[test]
    fn test_classify_module_ref_external_assembly() {
        let dll_module = create_test_module_ref("ExternalLibrary.dll");
        let classification = DependencyAnalyzer::classify_module_ref(&dll_module);
        assert_eq!(classification, ModuleRefType::ExternalAssemblyModule);

        let exe_module = create_test_module_ref("Application.exe");
        let classification = DependencyAnalyzer::classify_module_ref(&exe_module);
        assert_eq!(classification, ModuleRefType::ExternalAssemblyModule);
    }

    #[test]
    fn test_classify_module_ref_unknown() {
        let unknown_module = create_test_module_ref("SomeFile.unknown");
        let classification = DependencyAnalyzer::classify_module_ref(&unknown_module);
        assert_eq!(classification, ModuleRefType::Unknown);
    }

    #[test]
    fn test_is_native_library() {
        // Test exact matches
        assert!(DependencyAnalyzer::is_native_library("kernel32.dll"));
        assert!(DependencyAnalyzer::is_native_library("KERNEL32.DLL")); // Case insensitive
        assert!(DependencyAnalyzer::is_native_library("user32.dll"));
        assert!(DependencyAnalyzer::is_native_library("msvcrt.dll"));

        // Test prefix matches
        assert!(DependencyAnalyzer::is_native_library("msvcr120.dll"));
        assert!(DependencyAnalyzer::is_native_library("msvcp140.dll"));
        assert!(DependencyAnalyzer::is_native_library("vcruntime140.dll"));
        assert!(DependencyAnalyzer::is_native_library(
            "api-ms-win-core-kernel32-l1-1-0.dll"
        ));

        // Test non-native libraries
        assert!(!DependencyAnalyzer::is_native_library("System.dll"));
        assert!(!DependencyAnalyzer::is_native_library("MyAssembly.dll"));
        assert!(!DependencyAnalyzer::is_native_library("Module.netmodule"));
    }

    #[test]
    fn test_create_module_assembly_identity() {
        // Test .dll module
        let dll_module = create_test_module_ref("TestLibrary.dll");
        let identity = DependencyAnalyzer::create_module_assembly_identity(&dll_module);
        assert_eq!(identity.name, "TestLibrary");
        assert_eq!(identity.version, AssemblyVersion::UNKNOWN);
        assert!(identity.version.is_unknown());

        // Test .exe module
        let exe_module = create_test_module_ref("Application.exe");
        let identity = DependencyAnalyzer::create_module_assembly_identity(&exe_module);
        assert_eq!(identity.name, "Application");

        // Test .netmodule
        let netmodule = create_test_module_ref("Module.netmodule");
        let identity = DependencyAnalyzer::create_module_assembly_identity(&netmodule);
        assert_eq!(identity.name, "Module");

        // Test module without extension
        let no_ext_module = create_test_module_ref("SomeModule");
        let identity = DependencyAnalyzer::create_module_assembly_identity(&no_ext_module);
        assert_eq!(identity.name, "SomeModule");
    }

    #[test]
    fn test_classify_file_ref_intra_assembly_module() {
        let mut file = create_test_file("Module.netmodule");
        let file_mut = Arc::get_mut(&mut file).unwrap();
        file_mut.flags = 0x0000; // CONTAINS_META_DATA

        let classification = DependencyAnalyzer::classify_file_ref(&file);
        assert_eq!(classification, FileRefType::IntraAssemblyModule);
    }

    #[test]
    fn test_classify_file_ref_resource_file() {
        let mut resources_file = create_test_file("Strings.resources");
        let file_mut = Arc::get_mut(&mut resources_file).unwrap();
        file_mut.flags = 0x0001; // CONTAINS_NO_META_DATA

        let classification = DependencyAnalyzer::classify_file_ref(&resources_file);
        assert_eq!(classification, FileRefType::ResourceFile);

        let mut resx_file = create_test_file("Form.resx");
        let file_mut = Arc::get_mut(&mut resx_file).unwrap();
        file_mut.flags = 0x0001; // CONTAINS_NO_META_DATA

        let classification = DependencyAnalyzer::classify_file_ref(&resx_file);
        assert_eq!(classification, FileRefType::ResourceFile);
    }

    #[test]
    fn test_classify_file_ref_documentation_file() {
        let xml_file = create_test_file("Documentation.xml");
        let classification = DependencyAnalyzer::classify_file_ref(&xml_file);
        assert_eq!(classification, FileRefType::DocumentationFile);

        let pdb_file = create_test_file("Debug.pdb");
        let classification = DependencyAnalyzer::classify_file_ref(&pdb_file);
        assert_eq!(classification, FileRefType::DocumentationFile);

        let mdb_file = create_test_file("Debug.mdb");
        let classification = DependencyAnalyzer::classify_file_ref(&mdb_file);
        assert_eq!(classification, FileRefType::DocumentationFile);
    }

    #[test]
    fn test_classify_file_ref_external_assembly_file() {
        let dll_file = create_test_file("External.dll");
        let classification = DependencyAnalyzer::classify_file_ref(&dll_file);
        assert_eq!(classification, FileRefType::ExternalAssemblyFile);

        let exe_file = create_test_file("Application.exe");
        let classification = DependencyAnalyzer::classify_file_ref(&exe_file);
        assert_eq!(classification, FileRefType::ExternalAssemblyFile);
    }

    #[test]
    fn test_classify_file_ref_unknown() {
        let unknown_file = create_test_file("SomeFile.unknown");
        let classification = DependencyAnalyzer::classify_file_ref(&unknown_file);
        assert_eq!(classification, FileRefType::Unknown);
    }

    #[test]
    fn test_create_file_assembly_identity() {
        // Test .dll file
        let dll_file = create_test_file("TestLibrary.dll");
        let identity = DependencyAnalyzer::create_file_assembly_identity(&dll_file);
        assert_eq!(identity.name, "TestLibrary");
        assert_eq!(identity.version, AssemblyVersion::UNKNOWN);
        assert!(identity.version.is_unknown());

        // Test .exe file
        let exe_file = create_test_file("Application.exe");
        let identity = DependencyAnalyzer::create_file_assembly_identity(&exe_file);
        assert_eq!(identity.name, "Application");

        // Test .netmodule file
        let netmodule_file = create_test_file("Module.netmodule");
        let identity = DependencyAnalyzer::create_file_assembly_identity(&netmodule_file);
        assert_eq!(identity.name, "Module");

        // Test .resources file
        let resources_file = create_test_file("Strings.resources");
        let identity = DependencyAnalyzer::create_file_assembly_identity(&resources_file);
        assert_eq!(identity.name, "Strings");

        // Test file without extension
        let no_ext_file = create_test_file("SomeFile");
        let identity = DependencyAnalyzer::create_file_assembly_identity(&no_ext_file);
        assert_eq!(identity.name, "SomeFile");
    }
}