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;
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; }
237 public string FullName
245 StringBuilder sb = new StringBuilder();
246 bool doubleQuotes = name.StartsWith(" ") || name.EndsWith(" ") || name.IndexOf('\'') != -1;
247 bool singleQuotes = name.IndexOf('"') != -1;
252 else if (doubleQuotes)
256 if (name.IndexOf(',') != -1 || name.IndexOf('\\') != -1 || name.IndexOf('=') != -1 || (singleQuotes && name.IndexOf('\'') != -1))
258 for (int i = 0; i < name.Length; i++)
261 if (c == ',' || c == '\\' || c == '=' || (singleQuotes && c == '\''))
276 else if (doubleQuotes)
282 if ((version.Major & 0xFFFF) != 0xFFFF)
284 sb.Append(", Version=").Append(version.Major & 0xFFFF);
285 if ((version.Minor & 0xFFFF) != 0xFFFF)
287 sb.Append('.').Append(version.Minor & 0xFFFF);
288 if ((version.Build & 0xFFFF) != 0xFFFF)
290 sb.Append('.').Append(version.Build & 0xFFFF);
291 if ((version.Revision & 0xFFFF) != 0xFFFF)
293 sb.Append('.').Append(version.Revision & 0xFFFF);
301 sb.Append(", Culture=").Append(culture == "" ? "neutral" : culture);
303 byte[] publicKeyToken = this.publicKeyToken;
304 if ((publicKeyToken == null || publicKeyToken.Length == 0) && publicKey != null)
306 publicKeyToken = ComputePublicKeyToken(publicKey);
308 if (publicKeyToken != null)
310 sb.Append(", PublicKeyToken=");
311 if (publicKeyToken.Length == 0)
317 AppendPublicKey(sb, publicKeyToken);
320 if ((Flags & AssemblyNameFlags.Retargetable) != 0)
322 sb.Append(", Retargetable=Yes");
324 return sb.ToString();
328 private static byte[] ComputePublicKeyToken(byte[] publicKey)
330 if (publicKey.Length == 0)
334 // HACK use the real AssemblyName to convert PublicKey to PublicKeyToken
335 StringBuilder sb = new StringBuilder("Foo, PublicKey=", 20 + publicKey.Length * 2);
336 AppendPublicKey(sb, publicKey);
337 string str = sb.ToString();
338 if (str == "Foo, PublicKey=00000000000000000400000000000000")
340 // MONOBUG workaround Mono 2.10 bug (fixed in 2.11)
341 // it does not return the correct public key token for the ECMA key
342 return new byte[] { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89 };
344 return new System.Reflection.AssemblyName(str).GetPublicKeyToken();
347 private static void AppendPublicKey(StringBuilder sb, byte[] publicKey)
349 for (int i = 0; i < publicKey.Length; i++)
351 sb.Append("0123456789abcdef"[publicKey[i] >> 4]);
352 sb.Append("0123456789abcdef"[publicKey[i] & 0x0F]);
356 public override bool Equals(object obj)
358 AssemblyName other = obj as AssemblyName;
359 return other != null && other.FullName == this.FullName;
362 public override int GetHashCode()
364 return FullName.GetHashCode();
367 public object Clone()
369 AssemblyName copy = (AssemblyName)MemberwiseClone();
370 copy.publicKey = Copy(publicKey);
371 copy.publicKeyToken = Copy(publicKeyToken);
375 private static byte[] Copy(byte[] b)
377 return b == null || b.Length == 0 ? b : (byte[])b.Clone();
380 public static bool ReferenceMatchesDefinition(AssemblyName reference, AssemblyName definition)
382 // HACK use the real AssemblyName to implement the (broken) ReferenceMatchesDefinition method
383 return System.Reflection.AssemblyName.ReferenceMatchesDefinition(new System.Reflection.AssemblyName(reference.FullName), new System.Reflection.AssemblyName(definition.FullName));
386 public static AssemblyName GetAssemblyName(string path)
390 path = Path.GetFullPath(path);
391 using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
393 ModuleReader module = new ModuleReader(null, null, fs, path);
394 if (module.Assembly == null)
396 throw new BadImageFormatException("Module does not contain a manifest");
398 return module.Assembly.GetName();
401 catch (IOException x)
403 throw new FileNotFoundException(x.Message, x);
405 catch (UnauthorizedAccessException x)
407 throw new FileNotFoundException(x.Message, x);
411 internal AssemblyNameFlags RawFlags
413 get { return flags; }
414 set { flags = value; }