Update mcs/class/Commons.Xml.Relaxng/Commons.Xml.Relaxng/RelaxngPattern.cs
[mono.git] / mcs / class / IKVM.Reflection / Fusion.cs
1 /*
2   Copyright (C) 2010-2012 Jeroen Frijters
3   Copyright (C) 2011 Marek Safar
4
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.
8
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:
12
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.
20
21   Jeroen Frijters
22   jeroen@frijters.net
23   
24 */
25
26 using System;
27 using System.Runtime.InteropServices;
28 using System.Text;
29
30 namespace IKVM.Reflection
31 {
32         struct ParsedAssemblyName
33         {
34                 internal string Name;
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         }
42
43         enum ParseAssemblyResult
44         {
45                 OK,
46                 GenericError,
47                 DuplicateKey,
48         }
49
50         static class Fusion
51         {
52                 internal static bool CompareAssemblyIdentityNative(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
53                 {
54                         bool equivalent;
55                         Marshal.ThrowExceptionForHR(CompareAssemblyIdentity(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out equivalent, out result));
56                         return equivalent;
57                 }
58
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);
61
62                 // internal for use by mcs
63                 internal static bool CompareAssemblyIdentityPure(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
64                 {
65                         ParsedAssemblyName name1;
66                         ParsedAssemblyName name2;
67
68                         ParseAssemblyResult r = ParseAssemblyName(assemblyIdentity1, out name1);
69                         if (r != ParseAssemblyResult.OK || (r = ParseAssemblyName(assemblyIdentity2, out name2)) != ParseAssemblyResult.OK)
70                         {
71                                 result = AssemblyComparisonResult.NonEquivalent;
72                                 switch (r)
73                                 {
74                                         case ParseAssemblyResult.DuplicateKey:
75                                                 throw new System.IO.FileLoadException();
76                                         case ParseAssemblyResult.GenericError:
77                                         default:
78                                                 throw new ArgumentException();
79                                 }
80                         }
81
82                         bool partial = IsPartial(name1);
83
84                         if ((partial && unified1) || IsPartial(name2))
85                         {
86                                 result = AssemblyComparisonResult.NonEquivalent;
87                                 throw new ArgumentException();
88                         }
89                         if (!name1.Name.Equals(name2.Name, StringComparison.InvariantCultureIgnoreCase))
90                         {
91                                 result = AssemblyComparisonResult.NonEquivalent;
92                                 return false;
93                         }
94                         if (name1.Name.Equals("mscorlib", StringComparison.InvariantCultureIgnoreCase))
95                         {
96                                 result = AssemblyComparisonResult.EquivalentFullMatch;
97                                 return true;
98                         }
99                         if (partial && name1.Culture == null)
100                         {
101                         }
102                         else if (!name1.Culture.Equals(name2.Culture, StringComparison.InvariantCultureIgnoreCase))
103                         {
104                                 result = AssemblyComparisonResult.NonEquivalent;
105                                 return false;
106                         }
107                         if (IsStrongNamed(name2))
108                         {
109                                 if (partial && name1.PublicKeyToken == null)
110                                 {
111                                 }
112                                 else if (name1.PublicKeyToken != name2.PublicKeyToken)
113                                 {
114                                         result = AssemblyComparisonResult.NonEquivalent;
115                                         return false;
116                                 }
117                                 if (partial && name1.Version == null)
118                                 {
119                                         result = AssemblyComparisonResult.EquivalentPartialMatch;
120                                         return true;
121                                 }
122                                 else if (IsFrameworkAssembly(name2))
123                                 {
124                                         result = partial ? AssemblyComparisonResult.EquivalentPartialFXUnified : AssemblyComparisonResult.EquivalentFXUnified;
125                                         return true;
126                                 }
127                                 else if (name1.Version.Revision == -1 || name2.Version.Revision == -1)
128                                 {
129                                         result = AssemblyComparisonResult.NonEquivalent;
130                                         throw new ArgumentException();
131                                 }
132                                 else if (name1.Version < name2.Version)
133                                 {
134                                         if (unified2)
135                                         {
136                                                 result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
137                                                 return true;
138                                         }
139                                         else
140                                         {
141                                                 result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
142                                                 return false;
143                                         }
144                                 }
145                                 else if (name1.Version > name2.Version)
146                                 {
147                                         if (unified1)
148                                         {
149                                                 result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
150                                                 return true;
151                                         }
152                                         else
153                                         {
154                                                 result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
155                                                 return false;
156                                         }
157                                 }
158                                 else
159                                 {
160                                         result = partial ? AssemblyComparisonResult.EquivalentPartialMatch : AssemblyComparisonResult.EquivalentFullMatch;
161                                         return true;
162                                 }
163                         }
164                         else if (IsStrongNamed(name1))
165                         {
166                                 result = AssemblyComparisonResult.NonEquivalent;
167                                 return false;
168                         }
169                         else
170                         {
171                                 result = partial ? AssemblyComparisonResult.EquivalentPartialWeakNamed : AssemblyComparisonResult.EquivalentWeakNamed;
172                                 return true;
173                         }
174                 }
175
176                 static bool IsFrameworkAssembly(ParsedAssemblyName name)
177                 {
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
181                         switch (name.Name)
182                         {
183                                 case "System":
184                                 case "System.Core":
185                                 case "System.Data":
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":
198                                 case "System.Xml":
199                                 case "System.Xml.Linq":
200                                         return name.PublicKeyToken == "b77a5c561934e089";
201
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":
214                                 case "System.Web":
215                                 case "System.Web.Mobile":
216                                 case "System.Web.Services":
217                                         return name.PublicKeyToken == "b03f5f7f11d50a3a";
218
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";
227                         }
228
229                         return false;
230                 }
231
232                 internal static ParseAssemblyResult ParseAssemblySimpleName(string fullName, out int pos, out string simpleName)
233                 {
234                         StringBuilder sb = new StringBuilder();
235                         pos = 0;
236                         simpleName = null;
237                         while (pos < fullName.Length && char.IsWhiteSpace(fullName[pos]))
238                         {
239                                 pos++;
240                         }
241                         char quoteOrComma = ',';
242                         if (pos < fullName.Length && (fullName[pos] == '\"' || fullName[pos] == '\''))
243                         {
244                                 quoteOrComma = fullName[pos++];
245                         }
246                         while (pos < fullName.Length)
247                         {
248                                 char ch = fullName[pos++];
249                                 if (ch == '\\')
250                                 {
251                                         if (pos == fullName.Length)
252                                         {
253                                                 return ParseAssemblyResult.GenericError;
254                                         }
255                                         ch = fullName[pos++];
256                                         if (ch == '\\')
257                                         {
258                                                 return ParseAssemblyResult.GenericError;
259                                         }
260                                 }
261                                 else if (ch == quoteOrComma)
262                                 {
263                                         if (ch != ',')
264                                         {
265                                                 while (pos != fullName.Length)
266                                                 {
267                                                         ch = fullName[pos++];
268                                                         if (ch == ',')
269                                                         {
270                                                                 break;
271                                                         }
272                                                         if (!char.IsWhiteSpace(ch))
273                                                         {
274                                                                 return ParseAssemblyResult.GenericError;
275                                                         }
276                                                 }
277                                         }
278                                         break;
279                                 }
280                                 else if (ch == '=' || (quoteOrComma == ',' && (ch == '\'' || ch == '"')))
281                                 {
282                                         return ParseAssemblyResult.GenericError;
283                                 }
284                                 sb.Append(ch);
285                         }
286                         simpleName = sb.ToString().Trim();
287                         if (simpleName.Length == 0)
288                         {
289                                 return ParseAssemblyResult.GenericError;
290                         }
291                         if (pos == fullName.Length && fullName[fullName.Length - 1] == ',')
292                         {
293                                 return ParseAssemblyResult.GenericError;
294                         }
295                         return ParseAssemblyResult.OK;
296                 }
297
298                 internal static ParseAssemblyResult ParseAssemblyName(string fullName, out ParsedAssemblyName parsedName)
299                 {
300                         parsedName = new ParsedAssemblyName();
301                         int pos;
302                         ParseAssemblyResult res = ParseAssemblySimpleName(fullName, out pos, out parsedName.Name);
303                         if (res != ParseAssemblyResult.OK || pos == fullName.Length)
304                         {
305                                 return res;
306                         }
307                         else
308                         {
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++)
313                                 {
314                                         string[] kv = parts[i].Split('=');
315                                         if (kv.Length != 2)
316                                         {
317                                                 return ParseAssemblyResult.GenericError;
318                                         }
319                                         switch (kv[0].Trim().ToLowerInvariant())
320                                         {
321                                                 case "version":
322                                                         if (parsedName.Version != null)
323                                                         {
324                                                                 return ParseAssemblyResult.DuplicateKey;
325                                                         }
326                                                         if (!ParseVersion(kv[1].Trim(), out parsedName.Version))
327                                                         {
328                                                                 return ParseAssemblyResult.GenericError;
329                                                         }
330                                                         break;
331                                                 case "culture":
332                                                         if (parsedName.Culture != null)
333                                                         {
334                                                                 return ParseAssemblyResult.DuplicateKey;
335                                                         }
336                                                         if (!ParseCulture(kv[1].Trim(), out parsedName.Culture))
337                                                         {
338                                                                 return ParseAssemblyResult.GenericError;
339                                                         }
340                                                         break;
341                                                 case "publickeytoken":
342                                                         if (parsedName.PublicKeyToken != null)
343                                                         {
344                                                                 return ParseAssemblyResult.DuplicateKey;
345                                                         }
346                                                         if (!ParsePublicKeyToken(kv[1].Trim(), out parsedName.PublicKeyToken))
347                                                         {
348                                                                 return ParseAssemblyResult.GenericError;
349                                                         }
350                                                         break;
351                                                 case "publickey":
352                                                         if (parsedName.PublicKeyToken != null)
353                                                         {
354                                                                 return ParseAssemblyResult.DuplicateKey;
355                                                         }
356                                                         if (!ParsePublicKey(kv[1].Trim(), out parsedName.PublicKeyToken))
357                                                         {
358                                                                 return ParseAssemblyResult.GenericError;
359                                                         }
360                                                         parsedName.HasPublicKey = true;
361                                                         break;
362                                                 case "retargetable":
363                                                         if (parsedName.Retargetable.HasValue)
364                                                         {
365                                                                 return ParseAssemblyResult.DuplicateKey;
366                                                         }
367                                                         switch (kv[1].Trim().ToLowerInvariant())
368                                                         {
369                                                                 case "yes":
370                                                                         parsedName.Retargetable = true;
371                                                                         break;
372                                                                 case "no":
373                                                                         parsedName.Retargetable = false;
374                                                                         break;
375                                                                 default:
376                                                                         return ParseAssemblyResult.GenericError;
377                                                         }
378                                                         break;
379                                                 case "processorarchitecture":
380                                                         if (hasProcessorArchitecture)
381                                                         {
382                                                                 return ParseAssemblyResult.DuplicateKey;
383                                                         }
384                                                         hasProcessorArchitecture = true;
385                                                         switch (kv[1].Trim().ToLowerInvariant())
386                                                         {
387                                                                 case "none":
388                                                                         parsedName.ProcessorArchitecture = ProcessorArchitecture.None;
389                                                                         break;
390                                                                 case "msil":
391                                                                         parsedName.ProcessorArchitecture = ProcessorArchitecture.MSIL;
392                                                                         break;
393                                                                 case "x86":
394                                                                         parsedName.ProcessorArchitecture = ProcessorArchitecture.X86;
395                                                                         break;
396                                                                 case "ia64":
397                                                                         parsedName.ProcessorArchitecture = ProcessorArchitecture.IA64;
398                                                                         break;
399                                                                 case "amd64":
400                                                                         parsedName.ProcessorArchitecture = ProcessorArchitecture.Amd64;
401                                                                         break;
402                                                                 case "arm":
403                                                                         parsedName.ProcessorArchitecture = ProcessorArchitecture.Arm;
404                                                                         break;
405                                                                 default:
406                                                                         return ParseAssemblyResult.GenericError;
407                                                         }
408                                                         break;
409                                                 default:
410                                                         if (kv[1].Trim() == "")
411                                                         {
412                                                                 return ParseAssemblyResult.GenericError;
413                                                         }
414                                                         if (unknownAttributes == null)
415                                                         {
416                                                                 unknownAttributes = new System.Collections.Generic.Dictionary<string, string>();
417                                                         }
418                                                         if (unknownAttributes.ContainsKey(kv[0].Trim().ToLowerInvariant()))
419                                                         {
420                                                                 return ParseAssemblyResult.DuplicateKey;
421                                                         }
422                                                         unknownAttributes.Add(kv[0].Trim().ToLowerInvariant(), null);
423                                                         break;
424                                         }
425                                 }
426                         }
427                         return ParseAssemblyResult.OK;
428                 }
429
430                 private static bool ParseVersion(string str, out Version version)
431                 {
432                         string[] parts = str.Split('.');
433                         if (parts.Length < 2 || parts.Length > 4)
434                         {
435                                 version = null;
436                                 ushort dummy;
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);
439                         }
440                         if (parts[0] == "" || parts[1] == "")
441                         {
442                                 // this is a strange scenario, the version is invalid, but not invalid enough to fail the parse of the whole assembly name
443                                 version = null;
444                                 return true;
445                         }
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))))
451                         {
452                                 if (parts.Length == 4 && parts[3] != "" && parts[2] != "")
453                                 {
454                                         version = new Version(major, minor, build, revision);
455                                 }
456                                 else if (parts.Length == 3 && parts[2] != "")
457                                 {
458                                         version = new Version(major, minor, build);
459                                 }
460                                 else
461                                 {
462                                         version = new Version(major, minor);
463                                 }
464                                 return true;
465                         }
466                         version = null;
467                         return false;
468                 }
469
470                 private static bool ParseCulture(string str, out string culture)
471                 {
472                         if (str == null)
473                         {
474                                 culture = null;
475                                 return false;
476                         }
477                         culture = str;
478                         return true;
479                 }
480
481                 private static bool ParsePublicKeyToken(string str, out string publicKeyToken)
482                 {
483                         if (str == null)
484                         {
485                                 publicKeyToken = null;
486                                 return false;
487                         }
488                         publicKeyToken = str.ToLowerInvariant();
489                         return true;
490                 }
491
492                 private static bool ParsePublicKey(string str, out string publicKeyToken)
493                 {
494                         if (str == null)
495                         {
496                                 publicKeyToken = null;
497                                 return false;
498                         }
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++)
503                         {
504                                 sb.AppendFormat("{0:x2}", token[i]);
505                         }
506                         publicKeyToken = sb.ToString();
507                         return true;
508                 }
509
510                 private static bool IsPartial(ParsedAssemblyName name)
511                 {
512                         return name.Version == null || name.Culture == null || name.PublicKeyToken == null;
513                 }
514
515                 private static bool IsStrongNamed(ParsedAssemblyName name)
516                 {
517                         return name.PublicKeyToken != null && name.PublicKeyToken != "null";
518                 }
519         }
520 }