Skip to content

Commit eee01b0

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

File tree

1 file changed

+283
-50
lines changed

1 file changed

+283
-50
lines changed

Mono.Cecil/Import.cs

+283-50
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,307 @@ 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+
}
760744

761-
for (int i = 0; i < references.Count; i++) {
762-
var reference = references [i];
763-
if (!Equals (name_reference, reference))
764-
continue;
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+
}
765749

766-
assembly_reference = reference;
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)) {
767754
return true;
768755
}
769756

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+
}
761+
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)) {
769+
return true;
770+
}
771+
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)
802-
return false;
803-
if (!Equals (a.Version, b.Version))
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+
"System", // might be included if you are working with older versions of .NET Framework where it might be a system assembly
854+
};
855+
856+
var core_libs = new List<AssemblyNameReference> ();
857+
foreach (var asm_ref in module.AssemblyReferences) {
858+
foreach (var possible_system_assembly in possible_system_assemblies) {
859+
if (string.Equals (asm_ref.Name, possible_system_assembly, StringComparison.Ordinal)) {
860+
core_libs.Add (asm_ref);
861+
break;
862+
}
863+
}
864+
}
865+
866+
if (core_libs.Count != 1)
804867
return false;
805-
if (a.Culture != b.Culture)
868+
869+
var core_lib = core_libs [0];
870+
AssemblyDefinition resolved_core_lib;
871+
872+
try {
873+
resolved_core_lib = module.AssemblyResolver.Resolve (core_lib);
874+
}
875+
catch (AssemblyResolutionException) {
806876
return false;
807-
if (!Equals (a.PublicKeyToken, b.PublicKeyToken))
877+
}
878+
879+
if (resolved_core_lib == null)
808880
return false;
809-
return true;
881+
882+
TypeReference object_reference;
883+
foreach (var module_def in resolved_core_lib.Modules) {
884+
if (module_def.TryGetTypeReference (typeof (object).FullName, out object_reference)) {
885+
assembly_reference = core_lib;
886+
return true;
887+
}
888+
889+
foreach (var exported_type in module_def.ExportedTypes) {
890+
if (string.Equals (exported_type.FullName, typeof (object).FullName, StringComparison.Ordinal)) {
891+
892+
var scope = exported_type.Scope as AssemblyNameReference;
893+
if (scope != null) {
894+
foreach (var possible_system_assembly in possible_system_assemblies) {
895+
if (string.Equals (scope.Name, possible_system_assembly, StringComparison.Ordinal)) {
896+
assembly_reference = core_lib;
897+
return true;
898+
}
899+
}
900+
}
901+
}
902+
}
903+
}
904+
905+
return false;
906+
}
907+
908+
sealed class AssemblyWrapper {
909+
private readonly SR.AssemblyName assembly_name;
910+
private readonly AssemblyNameReference assembly_name_reference;
911+
912+
public AssemblyWrapper (SR.AssemblyName assembly_name)
913+
{
914+
CheckName (assembly_name);
915+
this.assembly_name = assembly_name;
916+
}
917+
918+
public AssemblyWrapper (AssemblyNameReference assembly_name_reference)
919+
{
920+
CheckName (assembly_name_reference);
921+
this.assembly_name_reference = assembly_name_reference;
922+
}
923+
924+
public string FullName {
925+
get {
926+
if (assembly_name == null) {
927+
return assembly_name_reference.FullName;
928+
} else {
929+
return assembly_name.FullName;
930+
}
931+
}
932+
}
933+
934+
public string Name {
935+
get {
936+
if (assembly_name == null) {
937+
return assembly_name_reference.Name;
938+
} else {
939+
return assembly_name.Name;
940+
}
941+
}
942+
}
943+
944+
public Version Version {
945+
get {
946+
if (assembly_name == null) {
947+
return assembly_name_reference.Version;
948+
} else {
949+
return assembly_name.Version;
950+
}
951+
}
952+
}
953+
954+
public byte [] PublicKeyToken {
955+
get {
956+
if (assembly_name == null) {
957+
return assembly_name_reference.PublicKeyToken;
958+
} else {
959+
return assembly_name.GetPublicKeyToken ();
960+
}
961+
}
962+
}
963+
964+
public string Culture {
965+
get {
966+
if (assembly_name == null) {
967+
return assembly_name_reference.Culture;
968+
} else {
969+
return assembly_name.CultureInfo.Name;
970+
}
971+
}
972+
}
973+
974+
public AssemblyNameReference GetAssemblyNameReference ()
975+
{
976+
if (this.assembly_name != null) {
977+
return new AssemblyNameReference (this.assembly_name.Name, this.assembly_name.Version) {
978+
PublicKeyToken = this.assembly_name.GetPublicKeyToken (),
979+
Culture = this.assembly_name.CultureInfo.Name,
980+
HashAlgorithm = (AssemblyHashAlgorithm)this.assembly_name.HashAlgorithm,
981+
};
982+
} else {
983+
return this.assembly_name_reference;
984+
}
985+
}
986+
987+
public override string ToString ()
988+
{
989+
return this.Name;
990+
}
991+
992+
public static bool Equals (AssemblyWrapper a, AssemblyWrapper b)
993+
{
994+
if (ReferenceEquals (a, b))
995+
return true;
996+
if (a.assembly_name != null && ReferenceEquals (a.assembly_name, b.assembly_name))
997+
return true;
998+
if (a.assembly_name_reference != null && ReferenceEquals (a.assembly_name_reference, b.assembly_name_reference))
999+
return true;
1000+
if (a.Name != b.Name)
1001+
return false;
1002+
if (!Equals (a.Version, b.Version))
1003+
return false;
1004+
if (a.Culture != b.Culture)
1005+
return false;
1006+
if (!Equals (a.PublicKeyToken, b.PublicKeyToken))
1007+
return false;
1008+
return true;
1009+
}
1010+
1011+
static bool Equals (byte [] a, byte [] b)
1012+
{
1013+
if (ReferenceEquals (a, b))
1014+
return true;
1015+
if (a == null)
1016+
return false;
1017+
if (a.Length != b.Length)
1018+
return false;
1019+
for (int i = 0; i < a.Length; i++)
1020+
if (a [i] != b [i])
1021+
return false;
1022+
return true;
1023+
}
1024+
1025+
static bool Equals<T> (T a, T b) where T : class, IEquatable<T>
1026+
{
1027+
if (ReferenceEquals (a, b))
1028+
return true;
1029+
if (a == null)
1030+
return false;
1031+
return a.Equals (b);
1032+
}
1033+
1034+
public static explicit operator AssemblyWrapper (SR.AssemblyName assembly_name)
1035+
{
1036+
return new AssemblyWrapper (assembly_name);
1037+
}
1038+
1039+
public static explicit operator AssemblyWrapper (AssemblyNameReference assembly_name_reference)
1040+
{
1041+
return new AssemblyWrapper (assembly_name_reference);
1042+
}
8101043
}
8111044
}
8121045
}

0 commit comments

Comments
 (0)