2 Copyright (C) 2009-2012 Jeroen Frijters
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.
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:
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.
25 using System.Globalization;
26 using System.Configuration.Assemblies;
29 using IKVM.Reflection.Reader;
31 namespace IKVM.Reflection
33 public sealed class AssemblyName : ICloneable
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;
51 public AssemblyName(string assemblyName)
53 if (assemblyName == null)
55 throw new ArgumentNullException("assemblyName");
57 if (assemblyName == "")
59 throw new ArgumentException();
61 ParsedAssemblyName parsed;
62 switch (Fusion.ParseAssemblyName(assemblyName, out parsed))
64 case ParseAssemblyResult.GenericError:
65 throw new FileLoadException();
66 case ParseAssemblyResult.DuplicateKey:
67 throw new System.Runtime.InteropServices.COMException();
70 if (parsed.Culture != null)
72 if (parsed.Culture.Equals("neutral", StringComparison.InvariantCultureIgnoreCase))
76 else if (parsed.Culture == "")
78 throw new FileLoadException();
82 culture = new CultureInfo(parsed.Culture).Name;
85 if (parsed.Version != null && parsed.Version.Major != 65535 && parsed.Version.Minor != 65535)
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);
90 if (parsed.PublicKeyToken != null)
92 if (parsed.PublicKeyToken.Equals("null", StringComparison.InvariantCultureIgnoreCase))
94 publicKeyToken = Empty<byte>.Array;
96 else if (parsed.PublicKeyToken.Length != 16)
98 throw new FileLoadException();
102 publicKeyToken = new byte[8];
103 for (int i = 0, pos = 0; i < publicKeyToken.Length; i++, pos += 2)
105 publicKeyToken[i] = (byte)("0123456789abcdef".IndexOf(char.ToLowerInvariant(parsed.PublicKeyToken[pos])) * 16
106 + "0123456789abcdef".IndexOf(char.ToLowerInvariant(parsed.PublicKeyToken[pos + 1])));
110 if (parsed.Retargetable.HasValue)
112 if (parsed.Culture == null || parsed.PublicKeyToken == null || parsed.Version == null || parsed.Version.Build == -1 || parsed.Version.Revision == -1)
114 throw new FileLoadException();
116 if (parsed.Retargetable.Value)
118 flags |= AssemblyNameFlags.Retargetable;
121 ProcessorArchitecture = parsed.ProcessorArchitecture;
122 if (parsed.WindowsRuntime)
124 ContentType = AssemblyContentType.WindowsRuntime;
128 public override string ToString()
136 set { name = value; }
139 public CultureInfo CultureInfo
141 get { return culture == null ? null : new CultureInfo(culture); }
142 set { culture = value == null ? null : value.Name; }
145 internal string Culture
147 get { return culture; }
148 set { culture = value; }
151 public Version Version
153 get { return version; }
154 set { version = value; }
157 public StrongNameKeyPair KeyPair
159 get { return keyPair; }
160 set { keyPair = value; }
163 public string CodeBase
165 get { return codeBase; }
166 set { codeBase = value; }
169 public string EscapedCodeBase
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;
180 public ProcessorArchitecture ProcessorArchitecture
182 get { return (ProcessorArchitecture)(((int)flags & 0x70) >> 4); }
185 if (value >= ProcessorArchitecture.None && value <= ProcessorArchitecture.Arm)
187 flags = (flags & ~(AssemblyNameFlags)0x70) | (AssemblyNameFlags)((int)value << 4);
192 public AssemblyNameFlags Flags
194 get { return flags & (AssemblyNameFlags)~0xEF0; }
195 set { flags = (flags & (AssemblyNameFlags)0xEF0) | (value & (AssemblyNameFlags)~0xEF0); }
198 public AssemblyVersionCompatibility VersionCompatibility
200 get { return versionCompatibility; }
201 set { versionCompatibility = value; }
204 public AssemblyContentType ContentType
206 get { return (AssemblyContentType)(((int)flags & 0xE00) >> 9); }
209 if (value >= AssemblyContentType.Default && value <= AssemblyContentType.WindowsRuntime)
211 flags = (flags & ~(AssemblyNameFlags)0xE00) | (AssemblyNameFlags)((int)value << 9);
216 public byte[] GetPublicKey()
221 public void SetPublicKey(byte[] publicKey)
223 this.publicKey = publicKey;
224 flags = (flags & ~AssemblyNameFlags.PublicKey) | (publicKey == null ? 0 : AssemblyNameFlags.PublicKey);
227 public byte[] GetPublicKeyToken()
229 if (publicKeyToken == null && publicKey != null)
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);
234 return publicKeyToken;
237 public void SetPublicKeyToken(byte[] publicKeyToken)
239 this.publicKeyToken = publicKeyToken;
242 public AssemblyHashAlgorithm HashAlgorithm
244 get { return hashAlgorithm; }
245 set { hashAlgorithm = value; }
253 public string FullName
261 StringBuilder sb = new StringBuilder();
262 bool doubleQuotes = name.StartsWith(" ") || name.EndsWith(" ") || name.IndexOf('\'') != -1;
263 bool singleQuotes = name.IndexOf('"') != -1;
268 else if (doubleQuotes)
272 if (name.IndexOf(',') != -1 || name.IndexOf('\\') != -1 || name.IndexOf('=') != -1 || (singleQuotes && name.IndexOf('\'') != -1))
274 for (int i = 0; i < name.Length; i++)
277 if (c == ',' || c == '\\' || c == '=' || (singleQuotes && c == '\''))
292 else if (doubleQuotes)
298 if ((version.Major & 0xFFFF) != 0xFFFF)
300 sb.Append(", Version=").Append(version.Major & 0xFFFF);
301 if ((version.Minor & 0xFFFF) != 0xFFFF)
303 sb.Append('.').Append(version.Minor & 0xFFFF);
304 if ((version.Build & 0xFFFF) != 0xFFFF)
306 sb.Append('.').Append(version.Build & 0xFFFF);
307 if ((version.Revision & 0xFFFF) != 0xFFFF)
309 sb.Append('.').Append(version.Revision & 0xFFFF);
317 sb.Append(", Culture=").Append(culture == "" ? "neutral" : culture);
319 byte[] publicKeyToken = this.publicKeyToken;
320 if ((publicKeyToken == null || publicKeyToken.Length == 0) && publicKey != null)
322 publicKeyToken = ComputePublicKeyToken(publicKey);
324 if (publicKeyToken != null)
326 sb.Append(", PublicKeyToken=");
327 if (publicKeyToken.Length == 0)
333 AppendPublicKey(sb, publicKeyToken);
336 if ((Flags & AssemblyNameFlags.Retargetable) != 0)
338 sb.Append(", Retargetable=Yes");
340 if (ContentType == AssemblyContentType.WindowsRuntime)
342 sb.Append(", ContentType=WindowsRuntime");
344 return sb.ToString();
348 private static byte[] ComputePublicKeyToken(byte[] publicKey)
350 if (publicKey.Length == 0)
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")
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 };
364 return new System.Reflection.AssemblyName(str).GetPublicKeyToken();
367 private static void AppendPublicKey(StringBuilder sb, byte[] publicKey)
369 for (int i = 0; i < publicKey.Length; i++)
371 sb.Append("0123456789abcdef"[publicKey[i] >> 4]);
372 sb.Append("0123456789abcdef"[publicKey[i] & 0x0F]);
376 public override bool Equals(object obj)
378 AssemblyName other = obj as AssemblyName;
379 return other != null && other.FullName == this.FullName;
382 public override int GetHashCode()
384 return FullName.GetHashCode();
387 public object Clone()
389 AssemblyName copy = (AssemblyName)MemberwiseClone();
390 copy.publicKey = Copy(publicKey);
391 copy.publicKeyToken = Copy(publicKeyToken);
395 private static byte[] Copy(byte[] b)
397 return b == null || b.Length == 0 ? b : (byte[])b.Clone();
400 public static bool ReferenceMatchesDefinition(AssemblyName reference, AssemblyName definition)
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));
406 public static AssemblyName GetAssemblyName(string path)
410 path = Path.GetFullPath(path);
411 using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
413 ModuleReader module = new ModuleReader(null, null, fs, path);
414 if (module.Assembly == null)
416 throw new BadImageFormatException("Module does not contain a manifest");
418 return module.Assembly.GetName();
421 catch (IOException x)
423 throw new FileNotFoundException(x.Message, x);
425 catch (UnauthorizedAccessException x)
427 throw new FileNotFoundException(x.Message, x);
431 internal AssemblyNameFlags RawFlags
433 get { return flags; }
434 set { flags = value; }