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;
41 internal bool WindowsRuntime;
44 enum ParseAssemblyResult
53 internal static bool CompareAssemblyIdentityNative(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
56 Marshal.ThrowExceptionForHR(CompareAssemblyIdentity(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out equivalent, out result));
60 [DllImport("fusion", CharSet = CharSet.Unicode)]
61 private static extern int CompareAssemblyIdentity(string pwzAssemblyIdentity1, bool fUnified1, string pwzAssemblyIdentity2, bool fUnified2, out bool pfEquivalent, out AssemblyComparisonResult pResult);
63 // internal for use by mcs
64 internal static bool CompareAssemblyIdentityPure(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
66 ParsedAssemblyName name1;
67 ParsedAssemblyName name2;
69 ParseAssemblyResult r = ParseAssemblyName(assemblyIdentity1, out name1);
70 if (r != ParseAssemblyResult.OK || (r = ParseAssemblyName(assemblyIdentity2, out name2)) != ParseAssemblyResult.OK)
72 result = AssemblyComparisonResult.NonEquivalent;
75 case ParseAssemblyResult.DuplicateKey:
76 throw new System.IO.FileLoadException();
77 case ParseAssemblyResult.GenericError:
79 throw new ArgumentException();
83 bool partial = IsPartial(name1);
85 if ((partial && unified1) || IsPartial(name2))
87 result = AssemblyComparisonResult.NonEquivalent;
88 throw new ArgumentException();
90 if (!name1.Name.Equals(name2.Name, StringComparison.InvariantCultureIgnoreCase))
92 result = AssemblyComparisonResult.NonEquivalent;
95 if (name1.Name.Equals("mscorlib", StringComparison.InvariantCultureIgnoreCase))
97 result = AssemblyComparisonResult.EquivalentFullMatch;
100 if (partial && name1.Culture == null)
103 else if (!name1.Culture.Equals(name2.Culture, StringComparison.InvariantCultureIgnoreCase))
105 result = AssemblyComparisonResult.NonEquivalent;
108 if (IsStrongNamed(name2))
110 if (partial && name1.PublicKeyToken == null)
113 else if (name1.PublicKeyToken != name2.PublicKeyToken)
115 result = AssemblyComparisonResult.NonEquivalent;
118 if (partial && name1.Version == null)
120 result = AssemblyComparisonResult.EquivalentPartialMatch;
123 else if (IsFrameworkAssembly(name2))
125 result = partial ? AssemblyComparisonResult.EquivalentPartialFXUnified : AssemblyComparisonResult.EquivalentFXUnified;
128 else if (name1.Version.Revision == -1 || name2.Version.Revision == -1)
130 result = AssemblyComparisonResult.NonEquivalent;
131 throw new ArgumentException();
133 else if (name1.Version < name2.Version)
137 result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
142 result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
146 else if (name1.Version > name2.Version)
150 result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
155 result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
161 result = partial ? AssemblyComparisonResult.EquivalentPartialMatch : AssemblyComparisonResult.EquivalentFullMatch;
165 else if (IsStrongNamed(name1))
167 result = AssemblyComparisonResult.NonEquivalent;
172 result = partial ? AssemblyComparisonResult.EquivalentPartialWeakNamed : AssemblyComparisonResult.EquivalentWeakNamed;
177 static bool IsFrameworkAssembly(ParsedAssemblyName name)
179 // A list of FX assemblies which require some form of remapping
180 // When 4.0 + 1 version is release, assemblies introduced in v4.0
181 // will have to be added
187 case "System.Data.DataSetExtensions":
188 case "System.Data.Linq":
189 case "System.Data.OracleClient":
190 case "System.Data.Services":
191 case "System.Data.Services.Client":
192 case "System.IdentityModel":
193 case "System.IdentityModel.Selectors":
194 case "System.Runtime.Remoting":
195 case "System.Runtime.Serialization":
196 case "System.ServiceModel":
197 case "System.Transactions":
198 case "System.Windows.Forms":
200 case "System.Xml.Linq":
201 return name.PublicKeyToken == "b77a5c561934e089";
203 case "System.Configuration":
204 case "System.Configuration.Install":
205 case "System.Design":
206 case "System.DirectoryServices":
207 case "System.Drawing":
208 case "System.Drawing.Design":
209 case "System.EnterpriseServices":
210 case "System.Management":
211 case "System.Messaging":
212 case "System.Runtime.Serialization.Formatters.Soap":
213 case "System.Security":
214 case "System.ServiceProcess":
216 case "System.Web.Mobile":
217 case "System.Web.Services":
218 return name.PublicKeyToken == "b03f5f7f11d50a3a";
220 case "System.ComponentModel.DataAnnotations":
221 case "System.ServiceModel.Web":
222 case "System.Web.Abstractions":
223 case "System.Web.Extensions":
224 case "System.Web.Extensions.Design":
225 case "System.Web.DynamicData":
226 case "System.Web.Routing":
227 return name.PublicKeyToken == "31bf3856ad364e35";
233 internal static ParseAssemblyResult ParseAssemblySimpleName(string fullName, out int pos, out string simpleName)
235 StringBuilder sb = new StringBuilder();
238 while (pos < fullName.Length && char.IsWhiteSpace(fullName[pos]))
242 char quoteOrComma = ',';
243 if (pos < fullName.Length && (fullName[pos] == '\"' || fullName[pos] == '\''))
245 quoteOrComma = fullName[pos++];
247 while (pos < fullName.Length)
249 char ch = fullName[pos++];
252 if (pos == fullName.Length)
254 return ParseAssemblyResult.GenericError;
256 ch = fullName[pos++];
259 return ParseAssemblyResult.GenericError;
262 else if (ch == quoteOrComma)
266 while (pos != fullName.Length)
268 ch = fullName[pos++];
273 if (!char.IsWhiteSpace(ch))
275 return ParseAssemblyResult.GenericError;
281 else if (ch == '=' || (quoteOrComma == ',' && (ch == '\'' || ch == '"')))
283 return ParseAssemblyResult.GenericError;
287 simpleName = sb.ToString().Trim();
288 if (simpleName.Length == 0)
290 return ParseAssemblyResult.GenericError;
292 if (pos == fullName.Length && fullName[fullName.Length - 1] == ',')
294 return ParseAssemblyResult.GenericError;
296 return ParseAssemblyResult.OK;
299 internal static ParseAssemblyResult ParseAssemblyName(string fullName, out ParsedAssemblyName parsedName)
301 parsedName = new ParsedAssemblyName();
303 ParseAssemblyResult res = ParseAssemblySimpleName(fullName, out pos, out parsedName.Name);
304 if (res != ParseAssemblyResult.OK || pos == fullName.Length)
310 System.Collections.Generic.Dictionary<string, string> unknownAttributes = null;
311 bool hasProcessorArchitecture = false;
312 bool hasContentType = false;
313 string[] parts = fullName.Substring(pos).Split(',');
314 for (int i = 0; i < parts.Length; i++)
316 string[] kv = parts[i].Split('=');
319 return ParseAssemblyResult.GenericError;
321 switch (kv[0].Trim().ToLowerInvariant())
324 if (parsedName.Version != null)
326 return ParseAssemblyResult.DuplicateKey;
328 if (!ParseVersion(kv[1].Trim(), out parsedName.Version))
330 return ParseAssemblyResult.GenericError;
334 if (parsedName.Culture != null)
336 return ParseAssemblyResult.DuplicateKey;
338 if (!ParseCulture(kv[1].Trim(), out parsedName.Culture))
340 return ParseAssemblyResult.GenericError;
343 case "publickeytoken":
344 if (parsedName.PublicKeyToken != null)
346 return ParseAssemblyResult.DuplicateKey;
348 if (!ParsePublicKeyToken(kv[1].Trim(), out parsedName.PublicKeyToken))
350 return ParseAssemblyResult.GenericError;
354 if (parsedName.PublicKeyToken != null)
356 return ParseAssemblyResult.DuplicateKey;
358 if (!ParsePublicKey(kv[1].Trim(), out parsedName.PublicKeyToken))
360 return ParseAssemblyResult.GenericError;
362 parsedName.HasPublicKey = true;
365 if (parsedName.Retargetable.HasValue)
367 return ParseAssemblyResult.DuplicateKey;
369 switch (kv[1].Trim().ToLowerInvariant())
372 parsedName.Retargetable = true;
375 parsedName.Retargetable = false;
378 return ParseAssemblyResult.GenericError;
381 case "processorarchitecture":
382 if (hasProcessorArchitecture)
384 return ParseAssemblyResult.DuplicateKey;
386 hasProcessorArchitecture = true;
387 switch (kv[1].Trim().ToLowerInvariant())
390 parsedName.ProcessorArchitecture = ProcessorArchitecture.None;
393 parsedName.ProcessorArchitecture = ProcessorArchitecture.MSIL;
396 parsedName.ProcessorArchitecture = ProcessorArchitecture.X86;
399 parsedName.ProcessorArchitecture = ProcessorArchitecture.IA64;
402 parsedName.ProcessorArchitecture = ProcessorArchitecture.Amd64;
405 parsedName.ProcessorArchitecture = ProcessorArchitecture.Arm;
408 return ParseAssemblyResult.GenericError;
414 return ParseAssemblyResult.DuplicateKey;
416 hasContentType = true;
417 if (kv[1].Trim().ToLowerInvariant() != "windowsruntime")
419 return ParseAssemblyResult.GenericError;
421 parsedName.WindowsRuntime = true;
424 if (kv[1].Trim() == "")
426 return ParseAssemblyResult.GenericError;
428 if (unknownAttributes == null)
430 unknownAttributes = new System.Collections.Generic.Dictionary<string, string>();
432 if (unknownAttributes.ContainsKey(kv[0].Trim().ToLowerInvariant()))
434 return ParseAssemblyResult.DuplicateKey;
436 unknownAttributes.Add(kv[0].Trim().ToLowerInvariant(), null);
441 return ParseAssemblyResult.OK;
444 private static bool ParseVersion(string str, out Version version)
446 string[] parts = str.Split('.');
447 if (parts.Length < 2 || parts.Length > 4)
451 // if the version consists of a single integer, it is invalid, but not invalid enough to fail the parse of the whole assembly name
452 return parts.Length == 1 && ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out dummy);
454 if (parts[0] == "" || parts[1] == "")
456 // this is a strange scenario, the version is invalid, but not invalid enough to fail the parse of the whole assembly name
460 ushort major, minor, build = 65535, revision = 65535;
461 if (ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out major)
462 && ushort.TryParse(parts[1], System.Globalization.NumberStyles.Integer, null, out minor)
463 && (parts.Length <= 2 || parts[2] == "" || ushort.TryParse(parts[2], System.Globalization.NumberStyles.Integer, null, out build))
464 && (parts.Length <= 3 || parts[3] == "" || (parts[2] != "" && ushort.TryParse(parts[3], System.Globalization.NumberStyles.Integer, null, out revision))))
466 if (parts.Length == 4 && parts[3] != "" && parts[2] != "")
468 version = new Version(major, minor, build, revision);
470 else if (parts.Length == 3 && parts[2] != "")
472 version = new Version(major, minor, build);
476 version = new Version(major, minor);
484 private static bool ParseCulture(string str, out string culture)
495 private static bool ParsePublicKeyToken(string str, out string publicKeyToken)
499 publicKeyToken = null;
502 publicKeyToken = str.ToLowerInvariant();
506 private static bool ParsePublicKey(string str, out string publicKeyToken)
510 publicKeyToken = null;
513 // HACK use AssemblyName to convert PublicKey to PublicKeyToken
514 byte[] token = new System.Reflection.AssemblyName("Foo, PublicKey=" + str).GetPublicKeyToken();
515 StringBuilder sb = new StringBuilder(token.Length * 2);
516 for (int i = 0; i < token.Length; i++)
518 sb.AppendFormat("{0:x2}", token[i]);
520 publicKeyToken = sb.ToString();
524 private static bool IsPartial(ParsedAssemblyName name)
526 return name.Version == null || name.Culture == null || name.PublicKeyToken == null;
529 private static bool IsStrongNamed(ParsedAssemblyName name)
531 return name.PublicKeyToken != null && name.PublicKeyToken != "null";