AssemblyName.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Configuration.Assemblies;
  5. using System.Runtime.Serialization;
  6. using System.Text;
  7. using CultureInfo = System.Globalization.CultureInfo;
  8. namespace System.Reflection
  9. {
  10. public sealed partial class AssemblyName : ICloneable, IDeserializationCallback, ISerializable
  11. {
  12. // If you modify any of these fields, you must also update the
  13. // AssemblyBaseObject structure in object.h
  14. private string? _name;
  15. private byte[]? _publicKey;
  16. private byte[]? _publicKeyToken;
  17. private CultureInfo? _cultureInfo;
  18. private string? _codeBase;
  19. private Version? _version;
  20. private StrongNameKeyPair? _strongNameKeyPair;
  21. private AssemblyHashAlgorithm _hashAlgorithm;
  22. private AssemblyVersionCompatibility _versionCompatibility;
  23. private AssemblyNameFlags _flags;
  24. public AssemblyName()
  25. {
  26. _versionCompatibility = AssemblyVersionCompatibility.SameMachine;
  27. }
  28. // Set and get the name of the assembly. If this is a weak Name
  29. // then it optionally contains a site. For strong assembly names,
  30. // the name partitions up the strong name's namespace
  31. public string? Name
  32. {
  33. get => _name;
  34. set => _name = value;
  35. }
  36. public Version? Version
  37. {
  38. get => _version;
  39. set => _version = value;
  40. }
  41. // Locales, internally the LCID is used for the match.
  42. public CultureInfo? CultureInfo
  43. {
  44. get => _cultureInfo;
  45. set => _cultureInfo = value;
  46. }
  47. public string? CultureName
  48. {
  49. get => _cultureInfo?.Name;
  50. set => _cultureInfo = (value == null) ? null : new CultureInfo(value);
  51. }
  52. public string? CodeBase
  53. {
  54. get => _codeBase;
  55. set => _codeBase = value;
  56. }
  57. public string? EscapedCodeBase
  58. {
  59. get
  60. {
  61. if (_codeBase == null)
  62. return null;
  63. else
  64. return EscapeCodeBase(_codeBase);
  65. }
  66. }
  67. public ProcessorArchitecture ProcessorArchitecture
  68. {
  69. get
  70. {
  71. int x = (((int)_flags) & 0x70) >> 4;
  72. if (x > 5)
  73. x = 0;
  74. return (ProcessorArchitecture)x;
  75. }
  76. set
  77. {
  78. int x = ((int)value) & 0x07;
  79. if (x <= 5)
  80. {
  81. _flags = (AssemblyNameFlags)((int)_flags & 0xFFFFFF0F);
  82. _flags |= (AssemblyNameFlags)(x << 4);
  83. }
  84. }
  85. }
  86. public AssemblyContentType ContentType
  87. {
  88. get
  89. {
  90. int x = (((int)_flags) & 0x00000E00) >> 9;
  91. if (x > 1)
  92. x = 0;
  93. return (AssemblyContentType)x;
  94. }
  95. set
  96. {
  97. int x = ((int)value) & 0x07;
  98. if (x <= 1)
  99. {
  100. _flags = (AssemblyNameFlags)((int)_flags & 0xFFFFF1FF);
  101. _flags |= (AssemblyNameFlags)(x << 9);
  102. }
  103. }
  104. }
  105. // Make a copy of this assembly name.
  106. public object Clone()
  107. {
  108. var name = new AssemblyName
  109. {
  110. _name = _name,
  111. _publicKey = (byte[]?)_publicKey?.Clone(),
  112. _publicKeyToken = (byte[]?)_publicKeyToken?.Clone(),
  113. _cultureInfo = _cultureInfo,
  114. _version = (Version?)_version?.Clone(),
  115. _flags = _flags,
  116. _codeBase = _codeBase,
  117. _hashAlgorithm = _hashAlgorithm,
  118. _versionCompatibility = _versionCompatibility,
  119. };
  120. return name;
  121. }
  122. /*
  123. * Get the AssemblyName for a given file. This will only work
  124. * if the file contains an assembly manifest. This method causes
  125. * the file to be opened and closed.
  126. */
  127. public static AssemblyName GetAssemblyName(string assemblyFile)
  128. {
  129. if (assemblyFile == null)
  130. throw new ArgumentNullException(nameof(assemblyFile));
  131. return GetFileInformationCore(assemblyFile);
  132. }
  133. public byte[]? GetPublicKey()
  134. {
  135. return _publicKey;
  136. }
  137. public void SetPublicKey(byte[]? publicKey)
  138. {
  139. _publicKey = publicKey;
  140. if (publicKey == null)
  141. _flags &= ~AssemblyNameFlags.PublicKey;
  142. else
  143. _flags |= AssemblyNameFlags.PublicKey;
  144. }
  145. // The compressed version of the public key formed from a truncated hash.
  146. // Will throw a SecurityException if _publicKey is invalid
  147. public byte[]? GetPublicKeyToken() => _publicKeyToken ??= ComputePublicKeyToken();
  148. public void SetPublicKeyToken(byte[]? publicKeyToken)
  149. {
  150. _publicKeyToken = publicKeyToken;
  151. }
  152. // Flags modifying the name. So far the only flag is PublicKey, which
  153. // indicates that a full public key and not the compressed version is
  154. // present.
  155. // Processor Architecture flags are set only through ProcessorArchitecture
  156. // property and can't be set or retrieved directly
  157. // Content Type flags are set only through ContentType property and can't be
  158. // set or retrieved directly
  159. public AssemblyNameFlags Flags
  160. {
  161. get => (AssemblyNameFlags)((uint)_flags & 0xFFFFF10F);
  162. set
  163. {
  164. _flags &= unchecked((AssemblyNameFlags)0x00000EF0);
  165. _flags |= (value & unchecked((AssemblyNameFlags)0xFFFFF10F));
  166. }
  167. }
  168. public AssemblyHashAlgorithm HashAlgorithm
  169. {
  170. get => _hashAlgorithm;
  171. set => _hashAlgorithm = value;
  172. }
  173. public AssemblyVersionCompatibility VersionCompatibility
  174. {
  175. get => _versionCompatibility;
  176. set => _versionCompatibility = value;
  177. }
  178. public StrongNameKeyPair? KeyPair
  179. {
  180. get => _strongNameKeyPair;
  181. set => _strongNameKeyPair = value;
  182. }
  183. public string FullName
  184. {
  185. get
  186. {
  187. if (this.Name == null)
  188. return string.Empty;
  189. // Do not call GetPublicKeyToken() here - that latches the result into AssemblyName which isn't a side effect we want.
  190. byte[]? pkt = _publicKeyToken ?? ComputePublicKeyToken();
  191. return AssemblyNameFormatter.ComputeDisplayName(Name, Version, CultureName, pkt, Flags, ContentType);
  192. }
  193. }
  194. public override string ToString()
  195. {
  196. string s = FullName;
  197. if (s == null)
  198. return base.ToString()!;
  199. else
  200. return s;
  201. }
  202. public void GetObjectData(SerializationInfo info, StreamingContext context)
  203. {
  204. throw new PlatformNotSupportedException();
  205. }
  206. public void OnDeserialization(object? sender)
  207. {
  208. throw new PlatformNotSupportedException();
  209. }
  210. /// <summary>
  211. /// Compares the simple names disregarding Version, Culture and PKT. While this clearly does not
  212. /// match the intent of this api, this api has been broken this way since its debut and we cannot
  213. /// change its behavior now.
  214. /// </summary>
  215. public static bool ReferenceMatchesDefinition(AssemblyName? reference, AssemblyName? definition)
  216. {
  217. if (object.ReferenceEquals(reference, definition))
  218. return true;
  219. if (reference == null)
  220. throw new ArgumentNullException(nameof(reference));
  221. if (definition == null)
  222. throw new ArgumentNullException(nameof(definition));
  223. string refName = reference.Name ?? string.Empty;
  224. string defName = definition.Name ?? string.Empty;
  225. return refName.Equals(defName, StringComparison.OrdinalIgnoreCase);
  226. }
  227. internal static string EscapeCodeBase(string? codebase)
  228. {
  229. if (codebase == null)
  230. return string.Empty;
  231. int position = 0;
  232. char[]? dest = EscapeString(codebase, 0, codebase.Length, null, ref position, true, c_DummyChar, c_DummyChar, c_DummyChar);
  233. if (dest == null)
  234. return codebase;
  235. return new string(dest, 0, position);
  236. }
  237. // This implementation of EscapeString has been copied from System.Private.Uri from corefx repo
  238. // - forceX characters are always escaped if found
  239. // - rsvd character will remain unescaped
  240. //
  241. // start - starting offset from input
  242. // end - the exclusive ending offset in input
  243. // destPos - starting offset in dest for output, on return this will be an exclusive "end" in the output.
  244. //
  245. // In case "dest" has lack of space it will be reallocated by preserving the _whole_ content up to current destPos
  246. //
  247. // Returns null if nothing has to be escaped AND passed dest was null, otherwise the resulting array with the updated destPos
  248. //
  249. internal static unsafe char[]? EscapeString(string input, int start, int end, char[]? dest, ref int destPos,
  250. bool isUriString, char force1, char force2, char rsvd)
  251. {
  252. int i = start;
  253. int prevInputPos = start;
  254. byte* bytes = stackalloc byte[c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar]; // 40*4=160
  255. fixed (char* pStr = input)
  256. {
  257. for (; i < end; ++i)
  258. {
  259. char ch = pStr[i];
  260. // a Unicode ?
  261. if (ch > '\x7F')
  262. {
  263. short maxSize = (short)Math.Min(end - i, (int)c_MaxUnicodeCharsReallocate - 1);
  264. short count = 1;
  265. for (; count < maxSize && pStr[i + count] > '\x7f'; ++count) ;
  266. // Is the last a high surrogate?
  267. if (pStr[i + count - 1] >= 0xD800 && pStr[i + count - 1] <= 0xDBFF)
  268. {
  269. // Should be a rare case where the app tries to feed an invalid Unicode surrogates pair
  270. if (count == 1 || count == end - i)
  271. throw new FormatException(SR.Arg_FormatException);
  272. // need to grab one more char as a Surrogate except when it's a bogus input
  273. ++count;
  274. }
  275. dest = EnsureDestinationSize(pStr, dest, i,
  276. (short)(count * c_MaxUTF_8BytesPerUnicodeChar * c_EncodedCharsPerByte),
  277. c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar * c_EncodedCharsPerByte,
  278. ref destPos, prevInputPos);
  279. short numberOfBytes = (short)Encoding.UTF8.GetBytes(pStr + i, count, bytes,
  280. c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar);
  281. // This is the only exception that built in UriParser can throw after a Uri ctor.
  282. // Should not happen unless the app tries to feed an invalid Unicode string
  283. if (numberOfBytes == 0)
  284. throw new FormatException(SR.Arg_FormatException);
  285. i += (count - 1);
  286. for (count = 0; count < numberOfBytes; ++count)
  287. EscapeAsciiChar((char)bytes[count], dest, ref destPos);
  288. prevInputPos = i + 1;
  289. }
  290. else if (ch == '%' && rsvd == '%')
  291. {
  292. // Means we don't reEncode '%' but check for the possible escaped sequence
  293. dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte,
  294. c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos);
  295. if (i + 2 < end && EscapedAscii(pStr[i + 1], pStr[i + 2]) != c_DummyChar)
  296. {
  297. // leave it escaped
  298. dest[destPos++] = '%';
  299. dest[destPos++] = pStr[i + 1];
  300. dest[destPos++] = pStr[i + 2];
  301. i += 2;
  302. }
  303. else
  304. {
  305. EscapeAsciiChar('%', dest, ref destPos);
  306. }
  307. prevInputPos = i + 1;
  308. }
  309. else if (ch == force1 || ch == force2 || (ch != rsvd && (isUriString ? !IsReservedUnreservedOrHash(ch) : !IsUnreserved(ch))))
  310. {
  311. dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte,
  312. c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos);
  313. EscapeAsciiChar(ch, dest, ref destPos);
  314. prevInputPos = i + 1;
  315. }
  316. }
  317. if (prevInputPos != i)
  318. {
  319. // need to fill up the dest array ?
  320. if (prevInputPos != start || dest != null)
  321. dest = EnsureDestinationSize(pStr, dest, i, 0, 0, ref destPos, prevInputPos);
  322. }
  323. }
  324. return dest;
  325. }
  326. //
  327. // ensure destination array has enough space and contains all the needed input stuff
  328. //
  329. private static unsafe char[] EnsureDestinationSize(char* pStr, char[]? dest, int currentInputPos,
  330. short charsToAdd, short minReallocateChars, ref int destPos, int prevInputPos)
  331. {
  332. if (dest is null || dest.Length < destPos + (currentInputPos - prevInputPos) + charsToAdd)
  333. {
  334. // allocating or reallocating array by ensuring enough space based on maxCharsToAdd.
  335. char[] newresult = new char[destPos + (currentInputPos - prevInputPos) + minReallocateChars];
  336. if (!(dest is null) && destPos != 0)
  337. Buffer.BlockCopy(dest, 0, newresult, 0, destPos << 1);
  338. dest = newresult;
  339. }
  340. // ensuring we copied everything form the input string left before last escaping
  341. while (prevInputPos != currentInputPos)
  342. dest[destPos++] = pStr[prevInputPos++];
  343. return dest;
  344. }
  345. internal static void EscapeAsciiChar(char ch, char[] to, ref int pos)
  346. {
  347. to[pos++] = '%';
  348. to[pos++] = s_hexUpperChars[(ch & 0xf0) >> 4];
  349. to[pos++] = s_hexUpperChars[ch & 0xf];
  350. }
  351. internal static char EscapedAscii(char digit, char next)
  352. {
  353. if (!(((digit >= '0') && (digit <= '9'))
  354. || ((digit >= 'A') && (digit <= 'F'))
  355. || ((digit >= 'a') && (digit <= 'f'))))
  356. {
  357. return c_DummyChar;
  358. }
  359. int res = (digit <= '9')
  360. ? ((int)digit - (int)'0')
  361. : (((digit <= 'F')
  362. ? ((int)digit - (int)'A')
  363. : ((int)digit - (int)'a'))
  364. + 10);
  365. if (!(((next >= '0') && (next <= '9'))
  366. || ((next >= 'A') && (next <= 'F'))
  367. || ((next >= 'a') && (next <= 'f'))))
  368. {
  369. return c_DummyChar;
  370. }
  371. return (char)((res << 4) + ((next <= '9')
  372. ? ((int)next - (int)'0')
  373. : (((next <= 'F')
  374. ? ((int)next - (int)'A')
  375. : ((int)next - (int)'a'))
  376. + 10)));
  377. }
  378. private static bool IsReservedUnreservedOrHash(char c)
  379. {
  380. if (IsUnreserved(c))
  381. {
  382. return true;
  383. }
  384. return RFC3986ReservedMarks.Contains(c);
  385. }
  386. internal static bool IsUnreserved(char c)
  387. {
  388. if (IsAsciiLetterOrDigit(c))
  389. {
  390. return true;
  391. }
  392. return RFC3986UnreservedMarks.Contains(c);
  393. }
  394. // Only consider ASCII characters
  395. internal static bool IsAsciiLetter(char character)
  396. {
  397. return (character >= 'a' && character <= 'z') ||
  398. (character >= 'A' && character <= 'Z');
  399. }
  400. internal static bool IsAsciiLetterOrDigit(char character)
  401. {
  402. return IsAsciiLetter(character) || (character >= '0' && character <= '9');
  403. }
  404. private static readonly char[] s_hexUpperChars = {
  405. '0', '1', '2', '3', '4', '5', '6', '7',
  406. '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
  407. internal const char c_DummyChar = (char)0xFFFF; // An Invalid Unicode character used as a dummy char passed into the parameter
  408. private const short c_MaxAsciiCharsReallocate = 40;
  409. private const short c_MaxUnicodeCharsReallocate = 40;
  410. private const short c_MaxUTF_8BytesPerUnicodeChar = 4;
  411. private const short c_EncodedCharsPerByte = 3;
  412. private const string RFC3986ReservedMarks = ":/?#[]@!$&'()*+,;=";
  413. private const string RFC3986UnreservedMarks = "-._~";
  414. }
  415. }