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
;
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
;
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
44 public Version
? Version
47 set => _version
= value;
50 // Locales, internally the LCID is used for the match.
51 public CultureInfo
? 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
66 set => _codeBase
= value;
69 public string? EscapedCodeBase
73 if (_codeBase
== null)
76 return EscapeCodeBase(_codeBase
);
80 public ProcessorArchitecture ProcessorArchitecture
84 int x
= (((int)_flags
) & 0x70) >> 4;
87 return (ProcessorArchitecture
)x
;
91 int x
= ((int)value) & 0x07;
94 _flags
= (AssemblyNameFlags
)((int)_flags
& 0xFFFFFF0F);
95 _flags
|= (AssemblyNameFlags
)(x
<< 4);
100 public AssemblyContentType ContentType
104 int x
= (((int)_flags
) & 0x00000E00) >> 9;
107 return (AssemblyContentType
)x
;
111 int x
= ((int)value) & 0x07;
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
126 _publicKey
= (byte[]?)_publicKey
?.Clone(),
127 _publicKeyToken
= (byte[]?)_publicKeyToken
?.Clone(),
128 _cultureInfo
= _cultureInfo
,
129 _version
= (Version
?)_version
?.Clone(),
131 _codeBase
= _codeBase
,
132 _hashAlgorithm
= _hashAlgorithm
,
133 _versionCompatibility
= _versionCompatibility
,
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()
156 public void SetPublicKey(byte[]? publicKey
)
158 _publicKey
= publicKey
;
160 if (publicKey
== null)
161 _flags
&= ~AssemblyNameFlags
.PublicKey
;
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
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)
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()
226 return base.ToString()!;
231 public void GetObjectData(SerializationInfo info
, StreamingContext context
)
233 throw new PlatformNotSupportedException();
236 public void OnDeserialization(object? sender
)
238 throw new PlatformNotSupportedException();
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.
246 public static bool ReferenceMatchesDefinition(AssemblyName
? reference
, AssemblyName
? definition
)
248 if (object.ReferenceEquals(reference
, definition
))
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)
268 char[]? dest
= EscapeString(codebase
, 0, codebase
.Length
, null, ref position
, true, c_DummyChar
, c_DummyChar
, c_DummyChar
);
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
)
291 int prevInputPos
= start
;
292 byte* bytes
= stackalloc byte[c_MaxUnicodeCharsReallocate
* c_MaxUTF_8BytesPerUnicodeChar
]; // 40*4=160
294 fixed (char* pStr
= input
)
303 short maxSize
= (short)Math
.Min(end
- i
, (int)c_MaxUnicodeCharsReallocate
- 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
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
);
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
)
347 dest
[destPos
++] = '%';
348 dest
[destPos
++] = pStr
[i
+ 1];
349 dest
[destPos
++] = pStr
[i
+ 2];
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
);
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);
401 // ensuring we copied everything form the input string left before last escaping
402 while (prevInputPos
!= currentInputPos
)
403 dest
[destPos
++] = pStr
[prevInputPos
++];
407 internal static void EscapeAsciiChar(char ch
, char[] to
, ref int 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'))))
423 int res
= (digit
<= '9')
424 ? ((int)digit
- (int)'0')
426 ? ((int)digit
- (int)'A')
427 : ((int)digit
- (int)'a'))
430 if (!(((next
>= '0') && (next
<= '9'))
431 || ((next
>= 'A') && (next
<= 'F'))
432 || ((next
>= 'a') && (next
<= 'f'))))
437 return (char)((res
<< 4) + ((next
<= '9')
438 ? ((int)next
- (int)'0')
440 ? ((int)next
- (int)'A')
441 : ((int)next
- (int)'a'))
445 private static bool IsReservedUnreservedOrHash(char c
)
451 return RFC3986ReservedMarks
.Contains(c
);
454 internal static bool IsUnreserved(char c
)
456 if (IsAsciiLetterOrDigit(c
))
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
= "-._~";