2 Copyright (C) 2009-2011 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;
124 public override string ToString()
132 set { name = value; }
135 public CultureInfo CultureInfo
137 get { return culture == null ? null : new CultureInfo(culture); }
138 set { culture = value == null ? null : value.Name; }
141 internal string Culture
143 get { return culture; }
144 set { culture = value; }
147 public Version Version
149 get { return version; }
150 set { version = value; }
153 public StrongNameKeyPair KeyPair
155 get { return keyPair; }
156 set { keyPair = value; }
159 public string CodeBase
161 get { return codeBase; }
162 set { codeBase = value; }
165 public string EscapedCodeBase
169 // HACK use the real AssemblyName to escape the codebase
170 System.Reflection.AssemblyName tmp = new System.Reflection.AssemblyName();
171 tmp.CodeBase = codeBase;
172 return tmp.EscapedCodeBase;
176 public ProcessorArchitecture ProcessorArchitecture
178 get { return (ProcessorArchitecture)(((int)flags & 0x70) >> 4); }
181 if (value >= ProcessorArchitecture.None && value <= ProcessorArchitecture.Arm)
183 flags = (flags & ~(AssemblyNameFlags)0x70) | (AssemblyNameFlags)((int)value << 4);
188 public AssemblyNameFlags Flags
190 get { return flags & (AssemblyNameFlags)~0xF0; }
191 set { flags = (flags & (AssemblyNameFlags)0xF0) | (value & (AssemblyNameFlags)~0xF0); }
194 public AssemblyVersionCompatibility VersionCompatibility
196 get { return versionCompatibility; }
197 set { versionCompatibility = value; }
200 public byte[] GetPublicKey()
205 public void SetPublicKey(byte[] publicKey)
207 this.publicKey = publicKey;
208 flags = (flags & ~AssemblyNameFlags.PublicKey) | (publicKey == null ? 0 : AssemblyNameFlags.PublicKey);
211 public byte[] GetPublicKeyToken()
213 if (publicKeyToken == null && publicKey != null)
215 // note that GetPublicKeyToken() has a side effect in this case, because we retain this token even after the public key subsequently gets changed
216 publicKeyToken = ComputePublicKeyToken(publicKey);
218 return publicKeyToken;
221 public void SetPublicKeyToken(byte[] publicKeyToken)
223 this.publicKeyToken = publicKeyToken;
226 public AssemblyHashAlgorithm HashAlgorithm
228 get { return hashAlgorithm; }
229 set { hashAlgorithm = value; }
232 public string FullName
240 StringBuilder sb = new StringBuilder();
241 bool doubleQuotes = name.StartsWith(" ") || name.EndsWith(" ") || name.IndexOf('\'') != -1;
242 bool singleQuotes = name.IndexOf('"') != -1;
247 else if (doubleQuotes)
251 if (name.IndexOf(',') != -1 || name.IndexOf('\\') != -1 || name.IndexOf('=') != -1 || (singleQuotes && name.IndexOf('\'') != -1))
253 for (int i = 0; i < name.Length; i++)
256 if (c == ',' || c == '\\' || c == '=' || (singleQuotes && c == '\''))
271 else if (doubleQuotes)
277 if ((version.Major & 0xFFFF) != 0xFFFF)
279 sb.AppendFormat(", Version={0}", version.Major & 0xFFFF);
280 if ((version.Minor & 0xFFFF) != 0xFFFF)
282 sb.AppendFormat(".{0}", version.Minor & 0xFFFF);
283 if ((version.Build & 0xFFFF) != 0xFFFF)
285 sb.AppendFormat(".{0}", version.Build & 0xFFFF);
286 if ((version.Revision & 0xFFFF) != 0xFFFF)
288 sb.AppendFormat(".{0}", version.Revision & 0xFFFF);
296 sb.Append(", Culture=").Append(culture == "" ? "neutral" : culture);
298 byte[] publicKeyToken = this.publicKeyToken;
299 if ((publicKeyToken == null || publicKeyToken.Length == 0) && publicKey != null)
301 publicKeyToken = ComputePublicKeyToken(publicKey);
303 if (publicKeyToken != null)
305 sb.Append(", PublicKeyToken=");
306 if (publicKeyToken.Length == 0)
312 for (int i = 0; i < publicKeyToken.Length; i++)
314 sb.AppendFormat("{0:x2}", publicKeyToken[i]);
318 if ((Flags & AssemblyNameFlags.Retargetable) != 0)
320 sb.Append(", Retargetable=Yes");
322 return sb.ToString();
326 private static byte[] ComputePublicKeyToken(byte[] publicKey)
328 if (publicKey.Length == 0)
332 // HACK use the real AssemblyName to convert PublicKey to PublicKeyToken
333 StringBuilder sb = new StringBuilder("Foo, PublicKey=", 20 + publicKey.Length * 2);
334 for (int i = 0; i < publicKey.Length; i++)
336 sb.AppendFormat("{0:x2}", publicKey[i]);
338 return new System.Reflection.AssemblyName(sb.ToString()).GetPublicKeyToken();
341 public override bool Equals(object obj)
343 AssemblyName other = obj as AssemblyName;
344 return other != null && other.FullName == this.FullName;
347 public override int GetHashCode()
349 return FullName.GetHashCode();
352 public object Clone()
354 AssemblyName copy = (AssemblyName)MemberwiseClone();
355 copy.publicKey = Copy(publicKey);
356 copy.publicKeyToken = Copy(publicKeyToken);
360 private static byte[] Copy(byte[] b)
362 return b == null || b.Length == 0 ? b : (byte[])b.Clone();
365 public static bool ReferenceMatchesDefinition(AssemblyName reference, AssemblyName definition)
367 // HACK use the real AssemblyName to implement the (broken) ReferenceMatchesDefinition method
368 return System.Reflection.AssemblyName.ReferenceMatchesDefinition(new System.Reflection.AssemblyName(reference.FullName), new System.Reflection.AssemblyName(definition.FullName));
371 public static AssemblyName GetAssemblyName(string path)
375 path = Path.GetFullPath(path);
376 using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
378 ModuleReader module = new ModuleReader(null, null, fs, path);
379 if (module.Assembly == null)
381 throw new BadImageFormatException("Module does not contain a manifest");
383 return module.Assembly.GetName();
386 catch (IOException x)
388 throw new FileNotFoundException(x.Message, x);
390 catch (UnauthorizedAccessException x)
392 throw new FileNotFoundException(x.Message, x);
396 internal AssemblyNameFlags RawFlags
398 get { return flags; }
399 set { flags = value; }