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
publicKeyToken = AssemblyName.ComputePublicKeyToken(str);
517
private static bool IsPartial(ParsedAssemblyName name)
519
return name.Version == null || name.Culture == null || name.PublicKeyToken == null;
522
private static bool IsStrongNamed(ParsedAssemblyName name)
524
return name.PublicKeyToken != null && name.PublicKeyToken != "null";