ikvm-fork/reflect/Fusion.cs

406 строки
11 KiB
C#

/*
Copyright (C) 2010 Jeroen Frijters
Copyright (C) 2011 Marek Safar
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace IKVM.Reflection
{
struct ParsedAssemblyName
{
internal string Name;
internal Version Version;
internal string Culture;
internal string PublicKeyToken;
}
static class Fusion
{
private static readonly bool UseNativeFusion = GetUseNativeFusion();
private static bool GetUseNativeFusion()
{
try
{
return Environment.OSVersion.Platform == PlatformID.Win32NT
&& System.Type.GetType("Mono.Runtime") == null
&& Environment.GetEnvironmentVariable("IKVM_DISABLE_FUSION") == null;
}
catch (System.Security.SecurityException)
{
return false;
}
}
internal static bool CompareAssemblyIdentity(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
{
if (UseNativeFusion)
{
return CompareAssemblyIdentityNative(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out result);
}
else
{
return CompareAssemblyIdentityPure(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out result);
}
}
private static bool CompareAssemblyIdentityNative(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
{
bool equivalent;
Marshal.ThrowExceptionForHR(CompareAssemblyIdentity(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out equivalent, out result));
return equivalent;
}
[DllImport("fusion", CharSet = CharSet.Unicode)]
private static extern int CompareAssemblyIdentity(string pwzAssemblyIdentity1, bool fUnified1, string pwzAssemblyIdentity2, bool fUnified2, out bool pfEquivalent, out AssemblyComparisonResult pResult);
// internal for use by mcs
internal static bool CompareAssemblyIdentityPure(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
{
ParsedAssemblyName name1;
ParsedAssemblyName name2;
if (!ParseAssemblyName(assemblyIdentity1, out name1)
|| !ParseAssemblyName(assemblyIdentity2, out name2))
{
result = AssemblyComparisonResult.NonEquivalent;
throw new ArgumentException();
}
bool partial = IsPartial(name1);
if ((partial && unified1) || IsPartial(name2))
{
result = AssemblyComparisonResult.NonEquivalent;
throw new ArgumentException();
}
if (!name1.Name.Equals(name2.Name, StringComparison.InvariantCultureIgnoreCase))
{
result = AssemblyComparisonResult.NonEquivalent;
return false;
}
if (name1.Name.Equals("mscorlib", StringComparison.InvariantCultureIgnoreCase))
{
result = AssemblyComparisonResult.EquivalentFullMatch;
return true;
}
if (partial && name1.Culture == null)
{
}
else if (!name1.Culture.Equals(name2.Culture, StringComparison.InvariantCultureIgnoreCase))
{
result = AssemblyComparisonResult.NonEquivalent;
return false;
}
if (IsStrongNamed(name2))
{
if (partial && name1.PublicKeyToken == null)
{
}
else if (name1.PublicKeyToken != name2.PublicKeyToken)
{
result = AssemblyComparisonResult.NonEquivalent;
return false;
}
if (partial && name1.Version == null)
{
result = AssemblyComparisonResult.EquivalentPartialMatch;
return true;
}
else if (IsFrameworkAssembly(name2))
{
result = partial ? AssemblyComparisonResult.EquivalentPartialFXUnified : AssemblyComparisonResult.EquivalentFXUnified;
return true;
}
else if (name1.Version < name2.Version)
{
if (unified2)
{
result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
return true;
}
else
{
result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
return false;
}
}
else if (name1.Version > name2.Version)
{
if (unified1)
{
result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
return true;
}
else
{
result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
return false;
}
}
else
{
result = partial ? AssemblyComparisonResult.EquivalentPartialMatch : AssemblyComparisonResult.EquivalentFullMatch;
return true;
}
}
else if (IsStrongNamed(name1))
{
result = AssemblyComparisonResult.NonEquivalent;
return false;
}
else
{
result = partial ? AssemblyComparisonResult.EquivalentPartialWeakNamed : AssemblyComparisonResult.EquivalentWeakNamed;
return true;
}
}
static bool IsFrameworkAssembly(ParsedAssemblyName name)
{
// A list of FX assemblies which require some form of remapping
// When 4.0 + 1 version is release, assemblies introduced in v4.0
// will have to be added
switch (name.Name)
{
case "System":
case "System.Core":
case "System.Data":
case "System.Data.DataSetExtensions":
case "System.Data.Linq":
case "System.Data.OracleClient":
case "System.Data.Services":
case "System.Data.Services.Client":
case "System.IdentityModel":
case "System.IdentityModel.Selectors":
case "System.Runtime.Remoting":
case "System.Runtime.Serialization":
case "System.ServiceModel":
case "System.Transactions":
case "System.Windows.Forms":
case "System.Xml":
case "System.Xml.Linq":
return name.PublicKeyToken == "b77a5c561934e089";
case "System.Configuration":
case "System.Configuration.Install":
case "System.Design":
case "System.DirectoryServices":
case "System.Drawing":
case "System.Drawing.Design":
case "System.EnterpriseServices":
case "System.Management":
case "System.Messaging":
case "System.Runtime.Serialization.Formatters.Soap":
case "System.Security":
case "System.ServiceProcess":
case "System.Web":
case "System.Web.Mobile":
case "System.Web.Services":
return name.PublicKeyToken == "b03f5f7f11d50a3a";
case "System.ComponentModel.DataAnnotations":
case "System.ServiceModel.Web":
case "System.Web.Abstractions":
case "System.Web.Extensions":
case "System.Web.Extensions.Design":
case "System.Web.DynamicData":
case "System.Web.Routing":
return name.PublicKeyToken == "31bf3856ad364e35";
}
return false;
}
// note that this is the fusion specific parser, it is not the same as System.Reflection.AssemblyName
private static bool ParseAssemblyName(string fullName, out ParsedAssemblyName parsedName)
{
parsedName = new ParsedAssemblyName();
StringBuilder sb = new StringBuilder();
int pos = 0;
while (pos < fullName.Length)
{
char ch = fullName[pos++];
if (ch == '\\')
{
if (pos == fullName.Length)
{
return false;
}
ch = fullName[pos++];
}
else if (ch == ',')
{
break;
}
sb.Append(ch);
}
parsedName.Name = sb.ToString().Trim();
if (pos < fullName.Length)
{
string[] parts = fullName.Substring(pos).Split(',');
for (int i = 0; i < parts.Length; i++)
{
string[] kv = parts[i].Split('=');
if (kv.Length != 2)
{
return false;
}
switch (kv[0].Trim().ToLowerInvariant())
{
case "version":
if (parsedName.Version != null)
{
return false;
}
if (!ParseVersion(kv[1].Trim(), out parsedName.Version))
{
return false;
}
break;
case "culture":
if (parsedName.Culture != null)
{
return false;
}
if (!ParseCulture(kv[1].Trim(), out parsedName.Culture))
{
return false;
}
break;
case "publickeytoken":
if (parsedName.PublicKeyToken != null)
{
return false;
}
if (!ParsePublicKeyToken(kv[1].Trim(), out parsedName.PublicKeyToken))
{
return false;
}
break;
case "publickey":
if (parsedName.PublicKeyToken != null)
{
return false;
}
if (!ParsePublicKey(kv[1].Trim(), out parsedName.PublicKeyToken))
{
return false;
}
break;
}
}
}
return true;
}
private static bool ParseVersion(string str, out Version version)
{
string[] parts = str.Split('.');
if (parts.Length == 4)
{
ushort major, minor, build, revision;
if (ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out major)
&& ushort.TryParse(parts[1], System.Globalization.NumberStyles.Integer, null, out minor)
&& ushort.TryParse(parts[2], System.Globalization.NumberStyles.Integer, null, out build)
&& ushort.TryParse(parts[3], System.Globalization.NumberStyles.Integer, null, out revision))
{
version = new Version(major, minor, build, revision);
return true;
}
}
version = null;
return false;
}
private static bool ParseCulture(string str, out string culture)
{
if (str == null)
{
culture = null;
return false;
}
culture = str;
return true;
}
private static bool ParsePublicKeyToken(string str, out string publicKeyToken)
{
if (str == null)
{
publicKeyToken = null;
return false;
}
publicKeyToken = str.ToLowerInvariant();
return true;
}
private static bool ParsePublicKey(string str, out string publicKeyToken)
{
if (str == null)
{
publicKeyToken = null;
return false;
}
// HACK use AssemblyName to convert PublicKey to PublicKeyToken
byte[] token = new AssemblyName("Foo, PublicKey=" + str).GetPublicKeyToken();
StringBuilder sb = new StringBuilder(token.Length * 2);
for (int i = 0; i < token.Length; i++)
{
sb.AppendFormat("{0:x2}", token[i]);
}
publicKeyToken = sb.ToString();
return true;
}
private static bool IsPartial(ParsedAssemblyName name)
{
return name.Version == null || name.Culture == null || name.PublicKeyToken == null;
}
private static bool IsStrongNamed(ParsedAssemblyName name)
{
return name.PublicKeyToken != null && name.PublicKeyToken != "null";
}
private static bool IsEqual(byte[] b1, byte[] b2)
{
if (b1.Length != b2.Length)
{
return false;
}
for (int i = 0; i < b1.Length; i++)
{
if (b1[i] != b2[i])
{
return false;
}
}
return true;
}
}
}