Update to the latest IKVM.Reflection
[mono.git] / mcs / class / IKVM.Reflection / AssemblyName.cs
1 /*
2   Copyright (C) 2009-2012 Jeroen Frijters
3
4   This software is provided 'as-is', without any express or implied
5   warranty.  In no event will the authors be held liable for any damages
6   arising from the use of this software.
7
8   Permission is granted to anyone to use this software for any purpose,
9   including commercial applications, and to alter it and redistribute it
10   freely, subject to the following restrictions:
11
12   1. The origin of this software must not be misrepresented; you must not
13      claim that you wrote the original software. If you use this software
14      in a product, an acknowledgment in the product documentation would be
15      appreciated but is not required.
16   2. Altered source versions must be plainly marked as such, and must not be
17      misrepresented as being the original software.
18   3. This notice may not be removed or altered from any source distribution.
19
20   Jeroen Frijters
21   jeroen@frijters.net
22   
23 */
24 using System;
25 using System.Globalization;
26 using System.Configuration.Assemblies;
27 using System.IO;
28 using System.Text;
29 using IKVM.Reflection.Reader;
30
31 namespace IKVM.Reflection
32 {
33         public sealed class AssemblyName : ICloneable
34         {
35                 private string name;
36                 private string culture;
37                 private Version version;
38                 private byte[] publicKeyToken;
39                 private byte[] publicKey;
40                 private StrongNameKeyPair keyPair;
41                 private AssemblyNameFlags flags;
42                 private AssemblyHashAlgorithm hashAlgorithm;
43                 private AssemblyVersionCompatibility versionCompatibility = AssemblyVersionCompatibility.SameMachine;
44                 private string codeBase;
45                 internal byte[] hash;
46
47                 public AssemblyName()
48                 {
49                 }
50
51                 public AssemblyName(string assemblyName)
52                 {
53                         if (assemblyName == null)
54                         {
55                                 throw new ArgumentNullException("assemblyName");
56                         }
57                         if (assemblyName == "")
58                         {
59                                 throw new ArgumentException();
60                         }
61                         ParsedAssemblyName parsed;
62                         switch (Fusion.ParseAssemblyName(assemblyName, out parsed))
63                         {
64                                 case ParseAssemblyResult.GenericError:
65                                         throw new FileLoadException();
66                                 case ParseAssemblyResult.DuplicateKey:
67                                         throw new System.Runtime.InteropServices.COMException();
68                         }
69                         name = parsed.Name;
70                         if (parsed.Culture != null)
71                         {
72                                 if (parsed.Culture.Equals("neutral", StringComparison.InvariantCultureIgnoreCase))
73                                 {
74                                         culture = "";
75                                 }
76                                 else if (parsed.Culture == "")
77                                 {
78                                         throw new FileLoadException();
79                                 }
80                                 else
81                                 {
82                                         culture = new CultureInfo(parsed.Culture).Name;
83                                 }
84                         }
85                         if (parsed.Version != null && parsed.Version.Major != 65535 && parsed.Version.Minor != 65535)
86                         {
87                                 // our Fusion parser returns -1 for build and revision for incomplete version numbers (and we want 65535)
88                                 version = new Version(parsed.Version.Major, parsed.Version.Minor, parsed.Version.Build & 0xFFFF, parsed.Version.Revision & 0xFFFF);
89                         }
90                         if (parsed.PublicKeyToken != null)
91                         {
92                                 if (parsed.PublicKeyToken.Equals("null", StringComparison.InvariantCultureIgnoreCase))
93                                 {
94                                         publicKeyToken = Empty<byte>.Array;
95                                 }
96                                 else if (parsed.PublicKeyToken.Length != 16)
97                                 {
98                                         throw new FileLoadException();
99                                 }
100                                 else
101                                 {
102                                         publicKeyToken = new byte[8];
103                                         for (int i = 0, pos = 0; i < publicKeyToken.Length; i++, pos += 2)
104                                         {
105                                                 publicKeyToken[i] = (byte)("0123456789abcdef".IndexOf(char.ToLowerInvariant(parsed.PublicKeyToken[pos])) * 16
106                                                         + "0123456789abcdef".IndexOf(char.ToLowerInvariant(parsed.PublicKeyToken[pos + 1])));
107                                         }
108                                 }
109                         }
110                         if (parsed.Retargetable.HasValue)
111                         {
112                                 if (parsed.Culture == null || parsed.PublicKeyToken == null || parsed.Version == null || parsed.Version.Build == -1 || parsed.Version.Revision == -1)
113                                 {
114                                         throw new FileLoadException();
115                                 }
116                                 if (parsed.Retargetable.Value)
117                                 {
118                                         flags |= AssemblyNameFlags.Retargetable;
119                                 }
120                         }
121                         ProcessorArchitecture = parsed.ProcessorArchitecture;
122                         if (parsed.WindowsRuntime)
123                         {
124                                 ContentType = AssemblyContentType.WindowsRuntime;
125                         }
126                 }
127
128                 public override string ToString()
129                 {
130                         return FullName;
131                 }
132
133                 public string Name
134                 {
135                         get { return name; }
136                         set { name = value; }
137                 }
138
139                 public CultureInfo CultureInfo
140                 {
141                         get { return culture == null ? null : new CultureInfo(culture); }
142                         set { culture = value == null ? null : value.Name; }
143                 }
144
145                 internal string Culture
146                 {
147                         get { return culture; }
148                         set { culture = value; }
149                 }
150
151                 public Version Version
152                 {
153                         get { return version; }
154                         set { version = value; }
155                 }
156
157                 public StrongNameKeyPair KeyPair
158                 {
159                         get { return keyPair; }
160                         set { keyPair = value; }
161                 }
162
163                 public string CodeBase
164                 {
165                         get { return codeBase; }
166                         set { codeBase = value; }
167                 }
168
169                 public string EscapedCodeBase
170                 {
171                         get
172                         {
173                                 // HACK use the real AssemblyName to escape the codebase
174                                 System.Reflection.AssemblyName tmp = new System.Reflection.AssemblyName();
175                                 tmp.CodeBase = codeBase;
176                                 return tmp.EscapedCodeBase;
177                         }
178                 }
179
180                 public ProcessorArchitecture ProcessorArchitecture
181                 {
182                         get { return (ProcessorArchitecture)(((int)flags & 0x70) >> 4); }
183                         set
184                         {
185                                 if (value >= ProcessorArchitecture.None && value <= ProcessorArchitecture.Arm)
186                                 {
187                                         flags = (flags & ~(AssemblyNameFlags)0x70) | (AssemblyNameFlags)((int)value << 4);
188                                 }
189                         }
190                 }
191
192                 public AssemblyNameFlags Flags
193                 {
194                         get { return flags & (AssemblyNameFlags)~0xEF0; }
195                         set { flags = (flags & (AssemblyNameFlags)0xEF0) | (value & (AssemblyNameFlags)~0xEF0); }
196                 }
197
198                 public AssemblyVersionCompatibility VersionCompatibility
199                 {
200                         get { return versionCompatibility; }
201                         set { versionCompatibility = value; }
202                 }
203
204                 public AssemblyContentType ContentType
205                 {
206                         get { return (AssemblyContentType)(((int)flags & 0xE00) >> 9); }
207                         set
208                         {
209                                 if (value >= AssemblyContentType.Default && value <= AssemblyContentType.WindowsRuntime)
210                                 {
211                                         flags = (flags & ~(AssemblyNameFlags)0xE00) | (AssemblyNameFlags)((int)value << 9);
212                                 }
213                         }
214                 }
215
216                 public byte[] GetPublicKey()
217                 {
218                         return publicKey;
219                 }
220
221                 public void SetPublicKey(byte[] publicKey)
222                 {
223                         this.publicKey = publicKey;
224                         flags = (flags & ~AssemblyNameFlags.PublicKey) | (publicKey == null ? 0 : AssemblyNameFlags.PublicKey);
225                 }
226
227                 public byte[] GetPublicKeyToken()
228                 {
229                         if (publicKeyToken == null && publicKey != null)
230                         {
231                                 // note that GetPublicKeyToken() has a side effect in this case, because we retain this token even after the public key subsequently gets changed
232                                 publicKeyToken = ComputePublicKeyToken(publicKey);
233                         }
234                         return publicKeyToken;
235                 }
236
237                 public void SetPublicKeyToken(byte[] publicKeyToken)
238                 {
239                         this.publicKeyToken = publicKeyToken;
240                 }
241
242                 public AssemblyHashAlgorithm HashAlgorithm
243                 {
244                         get { return hashAlgorithm; }
245                         set { hashAlgorithm = value; }
246                 }
247
248                 public byte[] __Hash
249                 {
250                         get { return hash; }
251                 }
252
253                 public string FullName
254                 {
255                         get
256                         {
257                                 if (name == null)
258                                 {
259                                         return "";
260                                 }
261                                 StringBuilder sb = new StringBuilder();
262                                 bool doubleQuotes = name.StartsWith(" ") || name.EndsWith(" ") || name.IndexOf('\'') != -1;
263                                 bool singleQuotes = name.IndexOf('"') != -1;
264                                 if (singleQuotes)
265                                 {
266                                         sb.Append('\'');
267                                 }
268                                 else if (doubleQuotes)
269                                 {
270                                         sb.Append('"');
271                                 }
272                                 if (name.IndexOf(',') != -1 || name.IndexOf('\\') != -1 || name.IndexOf('=') != -1 || (singleQuotes && name.IndexOf('\'') != -1))
273                                 {
274                                         for (int i = 0; i < name.Length; i++)
275                                         {
276                                                 char c = name[i];
277                                                 if (c == ',' || c == '\\' || c == '=' || (singleQuotes && c == '\''))
278                                                 {
279                                                         sb.Append('\\');
280                                                 }
281                                                 sb.Append(c);
282                                         }
283                                 }
284                                 else
285                                 {
286                                         sb.Append(name);
287                                 }
288                                 if (singleQuotes)
289                                 {
290                                         sb.Append('\'');
291                                 }
292                                 else if (doubleQuotes)
293                                 {
294                                         sb.Append('"');
295                                 }
296                                 if (version != null)
297                                 {
298                                         if ((version.Major & 0xFFFF) != 0xFFFF)
299                                         {
300                                                 sb.Append(", Version=").Append(version.Major & 0xFFFF);
301                                                 if ((version.Minor & 0xFFFF) != 0xFFFF)
302                                                 {
303                                                         sb.Append('.').Append(version.Minor & 0xFFFF);
304                                                         if ((version.Build & 0xFFFF) != 0xFFFF)
305                                                         {
306                                                                 sb.Append('.').Append(version.Build & 0xFFFF);
307                                                                 if ((version.Revision & 0xFFFF) != 0xFFFF)
308                                                                 {
309                                                                         sb.Append('.').Append(version.Revision & 0xFFFF);
310                                                                 }
311                                                         }
312                                                 }
313                                         }
314                                 }
315                                 if (culture != null)
316                                 {
317                                         sb.Append(", Culture=").Append(culture == "" ? "neutral" : culture);
318                                 }
319                                 byte[] publicKeyToken = this.publicKeyToken;
320                                 if ((publicKeyToken == null || publicKeyToken.Length == 0) && publicKey != null)
321                                 {
322                                         publicKeyToken = ComputePublicKeyToken(publicKey);
323                                 }
324                                 if (publicKeyToken != null)
325                                 {
326                                         sb.Append(", PublicKeyToken=");
327                                         if (publicKeyToken.Length == 0)
328                                         {
329                                                 sb.Append("null");
330                                         }
331                                         else
332                                         {
333                                                 AppendPublicKey(sb, publicKeyToken);
334                                         }
335                                 }
336                                 if ((Flags & AssemblyNameFlags.Retargetable) != 0)
337                                 {
338                                         sb.Append(", Retargetable=Yes");
339                                 }
340                                 if (ContentType == AssemblyContentType.WindowsRuntime)
341                                 {
342                                         sb.Append(", ContentType=WindowsRuntime");
343                                 }
344                                 return sb.ToString();
345                         }
346                 }
347
348                 private static byte[] ComputePublicKeyToken(byte[] publicKey)
349                 {
350                         if (publicKey.Length == 0)
351                         {
352                                 return publicKey;
353                         }
354                         // HACK use the real AssemblyName to convert PublicKey to PublicKeyToken
355                         StringBuilder sb = new StringBuilder("Foo, PublicKey=", 20 + publicKey.Length * 2);
356                         AppendPublicKey(sb, publicKey);
357                         string str = sb.ToString();
358                         if (str == "Foo, PublicKey=00000000000000000400000000000000")
359                         {
360                                 // MONOBUG workaround Mono 2.10 bug (fixed in 2.11)
361                                 // it does not return the correct public key token for the ECMA key
362                                 return new byte[] { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89 };
363                         }
364                         return new System.Reflection.AssemblyName(str).GetPublicKeyToken();
365                 }
366
367                 private static void AppendPublicKey(StringBuilder sb, byte[] publicKey)
368                 {
369                         for (int i = 0; i < publicKey.Length; i++)
370                         {
371                                 sb.Append("0123456789abcdef"[publicKey[i] >> 4]);
372                                 sb.Append("0123456789abcdef"[publicKey[i] & 0x0F]);
373                         }
374                 }
375
376                 public override bool Equals(object obj)
377                 {
378                         AssemblyName other = obj as AssemblyName;
379                         return other != null && other.FullName == this.FullName;
380                 }
381
382                 public override int GetHashCode()
383                 {
384                         return FullName.GetHashCode();
385                 }
386
387                 public object Clone()
388                 {
389                         AssemblyName copy = (AssemblyName)MemberwiseClone();
390                         copy.publicKey = Copy(publicKey);
391                         copy.publicKeyToken = Copy(publicKeyToken);
392                         return copy;
393                 }
394
395                 private static byte[] Copy(byte[] b)
396                 {
397                         return b == null || b.Length == 0 ? b : (byte[])b.Clone();
398                 }
399
400                 public static bool ReferenceMatchesDefinition(AssemblyName reference, AssemblyName definition)
401                 {
402                         // HACK use the real AssemblyName to implement the (broken) ReferenceMatchesDefinition method
403                         return System.Reflection.AssemblyName.ReferenceMatchesDefinition(new System.Reflection.AssemblyName(reference.FullName), new System.Reflection.AssemblyName(definition.FullName));
404                 }
405
406                 public static AssemblyName GetAssemblyName(string path)
407                 {
408                         try
409                         {
410                                 path = Path.GetFullPath(path);
411                                 using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
412                                 {
413                                         ModuleReader module = new ModuleReader(null, null, fs, path);
414                                         if (module.Assembly == null)
415                                         {
416                                                 throw new BadImageFormatException("Module does not contain a manifest");
417                                         }
418                                         return module.Assembly.GetName();
419                                 }
420                         }
421                         catch (IOException x)
422                         {
423                                 throw new FileNotFoundException(x.Message, x);
424                         }
425                         catch (UnauthorizedAccessException x)
426                         {
427                                 throw new FileNotFoundException(x.Message, x);
428                         }
429                 }
430
431                 internal AssemblyNameFlags RawFlags
432                 {
433                         get { return flags; }
434                         set { flags = value; }
435                 }
436         }
437 }