[Mono.Debugger.Soft] Reverted non-working support for MethodMirror.IsGenericMethod...
[mono.git] / mcs / class / IKVM.Reflection / Fusion.cs
index 4e81cd20074a4d19106c5a9815a00bfdf70437f4..044fb6c5ac8fb421075bccc43ce191a6290ffe56 100644 (file)
@@ -1,5 +1,6 @@
 /*
-  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
@@ -34,39 +35,48 @@ namespace IKVM.Reflection
                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);
@@ -109,6 +119,16 @@ namespace IKVM.Reflection
                                        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)
@@ -141,6 +161,11 @@ namespace IKVM.Reflection
                                        return true;
                                }
                        }
+                       else if (IsStrongNamed(name1))
+                       {
+                               result = AssemblyComparisonResult.NonEquivalent;
+                               return false;
+                       }
                        else
                        {
                                result = partial ? AssemblyComparisonResult.EquivalentPartialWeakNamed : AssemblyComparisonResult.EquivalentWeakNamed;
@@ -148,12 +173,76 @@ namespace IKVM.Reflection
                        }
                }
 
-               // 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++];
@@ -161,89 +250,218 @@ namespace IKVM.Reflection
                                {
                                        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;
@@ -279,7 +497,7 @@ namespace IKVM.Reflection
                                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++)
                        {