AssemblyName.cs 18 KB

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