2 Copyright (C) 2010-2012 Jeroen Frijters
3 Copyright (C) 2011 Marek Safar
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
27 using System.Runtime.InteropServices;
30 namespace IKVM.Reflection
32 struct ParsedAssemblyName
35 internal Version Version;
36 internal string Culture;
37 internal string PublicKeyToken;
38 internal bool? Retargetable;
39 internal ProcessorArchitecture ProcessorArchitecture;
40 internal bool HasPublicKey;
43 enum ParseAssemblyResult
52 internal static bool CompareAssemblyIdentityNative(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
55 Marshal.ThrowExceptionForHR(CompareAssemblyIdentity(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out equivalent, out result));
59 [DllImport("fusion", CharSet = CharSet.Unicode)]
60 private static extern int CompareAssemblyIdentity(string pwzAssemblyIdentity1, bool fUnified1, string pwzAssemblyIdentity2, bool fUnified2, out bool pfEquivalent, out AssemblyComparisonResult pResult);
62 // internal for use by mcs
63 internal static bool CompareAssemblyIdentityPure(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
65 ParsedAssemblyName name1;
66 ParsedAssemblyName name2;
68 ParseAssemblyResult r = ParseAssemblyName(assemblyIdentity1, out name1);
69 if (r != ParseAssemblyResult.OK || (r = ParseAssemblyName(assemblyIdentity2, out name2)) != ParseAssemblyResult.OK)
71 result = AssemblyComparisonResult.NonEquivalent;
74 case ParseAssemblyResult.DuplicateKey:
75 throw new System.IO.FileLoadException();
76 case ParseAssemblyResult.GenericError:
78 throw new ArgumentException();
82 bool partial = IsPartial(name1);
84 if ((partial && unified1) || IsPartial(name2))
86 result = AssemblyComparisonResult.NonEquivalent;
87 throw new ArgumentException();
89 if (!name1.Name.Equals(name2.Name, StringComparison.InvariantCultureIgnoreCase))
91 result = AssemblyComparisonResult.NonEquivalent;
94 if (name1.Name.Equals("mscorlib", StringComparison.InvariantCultureIgnoreCase))
96 result = AssemblyComparisonResult.EquivalentFullMatch;
99 if (partial && name1.Culture == null)
102 else if (!name1.Culture.Equals(name2.Culture, StringComparison.InvariantCultureIgnoreCase))
104 result = AssemblyComparisonResult.NonEquivalent;
107 if (IsStrongNamed(name2))
109 if (partial && name1.PublicKeyToken == null)
112 else if (name1.PublicKeyToken != name2.PublicKeyToken)
114 result = AssemblyComparisonResult.NonEquivalent;
117 if (partial && name1.Version == null)
119 result = AssemblyComparisonResult.EquivalentPartialMatch;
122 else if (IsFrameworkAssembly(name2))
124 result = partial ? AssemblyComparisonResult.EquivalentPartialFXUnified : AssemblyComparisonResult.EquivalentFXUnified;
127 else if (name1.Version.Revision == -1 || name2.Version.Revision == -1)
129 result = AssemblyComparisonResult.NonEquivalent;
130 throw new ArgumentException();
132 else if (name1.Version < name2.Version)
136 result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
141 result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
145 else if (name1.Version > name2.Version)
149 result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
154 result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
160 result = partial ? AssemblyComparisonResult.EquivalentPartialMatch : AssemblyComparisonResult.EquivalentFullMatch;
164 else if (IsStrongNamed(name1))
166 result = AssemblyComparisonResult.NonEquivalent;
171 result = partial ? AssemblyComparisonResult.EquivalentPartialWeakNamed : AssemblyComparisonResult.EquivalentWeakNamed;
176 static bool IsFrameworkAssembly(ParsedAssemblyName name)
178 // A list of FX assemblies which require some form of remapping
179 // When 4.0 + 1 version is release, assemblies introduced in v4.0
180 // will have to be added
186 case "System.Data.DataSetExtensions":
187 case "System.Data.Linq":
188 case "System.Data.OracleClient":
189 case "System.Data.Services":
190 case "System.Data.Services.Client":
191 case "System.IdentityModel":
192 case "System.IdentityModel.Selectors":
193 case "System.Runtime.Remoting":
194 case "System.Runtime.Serialization":
195 case "System.ServiceModel":
196 case "System.Transactions":
197 case "System.Windows.Forms":
199 case "System.Xml.Linq":
200 return name.PublicKeyToken == "b77a5c561934e089";
202 case "System.Configuration":
203 case "System.Configuration.Install":
204 case "System.Design":
205 case "System.DirectoryServices":
206 case "System.Drawing":
207 case "System.Drawing.Design":
208 case "System.EnterpriseServices":
209 case "System.Management":
210 case "System.Messaging":
211 case "System.Runtime.Serialization.Formatters.Soap":
212 case "System.Security":
213 case "System.ServiceProcess":
215 case "System.Web.Mobile":
216 case "System.Web.Services":
217 return name.PublicKeyToken == "b03f5f7f11d50a3a";
219 case "System.ComponentModel.DataAnnotations":
220 case "System.ServiceModel.Web":
221 case "System.Web.Abstractions":
222 case "System.Web.Extensions":
223 case "System.Web.Extensions.Design":
224 case "System.Web.DynamicData":
225 case "System.Web.Routing":
226 return name.PublicKeyToken == "31bf3856ad364e35";
232 internal static ParseAssemblyResult ParseAssemblySimpleName(string fullName, out int pos, out string simpleName)
234 StringBuilder sb = new StringBuilder();
237 while (pos < fullName.Length && char.IsWhiteSpace(fullName[pos]))
241 char quoteOrComma = ',';
242 if (pos < fullName.Length && (fullName[pos] == '\"' || fullName[pos] == '\''))
244 quoteOrComma = fullName[pos++];
246 while (pos < fullName.Length)
248 char ch = fullName[pos++];
251 if (pos == fullName.Length)
253 return ParseAssemblyResult.GenericError;
255 ch = fullName[pos++];
258 return ParseAssemblyResult.GenericError;
261 else if (ch == quoteOrComma)
265 while (pos != fullName.Length)
267 ch = fullName[pos++];
272 if (!char.IsWhiteSpace(ch))
274 return ParseAssemblyResult.GenericError;
280 else if (ch == '=' || (quoteOrComma == ',' && (ch == '\'' || ch == '"')))
282 return ParseAssemblyResult.GenericError;
286 simpleName = sb.ToString().Trim();
287 if (simpleName.Length == 0)
289 return ParseAssemblyResult.GenericError;
291 if (pos == fullName.Length && fullName[fullName.Length - 1] == ',')
293 return ParseAssemblyResult.GenericError;
295 return ParseAssemblyResult.OK;
298 internal static ParseAssemblyResult ParseAssemblyName(string fullName, out ParsedAssemblyName parsedName)
300 parsedName = new ParsedAssemblyName();
302 ParseAssemblyResult res = ParseAssemblySimpleName(fullName, out pos, out parsedName.Name);
303 if (res != ParseAssemblyResult.OK || pos == fullName.Length)
309 System.Collections.Generic.Dictionary<string, string> unknownAttributes = null;
310 bool hasProcessorArchitecture = false;
311 string[] parts = fullName.Substring(pos).Split(',');
312 for (int i = 0; i < parts.Length; i++)
314 string[] kv = parts[i].Split('=');
317 return ParseAssemblyResult.GenericError;
319 switch (kv[0].Trim().ToLowerInvariant())
322 if (parsedName.Version != null)
324 return ParseAssemblyResult.DuplicateKey;
326 if (!ParseVersion(kv[1].Trim(), out parsedName.Version))
328 return ParseAssemblyResult.GenericError;
332 if (parsedName.Culture != null)
334 return ParseAssemblyResult.DuplicateKey;
336 if (!ParseCulture(kv[1].Trim(), out parsedName.Culture))
338 return ParseAssemblyResult.GenericError;
341 case "publickeytoken":
342 if (parsedName.PublicKeyToken != null)
344 return ParseAssemblyResult.DuplicateKey;
346 if (!ParsePublicKeyToken(kv[1].Trim(), out parsedName.PublicKeyToken))
348 return ParseAssemblyResult.GenericError;
352 if (parsedName.PublicKeyToken != null)
354 return ParseAssemblyResult.DuplicateKey;
356 if (!ParsePublicKey(kv[1].Trim(), out parsedName.PublicKeyToken))
358 return ParseAssemblyResult.GenericError;
360 parsedName.HasPublicKey = true;
363 if (parsedName.Retargetable.HasValue)
365 return ParseAssemblyResult.DuplicateKey;
367 switch (kv[1].Trim().ToLowerInvariant())
370 parsedName.Retargetable = true;
373 parsedName.Retargetable = false;
376 return ParseAssemblyResult.GenericError;
379 case "processorarchitecture":
380 if (hasProcessorArchitecture)
382 return ParseAssemblyResult.DuplicateKey;
384 hasProcessorArchitecture = true;
385 switch (kv[1].Trim().ToLowerInvariant())
388 parsedName.ProcessorArchitecture = ProcessorArchitecture.None;
391 parsedName.ProcessorArchitecture = ProcessorArchitecture.MSIL;
394 parsedName.ProcessorArchitecture = ProcessorArchitecture.X86;
397 parsedName.ProcessorArchitecture = ProcessorArchitecture.IA64;
400 parsedName.ProcessorArchitecture = ProcessorArchitecture.Amd64;
403 parsedName.ProcessorArchitecture = ProcessorArchitecture.Arm;
406 return ParseAssemblyResult.GenericError;
410 if (kv[1].Trim() == "")
412 return ParseAssemblyResult.GenericError;
414 if (unknownAttributes == null)
416 unknownAttributes = new System.Collections.Generic.Dictionary<string, string>();
418 if (unknownAttributes.ContainsKey(kv[0].Trim().ToLowerInvariant()))
420 return ParseAssemblyResult.DuplicateKey;
422 unknownAttributes.Add(kv[0].Trim().ToLowerInvariant(), null);
427 return ParseAssemblyResult.OK;
430 private static bool ParseVersion(string str, out Version version)
432 string[] parts = str.Split('.');
433 if (parts.Length < 2 || parts.Length > 4)
437 // if the version consists of a single integer, it is invalid, but not invalid enough to fail the parse of the whole assembly name
438 return parts.Length == 1 && ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out dummy);
440 if (parts[0] == "" || parts[1] == "")
442 // this is a strange scenario, the version is invalid, but not invalid enough to fail the parse of the whole assembly name
446 ushort major, minor, build = 65535, revision = 65535;
447 if (ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out major)
448 && ushort.TryParse(parts[1], System.Globalization.NumberStyles.Integer, null, out minor)
449 && (parts.Length <= 2 || parts[2] == "" || ushort.TryParse(parts[2], System.Globalization.NumberStyles.Integer, null, out build))
450 && (parts.Length <= 3 || parts[3] == "" || (parts[2] != "" && ushort.TryParse(parts[3], System.Globalization.NumberStyles.Integer, null, out revision))))
452 if (parts.Length == 4 && parts[3] != "" && parts[2] != "")
454 version = new Version(major, minor, build, revision);
456 else if (parts.Length == 3 && parts[2] != "")
458 version = new Version(major, minor, build);
462 version = new Version(major, minor);
470 private static bool ParseCulture(string str, out string culture)
481 private static bool ParsePublicKeyToken(string str, out string publicKeyToken)
485 publicKeyToken = null;
488 publicKeyToken = str.ToLowerInvariant();
492 private static bool ParsePublicKey(string str, out string publicKeyToken)
496 publicKeyToken = null;
499 // HACK use AssemblyName to convert PublicKey to PublicKeyToken
500 byte[] token = new System.Reflection.AssemblyName("Foo, PublicKey=" + str).GetPublicKeyToken();
501 StringBuilder sb = new StringBuilder(token.Length * 2);
502 for (int i = 0; i < token.Length; i++)
504 sb.AppendFormat("{0:x2}", token[i]);
506 publicKeyToken = sb.ToString();
510 private static bool IsPartial(ParsedAssemblyName name)
512 return name.Version == null || name.Culture == null || name.PublicKeyToken == null;
515 private static bool IsStrongNamed(ParsedAssemblyName name)
517 return name.PublicKeyToken != null && name.PublicKeyToken != "null";