More Corelib cleanup (dotnet/coreclr#26872)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / Reflection / AssemblyName.cs
blob17cfdfe67dd1d82425c0180d5fae08d8f35f55f5
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.
5 using System.Configuration.Assemblies;
6 using System.Runtime.Serialization;
7 using System.Text;
8 using CultureInfo = System.Globalization.CultureInfo;
10 namespace System.Reflection
12 public sealed partial class AssemblyName : ICloneable, IDeserializationCallback, ISerializable
14 // If you modify any of these fields, you must also update the
15 // AssemblyBaseObject structure in object.h
16 private string? _name;
17 private byte[]? _publicKey;
18 private byte[]? _publicKeyToken;
19 private CultureInfo? _cultureInfo;
20 private string? _codeBase;
21 private Version? _version;
23 private StrongNameKeyPair? _strongNameKeyPair;
25 private AssemblyHashAlgorithm _hashAlgorithm;
27 private AssemblyVersionCompatibility _versionCompatibility;
28 private AssemblyNameFlags _flags;
30 public AssemblyName()
32 _versionCompatibility = AssemblyVersionCompatibility.SameMachine;
35 // Set and get the name of the assembly. If this is a weak Name
36 // then it optionally contains a site. For strong assembly names,
37 // the name partitions up the strong name's namespace
38 public string? Name
40 get => _name;
41 set => _name = value;
44 public Version? Version
46 get => _version;
47 set => _version = value;
50 // Locales, internally the LCID is used for the match.
51 public CultureInfo? CultureInfo
53 get => _cultureInfo;
54 set => _cultureInfo = value;
57 public string? CultureName
59 get => _cultureInfo?.Name;
60 set => _cultureInfo = (value == null) ? null : new CultureInfo(value);
63 public string? CodeBase
65 get => _codeBase;
66 set => _codeBase = value;
69 public string? EscapedCodeBase
71 get
73 if (_codeBase == null)
74 return null;
75 else
76 return EscapeCodeBase(_codeBase);
80 public ProcessorArchitecture ProcessorArchitecture
82 get
84 int x = (((int)_flags) & 0x70) >> 4;
85 if (x > 5)
86 x = 0;
87 return (ProcessorArchitecture)x;
89 set
91 int x = ((int)value) & 0x07;
92 if (x <= 5)
94 _flags = (AssemblyNameFlags)((int)_flags & 0xFFFFFF0F);
95 _flags |= (AssemblyNameFlags)(x << 4);
100 public AssemblyContentType ContentType
104 int x = (((int)_flags) & 0x00000E00) >> 9;
105 if (x > 1)
106 x = 0;
107 return (AssemblyContentType)x;
111 int x = ((int)value) & 0x07;
112 if (x <= 1)
114 _flags = (AssemblyNameFlags)((int)_flags & 0xFFFFF1FF);
115 _flags |= (AssemblyNameFlags)(x << 9);
120 // Make a copy of this assembly name.
121 public object Clone()
123 var name = new AssemblyName
125 _name = _name,
126 _publicKey = (byte[]?)_publicKey?.Clone(),
127 _publicKeyToken = (byte[]?)_publicKeyToken?.Clone(),
128 _cultureInfo = _cultureInfo,
129 _version = (Version?)_version?.Clone(),
130 _flags = _flags,
131 _codeBase = _codeBase,
132 _hashAlgorithm = _hashAlgorithm,
133 _versionCompatibility = _versionCompatibility,
135 return name;
139 * Get the AssemblyName for a given file. This will only work
140 * if the file contains an assembly manifest. This method causes
141 * the file to be opened and closed.
143 public static AssemblyName GetAssemblyName(string assemblyFile)
145 if (assemblyFile == null)
146 throw new ArgumentNullException(nameof(assemblyFile));
148 return GetFileInformationCore(assemblyFile);
151 public byte[]? GetPublicKey()
153 return _publicKey;
156 public void SetPublicKey(byte[]? publicKey)
158 _publicKey = publicKey;
160 if (publicKey == null)
161 _flags &= ~AssemblyNameFlags.PublicKey;
162 else
163 _flags |= AssemblyNameFlags.PublicKey;
166 // The compressed version of the public key formed from a truncated hash.
167 // Will throw a SecurityException if _publicKey is invalid
168 public byte[]? GetPublicKeyToken() => _publicKeyToken ??= ComputePublicKeyToken();
170 public void SetPublicKeyToken(byte[]? publicKeyToken)
172 _publicKeyToken = publicKeyToken;
175 // Flags modifying the name. So far the only flag is PublicKey, which
176 // indicates that a full public key and not the compressed version is
177 // present.
178 // Processor Architecture flags are set only through ProcessorArchitecture
179 // property and can't be set or retrieved directly
180 // Content Type flags are set only through ContentType property and can't be
181 // set or retrieved directly
182 public AssemblyNameFlags Flags
184 get => (AssemblyNameFlags)((uint)_flags & 0xFFFFF10F);
187 _flags &= unchecked((AssemblyNameFlags)0x00000EF0);
188 _flags |= (value & unchecked((AssemblyNameFlags)0xFFFFF10F));
192 public AssemblyHashAlgorithm HashAlgorithm
194 get => _hashAlgorithm;
195 set => _hashAlgorithm = value;
198 public AssemblyVersionCompatibility VersionCompatibility
200 get => _versionCompatibility;
201 set => _versionCompatibility = value;
204 public StrongNameKeyPair? KeyPair
206 get => _strongNameKeyPair;
207 set => _strongNameKeyPair = value;
210 public string FullName
214 if (this.Name == null)
215 return string.Empty;
216 // Do not call GetPublicKeyToken() here - that latches the result into AssemblyName which isn't a side effect we want.
217 byte[]? pkt = _publicKeyToken ?? ComputePublicKeyToken();
218 return AssemblyNameFormatter.ComputeDisplayName(Name, Version, CultureName, pkt, Flags, ContentType);
222 public override string ToString()
224 string s = FullName;
225 if (s == null)
226 return base.ToString()!;
227 else
228 return s;
231 public void GetObjectData(SerializationInfo info, StreamingContext context)
233 throw new PlatformNotSupportedException();
236 public void OnDeserialization(object? sender)
238 throw new PlatformNotSupportedException();
241 /// <summary>
242 /// Compares the simple names disregarding Version, Culture and PKT. While this clearly does not
243 /// match the intent of this api, this api has been broken this way since its debut and we cannot
244 /// change its behavior now.
245 /// </summary>
246 public static bool ReferenceMatchesDefinition(AssemblyName? reference, AssemblyName? definition)
248 if (object.ReferenceEquals(reference, definition))
249 return true;
251 if (reference == null)
252 throw new ArgumentNullException(nameof(reference));
254 if (definition == null)
255 throw new ArgumentNullException(nameof(definition));
257 string refName = reference.Name ?? string.Empty;
258 string defName = definition.Name ?? string.Empty;
259 return refName.Equals(defName, StringComparison.OrdinalIgnoreCase);
262 internal static string EscapeCodeBase(string? codebase)
264 if (codebase == null)
265 return string.Empty;
267 int position = 0;
268 char[]? dest = EscapeString(codebase, 0, codebase.Length, null, ref position, true, c_DummyChar, c_DummyChar, c_DummyChar);
269 if (dest == null)
270 return codebase;
272 return new string(dest, 0, position);
275 // This implementation of EscapeString has been copied from System.Private.Uri from corefx repo
276 // - forceX characters are always escaped if found
277 // - rsvd character will remain unescaped
279 // start - starting offset from input
280 // end - the exclusive ending offset in input
281 // destPos - starting offset in dest for output, on return this will be an exclusive "end" in the output.
283 // In case "dest" has lack of space it will be reallocated by preserving the _whole_ content up to current destPos
285 // Returns null if nothing has to be escaped AND passed dest was null, otherwise the resulting array with the updated destPos
287 internal static unsafe char[]? EscapeString(string input, int start, int end, char[]? dest, ref int destPos,
288 bool isUriString, char force1, char force2, char rsvd)
290 int i = start;
291 int prevInputPos = start;
292 byte* bytes = stackalloc byte[c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar]; // 40*4=160
294 fixed (char* pStr = input)
296 for (; i < end; ++i)
298 char ch = pStr[i];
300 // a Unicode ?
301 if (ch > '\x7F')
303 short maxSize = (short)Math.Min(end - i, (int)c_MaxUnicodeCharsReallocate - 1);
305 short count = 1;
306 for (; count < maxSize && pStr[i + count] > '\x7f'; ++count)
309 // Is the last a high surrogate?
310 if (pStr[i + count - 1] >= 0xD800 && pStr[i + count - 1] <= 0xDBFF)
312 // Should be a rare case where the app tries to feed an invalid Unicode surrogates pair
313 if (count == 1 || count == end - i)
314 throw new FormatException(SR.Arg_FormatException);
315 // need to grab one more char as a Surrogate except when it's a bogus input
316 ++count;
319 dest = EnsureDestinationSize(pStr, dest, i,
320 (short)(count * c_MaxUTF_8BytesPerUnicodeChar * c_EncodedCharsPerByte),
321 c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar * c_EncodedCharsPerByte,
322 ref destPos, prevInputPos);
324 short numberOfBytes = (short)Encoding.UTF8.GetBytes(pStr + i, count, bytes,
325 c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar);
327 // This is the only exception that built in UriParser can throw after a Uri ctor.
328 // Should not happen unless the app tries to feed an invalid Unicode string
329 if (numberOfBytes == 0)
330 throw new FormatException(SR.Arg_FormatException);
332 i += (count - 1);
334 for (count = 0; count < numberOfBytes; ++count)
335 EscapeAsciiChar((char)bytes[count], dest, ref destPos);
337 prevInputPos = i + 1;
339 else if (ch == '%' && rsvd == '%')
341 // Means we don't reEncode '%' but check for the possible escaped sequence
342 dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte,
343 c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos);
344 if (i + 2 < end && EscapedAscii(pStr[i + 1], pStr[i + 2]) != c_DummyChar)
346 // leave it escaped
347 dest[destPos++] = '%';
348 dest[destPos++] = pStr[i + 1];
349 dest[destPos++] = pStr[i + 2];
350 i += 2;
352 else
354 EscapeAsciiChar('%', dest, ref destPos);
356 prevInputPos = i + 1;
358 else if (ch == force1 || ch == force2)
360 dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte,
361 c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos);
362 EscapeAsciiChar(ch, dest, ref destPos);
363 prevInputPos = i + 1;
365 else if (ch != rsvd && (isUriString ? !IsReservedUnreservedOrHash(ch) : !IsUnreserved(ch)))
367 dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte,
368 c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos);
369 EscapeAsciiChar(ch, dest, ref destPos);
370 prevInputPos = i + 1;
374 if (prevInputPos != i)
376 // need to fill up the dest array ?
377 if (prevInputPos != start || dest != null)
378 dest = EnsureDestinationSize(pStr, dest, i, 0, 0, ref destPos, prevInputPos);
382 return dest;
386 // ensure destination array has enough space and contains all the needed input stuff
388 private static unsafe char[] EnsureDestinationSize(char* pStr, char[]? dest, int currentInputPos,
389 short charsToAdd, short minReallocateChars, ref int destPos, int prevInputPos)
391 if (dest is null || dest.Length < destPos + (currentInputPos - prevInputPos) + charsToAdd)
393 // allocating or reallocating array by ensuring enough space based on maxCharsToAdd.
394 char[] newresult = new char[destPos + (currentInputPos - prevInputPos) + minReallocateChars];
396 if (!(dest is null) && destPos != 0)
397 Buffer.BlockCopy(dest, 0, newresult, 0, destPos << 1);
398 dest = newresult;
401 // ensuring we copied everything form the input string left before last escaping
402 while (prevInputPos != currentInputPos)
403 dest[destPos++] = pStr[prevInputPos++];
404 return dest;
407 internal static void EscapeAsciiChar(char ch, char[] to, ref int pos)
409 to[pos++] = '%';
410 to[pos++] = s_hexUpperChars[(ch & 0xf0) >> 4];
411 to[pos++] = s_hexUpperChars[ch & 0xf];
414 internal static char EscapedAscii(char digit, char next)
416 if (!(((digit >= '0') && (digit <= '9'))
417 || ((digit >= 'A') && (digit <= 'F'))
418 || ((digit >= 'a') && (digit <= 'f'))))
420 return c_DummyChar;
423 int res = (digit <= '9')
424 ? ((int)digit - (int)'0')
425 : (((digit <= 'F')
426 ? ((int)digit - (int)'A')
427 : ((int)digit - (int)'a'))
428 + 10);
430 if (!(((next >= '0') && (next <= '9'))
431 || ((next >= 'A') && (next <= 'F'))
432 || ((next >= 'a') && (next <= 'f'))))
434 return c_DummyChar;
437 return (char)((res << 4) + ((next <= '9')
438 ? ((int)next - (int)'0')
439 : (((next <= 'F')
440 ? ((int)next - (int)'A')
441 : ((int)next - (int)'a'))
442 + 10)));
445 private static bool IsReservedUnreservedOrHash(char c)
447 if (IsUnreserved(c))
449 return true;
451 return RFC3986ReservedMarks.Contains(c);
454 internal static bool IsUnreserved(char c)
456 if (IsAsciiLetterOrDigit(c))
458 return true;
460 return RFC3986UnreservedMarks.Contains(c);
463 // Only consider ASCII characters
464 internal static bool IsAsciiLetter(char character)
466 return (character >= 'a' && character <= 'z') ||
467 (character >= 'A' && character <= 'Z');
470 internal static bool IsAsciiLetterOrDigit(char character)
472 return IsAsciiLetter(character) || (character >= '0' && character <= '9');
475 private static readonly char[] s_hexUpperChars = {
476 '0', '1', '2', '3', '4', '5', '6', '7',
477 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
478 internal const char c_DummyChar = (char)0xFFFF; // An Invalid Unicode character used as a dummy char passed into the parameter
479 private const short c_MaxAsciiCharsReallocate = 40;
480 private const short c_MaxUnicodeCharsReallocate = 40;
481 private const short c_MaxUTF_8BytesPerUnicodeChar = 4;
482 private const short c_EncodedCharsPerByte = 3;
483 private const string RFC3986ReservedMarks = ":/?#[]@!$&'()*+,;=";
484 private const string RFC3986UnreservedMarks = "-._~";