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 return new System.Reflection.AssemblyName(sb.ToString()).GetPublicKeyToken();
340 private static void AppendPublicKey(StringBuilder sb, byte[] publicKey)
342 for (int i = 0; i < publicKey.Length; i++)
344 sb.Append("0123456789abcdef"[publicKey[i] >> 4]);
345 sb.Append("0123456789abcdef"[publicKey[i] & 0x0F]);
349 public override bool Equals(object obj)
351 AssemblyName other = obj as AssemblyName;
352 return other != null && other.FullName == this.FullName;
355 public override int GetHashCode()
357 return FullName.GetHashCode();
360 public object Clone()
362 AssemblyName copy = (AssemblyName)MemberwiseClone();
363 copy.publicKey = Copy(publicKey);
364 copy.publicKeyToken = Copy(publicKeyToken);
368 private static byte[] Copy(byte[] b)
370 return b == null || b.Length == 0 ? b : (byte[])b.Clone();
373 public static bool ReferenceMatchesDefinition(AssemblyName reference, AssemblyName definition)
375 // HACK use the real AssemblyName to implement the (broken) ReferenceMatchesDefinition method
376 return System.Reflection.AssemblyName.ReferenceMatchesDefinition(new System.Reflection.AssemblyName(reference.FullName), new System.Reflection.AssemblyName(definition.FullName));
379 public static AssemblyName GetAssemblyName(string path)
383 path = Path.GetFullPath(path);
384 using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
386 ModuleReader module = new ModuleReader(null, null, fs, path);
387 if (module.Assembly == null)
389 throw new BadImageFormatException("Module does not contain a manifest");
391 return module.Assembly.GetName();
394 catch (IOException x)
396 throw new FileNotFoundException(x.Message, x);
398 catch (UnauthorizedAccessException x)
400 throw new FileNotFoundException(x.Message, x);
404 internal AssemblyNameFlags RawFlags
406 get { return flags; }
407 set { flags = value; }