/*
- Copyright (C) 2010 Jeroen Frijters
+ 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
internal Version Version;
internal string Culture;
internal string PublicKeyToken;
+ internal bool? Retargetable;
+ internal ProcessorArchitecture ProcessorArchitecture;
+ internal bool HasPublicKey;
}
- static class Fusion
+ enum ParseAssemblyResult
{
- private static readonly bool UseNativeFusion = Environment.OSVersion.Platform == PlatformID.Win32NT && System.Type.GetType("Mono.Runtime") == null && Environment.GetEnvironmentVariable("IKVM_DISABLE_FUSION") == null;
+ OK,
+ GenericError,
+ DuplicateKey,
+ }
- internal static bool CompareAssemblyIdentity(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
+ static class Fusion
+ {
+ internal static bool CompareAssemblyIdentityNative(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
{
- if (UseNativeFusion)
- {
- bool equivalent;
- Marshal.ThrowExceptionForHR(CompareAssemblyIdentity(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out equivalent, out result));
- return equivalent;
- }
- else
- {
- return CompareAssemblyIdentityPure(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out 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);
- private static bool CompareAssemblyIdentityPure(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
+ // 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))
+ ParseAssemblyResult r = ParseAssemblyName(assemblyIdentity1, out name1);
+ if (r != ParseAssemblyResult.OK || (r = ParseAssemblyName(assemblyIdentity2, out name2)) != ParseAssemblyResult.OK)
{
result = AssemblyComparisonResult.NonEquivalent;
- throw new ArgumentException();
+ switch (r)
+ {
+ case ParseAssemblyResult.DuplicateKey:
+ throw new System.IO.FileLoadException();
+ case ParseAssemblyResult.GenericError:
+ default:
+ throw new ArgumentException();
+ }
}
bool partial = IsPartial(name1);
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)
return true;
}
}
+ else if (IsStrongNamed(name1))
+ {
+ result = AssemblyComparisonResult.NonEquivalent;
+ return false;
+ }
else
{
result = partial ? AssemblyComparisonResult.EquivalentPartialWeakNamed : AssemblyComparisonResult.EquivalentWeakNamed;
}
}
- // 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)
+ 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)
{
- parsedName = new ParsedAssemblyName();
StringBuilder sb = new StringBuilder();
- int pos = 0;
+ 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 (pos == fullName.Length)
{
- return false;
+ return ParseAssemblyResult.GenericError;
}
ch = fullName[pos++];
+ if (ch == '\\')
+ {
+ return ParseAssemblyResult.GenericError;
+ }
}
- else if (ch == ',')
+ 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);
}
- parsedName.Name = sb.ToString().Trim();
- if (pos < fullName.Length)
+ 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 false;
+ return ParseAssemblyResult.GenericError;
}
switch (kv[0].Trim().ToLowerInvariant())
{
case "version":
if (parsedName.Version != null)
{
- return false;
+ return ParseAssemblyResult.DuplicateKey;
}
if (!ParseVersion(kv[1].Trim(), out parsedName.Version))
{
- return false;
+ return ParseAssemblyResult.GenericError;
}
break;
case "culture":
if (parsedName.Culture != null)
{
- return false;
+ return ParseAssemblyResult.DuplicateKey;
}
if (!ParseCulture(kv[1].Trim(), out parsedName.Culture))
{
- return false;
+ return ParseAssemblyResult.GenericError;
}
break;
case "publickeytoken":
if (parsedName.PublicKeyToken != null)
{
- return false;
+ return ParseAssemblyResult.DuplicateKey;
}
if (!ParsePublicKeyToken(kv[1].Trim(), out parsedName.PublicKeyToken))
{
- return false;
+ return ParseAssemblyResult.GenericError;
}
break;
case "publickey":
if (parsedName.PublicKeyToken != null)
{
- return false;
+ return ParseAssemblyResult.DuplicateKey;
}
if (!ParsePublicKey(kv[1].Trim(), out parsedName.PublicKeyToken))
{
- return false;
+ 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 true;
+ return ParseAssemblyResult.OK;
}
private static bool ParseVersion(string str, out Version version)
{
string[] parts = str.Split('.');
- if (parts.Length == 4)
+ 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))))
{
- 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))
+ if (parts.Length == 4 && parts[3] != "" && parts[2] != "")
{
version = new Version(major, minor, build, revision);
- return true;
}
+ 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;
return false;
}
// HACK use AssemblyName to convert PublicKey to PublicKeyToken
- byte[] token = new AssemblyName("Foo, PublicKey=" + str).GetPublicKeyToken();
+ 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++)
{