Skip to content

Commit 6c9718d

Browse files
author
Denis Kudelin
committed
Enhance assembly name reference resolution
1 parent 56d4409 commit 6c9718d

File tree

1 file changed

+291
-49
lines changed

1 file changed

+291
-49
lines changed

Mono.Cecil/Import.cs

+291-49
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ public virtual AssemblyNameReference ImportReference (SR.AssemblyName name)
305305
Mixin.CheckName (name);
306306

307307
AssemblyNameReference reference;
308-
if (TryGetAssemblyNameReference (name, out reference))
308+
if (Mixin.TryGetAssemblyNameReference (module, name, out reference))
309309
return reference;
310310

311311
reference = new AssemblyNameReference (name.Name, name.Version)
@@ -320,23 +320,6 @@ public virtual AssemblyNameReference ImportReference (SR.AssemblyName name)
320320
return reference;
321321
}
322322

323-
bool TryGetAssemblyNameReference (SR.AssemblyName name, out AssemblyNameReference assembly_reference)
324-
{
325-
var references = module.AssemblyReferences;
326-
327-
for (int i = 0; i < references.Count; i++) {
328-
var reference = references [i];
329-
if (name.FullName != reference.FullName) // TODO compare field by field
330-
continue;
331-
332-
assembly_reference = reference;
333-
return true;
334-
}
335-
336-
assembly_reference = null;
337-
return false;
338-
}
339-
340323
FieldReference ImportField (SR.FieldInfo field, ImportGenericContext context)
341324
{
342325
var declaring_type = ImportType (field.DeclaringType, context);
@@ -756,57 +739,316 @@ public static void CheckModule (ModuleDefinition module)
756739

757740
public static bool TryGetAssemblyNameReference (this ModuleDefinition module, AssemblyNameReference name_reference, out AssemblyNameReference assembly_reference)
758741
{
759-
var references = module.AssemblyReferences;
742+
return TryGetAssemblyNameReference (module, (AssemblyWrapper)name_reference, out assembly_reference);
743+
}
744+
745+
public static bool TryGetAssemblyNameReference (this ModuleDefinition module, SR.AssemblyName name_reference, out AssemblyNameReference assembly_reference)
746+
{
747+
return TryGetAssemblyNameReference (module, (AssemblyWrapper)name_reference, out assembly_reference);
748+
}
760749

761-
for (int i = 0; i < references.Count; i++) {
762-
var reference = references [i];
763-
if (!Equals (name_reference, reference))
764-
continue;
750+
static bool TryGetAssemblyNameReference (this ModuleDefinition module, AssemblyWrapper name_reference, out AssemblyNameReference assembly_reference)
751+
{
752+
// Try to resolve the assembly using direct reference first
753+
if (module.TryGetDirectAssemblyNameReference (name_reference, out assembly_reference)) {
754+
return true;
755+
}
756+
757+
// If direct resolution fails, try to resolve using forwarded types
758+
if (module.TryGetForwardedAssemblyNameReference (name_reference, out assembly_reference, new HashSet<string> ())) {
759+
return true;
760+
}
765761

766-
assembly_reference = reference;
762+
// As a fallback, if the assembly is still unresolved,
763+
// check if it could be a system assembly like 'System.Private.CoreLib',
764+
// which can be safely mapped to 'System.Runtime' in certain .NET platforms.
765+
// This handles the internal details of .NET Core and .NET 5+,
766+
// where 'System.Private.CoreLib' is the core library but it is not referenceable
767+
// and 'System.Runtime' is the referenceable contract assembly.
768+
if (module.TryGetSystemAssemblyNameReference (name_reference, out assembly_reference)) {
767769
return true;
768770
}
769771

772+
// If resolution fails, set the output reference to null
770773
assembly_reference = null;
771774
return false;
772775
}
773776

774-
static bool Equals (byte [] a, byte [] b)
777+
static bool TryGetDirectAssemblyNameReference (this ModuleDefinition module, AssemblyWrapper name_reference, out AssemblyNameReference assembly_reference)
775778
{
776-
if (ReferenceEquals (a, b))
777-
return true;
778-
if (a == null)
779-
return false;
780-
if (a.Length != b.Length)
781-
return false;
782-
for (int i = 0; i < a.Length; i++)
783-
if (a [i] != b [i])
784-
return false;
785-
return true;
779+
// Check each assembly reference in the module for a match by full name
780+
foreach (var reference in module.AssemblyReferences) {
781+
if (reference.FullName == name_reference.FullName) {
782+
assembly_reference = reference;
783+
return true;
784+
}
785+
}
786+
787+
// If no direct reference is found, set the output reference to null
788+
assembly_reference = null;
789+
return false;
786790
}
787791

788-
static bool Equals<T> (T a, T b) where T : class, IEquatable<T>
792+
static bool TryGetForwardedAssemblyNameReference (
793+
this ModuleDefinition module,
794+
AssemblyWrapper name_reference,
795+
out AssemblyNameReference assembly_reference,
796+
HashSet<string> checked_assemblies)
789797
{
790-
if (ReferenceEquals (a, b))
791-
return true;
792-
if (a == null)
793-
return false;
794-
return a.Equals (b);
798+
// Initialize the output parameter to null
799+
assembly_reference = null;
800+
801+
// Iterate through all assembly references
802+
foreach (var asm_ref in module.AssemblyReferences) {
803+
if (!checked_assemblies.Add (asm_ref.FullName))
804+
continue;
805+
806+
AssemblyDefinition resolved_assembly;
807+
try {
808+
// Attempt to resolve the assembly reference
809+
resolved_assembly = module.AssemblyResolver.Resolve (asm_ref);
810+
}
811+
catch (AssemblyResolutionException) {
812+
// Skip the assembly if resolution fails
813+
continue;
814+
}
815+
816+
// Check exported types for type forwarding within the assembly
817+
foreach (var module_def in resolved_assembly.Modules) {
818+
foreach (var exported_type in module_def.ExportedTypes) {
819+
// Check if the exported type has a scope that matches the target assembly name
820+
var scope = exported_type.Scope as AssemblyNameReference;
821+
if (scope == null)
822+
continue;
823+
824+
if (!checked_assemblies.Add (scope.FullName))
825+
continue;
826+
827+
if (AssemblyWrapper.Equals (scope, name_reference)) {
828+
// If a match is found, return the assembly reference from which the type was forwarded
829+
assembly_reference = asm_ref;
830+
return true;
831+
} else {
832+
if (TryGetForwardedAssemblyNameReference (module, (AssemblyWrapper)scope, out assembly_reference, checked_assemblies))
833+
return true;
834+
}
835+
}
836+
}
837+
}
838+
839+
return false;
795840
}
796841

797-
static bool Equals (AssemblyNameReference a, AssemblyNameReference b)
842+
static bool TryGetSystemAssemblyNameReference (this ModuleDefinition module, AssemblyWrapper name_reference, out AssemblyNameReference assembly_reference)
798843
{
799-
if (ReferenceEquals (a, b))
800-
return true;
801-
if (a.Name != b.Name)
844+
assembly_reference = null;
845+
846+
var possible_system_assemblies = new [] {
847+
"System.Runtime", // The main system assembly for .NET Core and .NET 5+
848+
"mscorlib", // The main system assembly for the .NET Framework
849+
"netstandard", // The pseudo-assembly for .NET Standard API surface
850+
851+
// Additional potential system assemblies used in specific contexts:
852+
"System.Private.CoreLib", // The implementation of the BCL in .NET Core and .NET 5+
853+
};
854+
855+
var core_libs = new List<AssemblyNameReference> ();
856+
foreach (var asm_ref in module.AssemblyReferences) {
857+
foreach (var possible_system_assembly in possible_system_assemblies) {
858+
if (string.Equals (asm_ref.Name, possible_system_assembly, StringComparison.Ordinal)) {
859+
core_libs.Add (asm_ref);
860+
break;
861+
}
862+
}
863+
}
864+
865+
var possible_core_lib = false;
866+
foreach (var possible_system_assembly in possible_system_assemblies) {
867+
if (string.Equals (name_reference.Name, possible_system_assembly, StringComparison.Ordinal)) {
868+
possible_core_lib = true;
869+
break;
870+
}
871+
}
872+
873+
if (!possible_core_lib)
802874
return false;
803-
if (!Equals (a.Version, b.Version))
875+
876+
if (core_libs.Count != 1)
804877
return false;
805-
if (a.Culture != b.Culture)
878+
879+
var core_lib = core_libs [0];
880+
AssemblyDefinition resolved_core_lib;
881+
882+
try {
883+
resolved_core_lib = module.AssemblyResolver.Resolve (name_reference.GetAssemblyNameReference());
884+
}
885+
catch (AssemblyResolutionException) {
806886
return false;
807-
if (!Equals (a.PublicKeyToken, b.PublicKeyToken))
887+
}
888+
889+
if (resolved_core_lib == null)
808890
return false;
809-
return true;
891+
892+
foreach (var module_def in resolved_core_lib.Modules) {
893+
if (module_def.GetType (typeof (object).FullName) != null) {
894+
assembly_reference = core_lib;
895+
return true;
896+
}
897+
898+
foreach (var exported_type in module_def.ExportedTypes) {
899+
if (string.Equals (exported_type.FullName, typeof (object).FullName, StringComparison.Ordinal)) {
900+
901+
var scope = exported_type.Scope as AssemblyNameReference;
902+
if (scope != null) {
903+
foreach (var possible_system_assembly in possible_system_assemblies) {
904+
if (string.Equals (scope.Name, possible_system_assembly, StringComparison.Ordinal)) {
905+
assembly_reference = core_lib;
906+
return true;
907+
}
908+
}
909+
}
910+
}
911+
}
912+
}
913+
914+
return false;
915+
}
916+
917+
sealed class AssemblyWrapper {
918+
private readonly SR.AssemblyName assembly_name;
919+
private readonly AssemblyNameReference assembly_name_reference;
920+
921+
public AssemblyWrapper (SR.AssemblyName assembly_name)
922+
{
923+
CheckName (assembly_name);
924+
this.assembly_name = assembly_name;
925+
}
926+
927+
public AssemblyWrapper (AssemblyNameReference assembly_name_reference)
928+
{
929+
CheckName (assembly_name_reference);
930+
this.assembly_name_reference = assembly_name_reference;
931+
}
932+
933+
public string FullName {
934+
get {
935+
if (assembly_name == null) {
936+
return assembly_name_reference.FullName;
937+
} else {
938+
return assembly_name.FullName;
939+
}
940+
}
941+
}
942+
943+
public string Name {
944+
get {
945+
if (assembly_name == null) {
946+
return assembly_name_reference.Name;
947+
} else {
948+
return assembly_name.Name;
949+
}
950+
}
951+
}
952+
953+
public Version Version {
954+
get {
955+
if (assembly_name == null) {
956+
return assembly_name_reference.Version;
957+
} else {
958+
return assembly_name.Version;
959+
}
960+
}
961+
}
962+
963+
public byte [] PublicKeyToken {
964+
get {
965+
if (assembly_name == null) {
966+
return assembly_name_reference.PublicKeyToken;
967+
} else {
968+
return assembly_name.GetPublicKeyToken ();
969+
}
970+
}
971+
}
972+
973+
public string Culture {
974+
get {
975+
if (assembly_name == null) {
976+
return assembly_name_reference.Culture;
977+
} else {
978+
return assembly_name.CultureInfo.Name;
979+
}
980+
}
981+
}
982+
983+
public AssemblyNameReference GetAssemblyNameReference ()
984+
{
985+
if (this.assembly_name != null) {
986+
return new AssemblyNameReference (this.assembly_name.Name, this.assembly_name.Version) {
987+
PublicKeyToken = this.assembly_name.GetPublicKeyToken (),
988+
Culture = this.assembly_name.CultureInfo.Name,
989+
HashAlgorithm = (AssemblyHashAlgorithm)this.assembly_name.HashAlgorithm,
990+
};
991+
} else {
992+
return this.assembly_name_reference;
993+
}
994+
}
995+
996+
public override string ToString ()
997+
{
998+
return this.Name;
999+
}
1000+
1001+
public static bool Equals (AssemblyWrapper a, AssemblyWrapper b)
1002+
{
1003+
if (ReferenceEquals (a, b))
1004+
return true;
1005+
if (a.assembly_name != null && ReferenceEquals (a.assembly_name, b.assembly_name))
1006+
return true;
1007+
if (a.assembly_name_reference != null && ReferenceEquals (a.assembly_name_reference, b.assembly_name_reference))
1008+
return true;
1009+
if (a.Name != b.Name)
1010+
return false;
1011+
if (!Equals (a.Version, b.Version))
1012+
return false;
1013+
if (a.Culture != b.Culture)
1014+
return false;
1015+
if (!Equals (a.PublicKeyToken, b.PublicKeyToken))
1016+
return false;
1017+
return true;
1018+
}
1019+
1020+
static bool Equals (byte [] a, byte [] b)
1021+
{
1022+
if (ReferenceEquals (a, b))
1023+
return true;
1024+
if (a == null)
1025+
return false;
1026+
if (a.Length != b.Length)
1027+
return false;
1028+
for (int i = 0; i < a.Length; i++)
1029+
if (a [i] != b [i])
1030+
return false;
1031+
return true;
1032+
}
1033+
1034+
static bool Equals<T> (T a, T b) where T : class, IEquatable<T>
1035+
{
1036+
if (ReferenceEquals (a, b))
1037+
return true;
1038+
if (a == null)
1039+
return false;
1040+
return a.Equals (b);
1041+
}
1042+
1043+
public static explicit operator AssemblyWrapper (SR.AssemblyName assembly_name)
1044+
{
1045+
return new AssemblyWrapper (assembly_name);
1046+
}
1047+
1048+
public static explicit operator AssemblyWrapper (AssemblyNameReference assembly_name_reference)
1049+
{
1050+
return new AssemblyWrapper (assembly_name_reference);
1051+
}
8101052
}
8111053
}
8121054
}

0 commit comments

Comments
 (0)