зеркало из https://github.com/mono/ikvm-fork.git
521 строка
15 KiB
C#
521 строка
15 KiB
C#
/*
|
|
Copyright (C) 2010-2012 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;
|
|
internal bool? Retargetable;
|
|
internal ProcessorArchitecture ProcessorArchitecture;
|
|
internal bool HasPublicKey;
|
|
}
|
|
|
|
enum ParseAssemblyResult
|
|
{
|
|
OK,
|
|
GenericError,
|
|
DuplicateKey,
|
|
}
|
|
|
|
static class Fusion
|
|
{
|
|
internal 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;
|
|
|
|
ParseAssemblyResult r = ParseAssemblyName(assemblyIdentity1, out name1);
|
|
if (r != ParseAssemblyResult.OK || (r = ParseAssemblyName(assemblyIdentity2, out name2)) != ParseAssemblyResult.OK)
|
|
{
|
|
result = AssemblyComparisonResult.NonEquivalent;
|
|
switch (r)
|
|
{
|
|
case ParseAssemblyResult.DuplicateKey:
|
|
throw new System.IO.FileLoadException();
|
|
case ParseAssemblyResult.GenericError:
|
|
default:
|
|
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.Revision == -1 || name2.Version.Revision == -1)
|
|
{
|
|
result = AssemblyComparisonResult.NonEquivalent;
|
|
throw new ArgumentException();
|
|
}
|
|
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;
|
|
}
|
|
|
|
internal static ParseAssemblyResult ParseAssemblySimpleName(string fullName, out int pos, out string simpleName)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
pos = 0;
|
|
simpleName = null;
|
|
while (pos < fullName.Length && char.IsWhiteSpace(fullName[pos]))
|
|
{
|
|
pos++;
|
|
}
|
|
char quoteOrComma = ',';
|
|
if (pos < fullName.Length && (fullName[pos] == '\"' || fullName[pos] == '\''))
|
|
{
|
|
quoteOrComma = fullName[pos++];
|
|
}
|
|
while (pos < fullName.Length)
|
|
{
|
|
char ch = fullName[pos++];
|
|
if (ch == '\\')
|
|
{
|
|
if (pos == fullName.Length)
|
|
{
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
ch = fullName[pos++];
|
|
if (ch == '\\')
|
|
{
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
}
|
|
else if (ch == quoteOrComma)
|
|
{
|
|
if (ch != ',')
|
|
{
|
|
while (pos != fullName.Length)
|
|
{
|
|
ch = fullName[pos++];
|
|
if (ch == ',')
|
|
{
|
|
break;
|
|
}
|
|
if (!char.IsWhiteSpace(ch))
|
|
{
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else if (ch == '=' || (quoteOrComma == ',' && (ch == '\'' || ch == '"')))
|
|
{
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
sb.Append(ch);
|
|
}
|
|
simpleName = sb.ToString().Trim();
|
|
if (simpleName.Length == 0)
|
|
{
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
if (pos == fullName.Length && fullName[fullName.Length - 1] == ',')
|
|
{
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
return ParseAssemblyResult.OK;
|
|
}
|
|
|
|
internal static ParseAssemblyResult ParseAssemblyName(string fullName, out ParsedAssemblyName parsedName)
|
|
{
|
|
parsedName = new ParsedAssemblyName();
|
|
int pos;
|
|
ParseAssemblyResult res = ParseAssemblySimpleName(fullName, out pos, out parsedName.Name);
|
|
if (res != ParseAssemblyResult.OK || pos == fullName.Length)
|
|
{
|
|
return res;
|
|
}
|
|
else
|
|
{
|
|
System.Collections.Generic.Dictionary<string, string> unknownAttributes = null;
|
|
bool hasProcessorArchitecture = false;
|
|
string[] parts = fullName.Substring(pos).Split(',');
|
|
for (int i = 0; i < parts.Length; i++)
|
|
{
|
|
string[] kv = parts[i].Split('=');
|
|
if (kv.Length != 2)
|
|
{
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
switch (kv[0].Trim().ToLowerInvariant())
|
|
{
|
|
case "version":
|
|
if (parsedName.Version != null)
|
|
{
|
|
return ParseAssemblyResult.DuplicateKey;
|
|
}
|
|
if (!ParseVersion(kv[1].Trim(), out parsedName.Version))
|
|
{
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
break;
|
|
case "culture":
|
|
if (parsedName.Culture != null)
|
|
{
|
|
return ParseAssemblyResult.DuplicateKey;
|
|
}
|
|
if (!ParseCulture(kv[1].Trim(), out parsedName.Culture))
|
|
{
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
break;
|
|
case "publickeytoken":
|
|
if (parsedName.PublicKeyToken != null)
|
|
{
|
|
return ParseAssemblyResult.DuplicateKey;
|
|
}
|
|
if (!ParsePublicKeyToken(kv[1].Trim(), out parsedName.PublicKeyToken))
|
|
{
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
break;
|
|
case "publickey":
|
|
if (parsedName.PublicKeyToken != null)
|
|
{
|
|
return ParseAssemblyResult.DuplicateKey;
|
|
}
|
|
if (!ParsePublicKey(kv[1].Trim(), out parsedName.PublicKeyToken))
|
|
{
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
parsedName.HasPublicKey = true;
|
|
break;
|
|
case "retargetable":
|
|
if (parsedName.Retargetable.HasValue)
|
|
{
|
|
return ParseAssemblyResult.DuplicateKey;
|
|
}
|
|
switch (kv[1].Trim().ToLowerInvariant())
|
|
{
|
|
case "yes":
|
|
parsedName.Retargetable = true;
|
|
break;
|
|
case "no":
|
|
parsedName.Retargetable = false;
|
|
break;
|
|
default:
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
break;
|
|
case "processorarchitecture":
|
|
if (hasProcessorArchitecture)
|
|
{
|
|
return ParseAssemblyResult.DuplicateKey;
|
|
}
|
|
hasProcessorArchitecture = true;
|
|
switch (kv[1].Trim().ToLowerInvariant())
|
|
{
|
|
case "none":
|
|
parsedName.ProcessorArchitecture = ProcessorArchitecture.None;
|
|
break;
|
|
case "msil":
|
|
parsedName.ProcessorArchitecture = ProcessorArchitecture.MSIL;
|
|
break;
|
|
case "x86":
|
|
parsedName.ProcessorArchitecture = ProcessorArchitecture.X86;
|
|
break;
|
|
case "ia64":
|
|
parsedName.ProcessorArchitecture = ProcessorArchitecture.IA64;
|
|
break;
|
|
case "amd64":
|
|
parsedName.ProcessorArchitecture = ProcessorArchitecture.Amd64;
|
|
break;
|
|
case "arm":
|
|
parsedName.ProcessorArchitecture = ProcessorArchitecture.Arm;
|
|
break;
|
|
default:
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
break;
|
|
default:
|
|
if (kv[1].Trim() == "")
|
|
{
|
|
return ParseAssemblyResult.GenericError;
|
|
}
|
|
if (unknownAttributes == null)
|
|
{
|
|
unknownAttributes = new System.Collections.Generic.Dictionary<string, string>();
|
|
}
|
|
if (unknownAttributes.ContainsKey(kv[0].Trim().ToLowerInvariant()))
|
|
{
|
|
return ParseAssemblyResult.DuplicateKey;
|
|
}
|
|
unknownAttributes.Add(kv[0].Trim().ToLowerInvariant(), null);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ParseAssemblyResult.OK;
|
|
}
|
|
|
|
private static bool ParseVersion(string str, out Version version)
|
|
{
|
|
string[] parts = str.Split('.');
|
|
if (parts.Length < 2 || parts.Length > 4)
|
|
{
|
|
version = null;
|
|
ushort dummy;
|
|
// if the version consists of a single integer, it is invalid, but not invalid enough to fail the parse of the whole assembly name
|
|
return parts.Length == 1 && ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out dummy);
|
|
}
|
|
if (parts[0] == "" || parts[1] == "")
|
|
{
|
|
// this is a strange scenario, the version is invalid, but not invalid enough to fail the parse of the whole assembly name
|
|
version = null;
|
|
return true;
|
|
}
|
|
ushort major, minor, build = 65535, revision = 65535;
|
|
if (ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out major)
|
|
&& ushort.TryParse(parts[1], System.Globalization.NumberStyles.Integer, null, out minor)
|
|
&& (parts.Length <= 2 || parts[2] == "" || ushort.TryParse(parts[2], System.Globalization.NumberStyles.Integer, null, out build))
|
|
&& (parts.Length <= 3 || parts[3] == "" || (parts[2] != "" && ushort.TryParse(parts[3], System.Globalization.NumberStyles.Integer, null, out revision))))
|
|
{
|
|
if (parts.Length == 4 && parts[3] != "" && parts[2] != "")
|
|
{
|
|
version = new Version(major, minor, build, revision);
|
|
}
|
|
else if (parts.Length == 3 && parts[2] != "")
|
|
{
|
|
version = new Version(major, minor, build);
|
|
}
|
|
else
|
|
{
|
|
version = new Version(major, minor);
|
|
}
|
|
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 System.Reflection.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";
|
|
}
|
|
}
|
|
}
|