diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs
index 55544a6f8ecc8..1a49aecab6d7f 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs
@@ -85,7 +85,7 @@ internal partial struct TypeNameParser
{
return null;
}
- else if (topLevelAssembly is not null && parsed.GetAssemblyName() is not null)
+ else if (topLevelAssembly is not null && parsed.AssemblyName is not null)
{
return throwOnError ? throw new ArgumentException(SR.Argument_AssemblyGetTypeCannotSpecifyAssembly) : null;
}
diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj
index 6091f03b4acbc..0a69514553758 100644
--- a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj
@@ -201,9 +201,18 @@
Utilities\HexConverter.cs
+
+ Utilities\AssemblyNameHelpers.StrongName.cs
+
+
+ Utilities\AssemblyNameFormatter.cs
+
Utilities\AssemblyNameParser.cs
+
+ Utilities\AssemblyNameInfo.cs
+
Utilities\TypeName.cs
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameFormatter.cs b/src/libraries/Common/src/System/Reflection/AssemblyNameFormatter.cs
similarity index 92%
rename from src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameFormatter.cs
rename to src/libraries/Common/src/System/Reflection/AssemblyNameFormatter.cs
index c1a810db50755..9d5eeebc85a6d 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameFormatter.cs
+++ b/src/libraries/Common/src/System/Reflection/AssemblyNameFormatter.cs
@@ -6,6 +6,8 @@
using System.Globalization;
using System.Text;
+#nullable enable
+
namespace System.Reflection
{
internal static class AssemblyNameFormatter
@@ -91,7 +93,8 @@ private static void AppendQuoted(this ref ValueStringBuilder vsb, string s)
// App-compat: You can use double or single quotes to quote a name, and Fusion (or rather the IdentityAuthority) picks one
// by some algorithm. Rather than guess at it, we use double quotes consistently.
- if (s != s.Trim() || s.Contains('\"') || s.Contains('\''))
+ ReadOnlySpan span = s.AsSpan();
+ if (s.Length != span.Trim().Length || span.IndexOfAny('\"', '\'') >= 0)
needsQuoting = true;
if (needsQuoting)
@@ -125,5 +128,12 @@ private static void AppendQuoted(this ref ValueStringBuilder vsb, string s)
if (needsQuoting)
vsb.Append(quoteChar);
}
+
+#if !NETCOREAPP
+ private static void AppendSpanFormattable(this ref ValueStringBuilder vsb, ushort value)
+ {
+ vsb.Append(value.ToString());
+ }
+#endif
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.StrongName.cs b/src/libraries/Common/src/System/Reflection/AssemblyNameHelpers.StrongName.cs
similarity index 95%
rename from src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.StrongName.cs
rename to src/libraries/Common/src/System/Reflection/AssemblyNameHelpers.StrongName.cs
index 7fb7a66815956..34413f7a43f23 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.StrongName.cs
+++ b/src/libraries/Common/src/System/Reflection/AssemblyNameHelpers.StrongName.cs
@@ -2,8 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Buffers.Binary;
+using System.Diagnostics;
using System.Security;
+#nullable enable
+
namespace System.Reflection
{
internal static partial class AssemblyNameHelpers
@@ -16,8 +19,12 @@ internal static partial class AssemblyNameHelpers
if (publicKey.Length == 0)
return Array.Empty();
+#if SYSTEM_PRIVATE_CORELIB
if (!IsValidPublicKey(publicKey))
throw new SecurityException(SR.Security_InvalidAssemblyPublicKey);
+#else
+ Debug.Assert(IsValidPublicKey(publicKey));
+#endif
Span hash = stackalloc byte[20];
@@ -35,7 +42,7 @@ internal static partial class AssemblyNameHelpers
//
// This validation logic is a port of StrongNameIsValidPublicKey() from src\coreclr\md\runtime\strongnameinternal.cpp
//
- private static bool IsValidPublicKey(byte[] publicKey)
+ internal static bool IsValidPublicKey(byte[] publicKey)
{
uint publicKeyLength = (uint)(publicKey.Length);
diff --git a/src/libraries/Common/src/System/Reflection/Metadata/AssemblyNameInfo.cs b/src/libraries/Common/src/System/Reflection/Metadata/AssemblyNameInfo.cs
new file mode 100644
index 0000000000000..fa1a2eeeb6336
--- /dev/null
+++ b/src/libraries/Common/src/System/Reflection/Metadata/AssemblyNameInfo.cs
@@ -0,0 +1,276 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Text;
+
+namespace System.Reflection.Metadata
+{
+ [DebuggerDisplay("{FullName}")]
+#if SYSTEM_PRIVATE_CORELIB
+ internal
+#else
+ public
+#endif
+ sealed class AssemblyNameInfo : IEquatable
+ {
+ private string? _fullName;
+
+#if !SYSTEM_PRIVATE_CORELIB
+ public AssemblyNameInfo(string name, Version? version = null, string? cultureName = null, AssemblyNameFlags flags = AssemblyNameFlags.None,
+ Collections.Immutable.ImmutableArray publicKey = default, Collections.Immutable.ImmutableArray publicKeyToken = default)
+ {
+ Name = name ?? throw new ArgumentNullException(nameof(name));
+ Version = version;
+ CultureName = cultureName;
+
+ if (!publicKey.IsDefaultOrEmpty
+#if NET8_0_OR_GREATER
+ && ValidatePublicKey(Runtime.InteropServices.ImmutableCollectionsMarshal.AsArray(publicKey)))
+#else
+ && ValidatePublicKey(System.Linq.ImmutableArrayExtensions.ToArray(publicKey)))
+#endif
+ {
+ throw new ArgumentException("SR.Security_InvalidAssemblyPublicKey", nameof(publicKey)); // TODO adsitnik: use actual resource
+ }
+
+ PublicKey = publicKey;
+ PublicKeyToken = publicKeyToken;
+
+ if (!publicKey.IsDefaultOrEmpty)
+ {
+ flags |= AssemblyNameFlags.PublicKey;
+ }
+
+ Flags = flags;
+ }
+#endif
+
+ internal AssemblyNameInfo(AssemblyNameParser.AssemblyNameParts parts)
+ {
+ Name = parts._name;
+ Version = parts._version;
+ CultureName = parts._cultureName;
+ Flags = parts._flags;
+
+ bool publicKey = (parts._flags & AssemblyNameFlags.PublicKey) != 0;
+
+#if SYSTEM_PRIVATE_CORELIB
+ PublicKey = publicKey ? parts._publicKeyOrToken : null;
+ PublicKeyToken = publicKey ? null : parts._publicKeyOrToken;
+#else
+ PublicKey = ToImmutable(publicKey ? parts._publicKeyOrToken : null);
+ PublicKeyToken = ToImmutable(publicKey ? null : parts._publicKeyOrToken);
+
+ static Collections.Immutable.ImmutableArray ToImmutable(byte[]? bytes)
+ => bytes is null ? default : bytes.Length == 0 ? Collections.Immutable.ImmutableArray.Empty :
+ #if NET8_0_OR_GREATER
+ Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray(bytes);
+ #else
+ Collections.Immutable.ImmutableArray.Create(bytes);
+ #endif
+#endif
+ }
+
+ public string Name { get; }
+ public Version? Version { get; }
+ public string? CultureName { get; }
+ public AssemblyNameFlags Flags { get; }
+
+#if SYSTEM_PRIVATE_CORELIB
+ public byte[]? PublicKey { get; }
+ public byte[]? PublicKeyToken { get; }
+#else
+ public Collections.Immutable.ImmutableArray PublicKey { get; }
+ public Collections.Immutable.ImmutableArray PublicKeyToken { get; }
+#endif
+
+ public string FullName
+ {
+ get
+ {
+ if (_fullName is null)
+ {
+#if SYSTEM_PRIVATE_CORELIB
+ byte[]? pkt = PublicKeyToken ?? AssemblyNameHelpers.ComputePublicKeyToken(PublicKey);
+#elif NET8_0_OR_GREATER
+ byte[]? pkt = !PublicKeyToken.IsDefault
+ ? Runtime.InteropServices.ImmutableCollectionsMarshal.AsArray(PublicKeyToken)
+ : !PublicKey.IsDefault
+ ? AssemblyNameHelpers.ComputePublicKeyToken(Runtime.InteropServices.ImmutableCollectionsMarshal.AsArray(PublicKey))
+ : null;
+#else
+ byte[]? pkt = !PublicKeyToken.IsDefault
+ ? System.Linq.ImmutableArrayExtensions.ToArray(PublicKeyToken)
+ : !PublicKey.IsDefault
+ ? AssemblyNameHelpers.ComputePublicKeyToken(System.Linq.ImmutableArrayExtensions.ToArray(PublicKey))
+ : null;
+#endif
+ _fullName = AssemblyNameFormatter.ComputeDisplayName(Name, Version, CultureName, pkt/*, ExtractAssemblyNameFlags(Flags), ExtractAssemblyContentType(Flags)*/); ;
+ }
+
+ return _fullName;
+ }
+ }
+
+ public bool Equals(AssemblyNameInfo? other)
+ {
+ if (other is null || Flags != other.Flags || !Name.Equals(other.Name) || !string.Equals(CultureName, other.CultureName))
+ {
+ return false;
+ }
+
+ if (Version is null)
+ {
+ if (other.Version is not null)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!Version.Equals(other.Version))
+ {
+ return false;
+ }
+ }
+
+ if (!SequenceEqual(PublicKey, other.PublicKey) || !SequenceEqual(PublicKeyToken, other.PublicKeyToken))
+ {
+ return false;
+ }
+
+ return true;
+
+#if SYSTEM_PRIVATE_CORELIB
+ static bool SequenceEqual(byte[]? left, byte[]? right)
+ {
+ if (left is null)
+ {
+ if (right is not null)
+ {
+ return false;
+ }
+ }
+ else if (right is null)
+ {
+ return false;
+ }
+ else if (left.Length != right.Length)
+ {
+ return false;
+ }
+ else
+ {
+ for (int i = 0; i < left.Length; i++)
+ {
+ if (left[i] != right[i])
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+#else
+ static bool SequenceEqual(Collections.Immutable.ImmutableArray left, Collections.Immutable.ImmutableArray right)
+ {
+ int leftLength = left.IsDefaultOrEmpty ? 0 : left.Length;
+ int rightLength = right.IsDefaultOrEmpty ? 0 : right.Length;
+
+ if (leftLength != rightLength)
+ {
+ return false;
+ }
+ else if (leftLength > 0)
+ {
+ for (int i = 0; i < leftLength; i++)
+ {
+ if (left[i] != right[i])
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+#endif
+ }
+
+ public override bool Equals(object? obj) => Equals(obj as AssemblyNameInfo);
+
+ public override int GetHashCode() => FullName.GetHashCode();
+
+ public AssemblyName ToAssemblyName()
+ {
+ AssemblyName assemblyName = new();
+ assemblyName.Name = Name;
+ assemblyName.CultureName = CultureName;
+ assemblyName.Version = Version;
+
+#if SYSTEM_PRIVATE_CORELIB
+ assemblyName._flags = Flags;
+ assemblyName.SetPublicKey(PublicKey);
+ assemblyName.SetPublicKeyToken(PublicKeyToken);
+#else
+ assemblyName.Flags = Flags;
+
+ if (!PublicKey.IsDefault)
+ {
+ assemblyName.SetPublicKey(System.Linq.ImmutableArrayExtensions.ToArray(PublicKey));
+ }
+ if (!PublicKeyToken.IsDefault)
+ {
+ assemblyName.SetPublicKeyToken(System.Linq.ImmutableArrayExtensions.ToArray(PublicKeyToken));
+ }
+#endif
+
+ return assemblyName;
+ }
+
+ ///
+ /// Parses a span of characters into a assembly name.
+ ///
+ /// A span containing the characters representing the assembly name to parse.
+ /// Parsed type name.
+ /// Provided assembly name was invalid.
+ public static AssemblyNameInfo Parse(ReadOnlySpan assemblyName)
+ => TryParse(assemblyName, out AssemblyNameInfo? result)
+ ? result
+ : throw new ArgumentException("TODO_adsitnik_add_or_reuse_resource");
+
+ ///
+ /// Tries to parse a span of characters into an assembly name.
+ ///
+ /// A span containing the characters representing the assembly name to parse.
+ /// Contains the result when parsing succeeds.
+ /// true if assembly name was converted successfully, otherwise, false.
+ public static bool TryParse(ReadOnlySpan assemblyName,
+#if SYSTEM_REFLECTION_METADATA || SYSTEM_PRIVATE_CORELIB // required by some tools that include this file but don't include the attribute
+ [NotNullWhen(true)]
+#endif
+ out AssemblyNameInfo? result)
+ {
+ AssemblyNameParser.AssemblyNameParts parts = default;
+ if (AssemblyNameParser.TryParse(assemblyName, ref parts)
+ && ((parts._flags & AssemblyNameFlags.PublicKey) == 0 || ValidatePublicKey(parts._publicKeyOrToken)))
+ {
+ result = new(parts);
+ return true;
+ }
+
+ result = null;
+ return false;
+ }
+
+ private static bool ValidatePublicKey(byte[]? publicKey)
+ => publicKey is null
+ || publicKey.Length == 0
+ || AssemblyNameHelpers.IsValidPublicKey(publicKey);
+ }
+}
diff --git a/src/libraries/Common/src/System/Reflection/Metadata/TypeName.cs b/src/libraries/Common/src/System/Reflection/Metadata/TypeName.cs
index 53d46071523fe..3be55a528b37e 100644
--- a/src/libraries/Common/src/System/Reflection/Metadata/TypeName.cs
+++ b/src/libraries/Common/src/System/Reflection/Metadata/TypeName.cs
@@ -29,13 +29,12 @@ sealed class TypeName : IEquatable
///
private readonly int _nestedNameLength;
private readonly TypeName[]? _genericArguments;
- private readonly AssemblyName? _assemblyName;
private readonly TypeName? _elementOrGenericType;
private readonly TypeName? _declaringType;
private string? _name, _fullName, _assemblyQualifiedName;
internal TypeName(string? fullName,
- AssemblyName? assemblyName,
+ AssemblyNameInfo? assemblyName,
TypeName? elementOrGenericType = default,
TypeName? declaringType = default,
TypeName[]? genericTypeArguments = default,
@@ -43,7 +42,7 @@ internal TypeName(string? fullName,
int nestedNameLength = -1)
{
_fullName = fullName;
- _assemblyName = assemblyName;
+ AssemblyName = assemblyName;
_rankOrModifier = rankOrModifier;
_elementOrGenericType = elementOrGenericType;
_declaringType = declaringType;
@@ -58,18 +57,16 @@ internal TypeName(string? fullName,
/// The assembly-qualified name of the type; e.g., "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089".
///
///
- /// If returns null, simply returns .
+ /// If returns null, simply returns .
///
public string AssemblyQualifiedName
- => _assemblyQualifiedName ??= _assemblyName is null ? FullName : $"{FullName}, {_assemblyName.FullName}";
+ => _assemblyQualifiedName ??= AssemblyName is null ? FullName : $"{FullName}, {AssemblyName.FullName}";
///
- /// Returns the name of the assembly (not the full name).
+ /// Returns assembly name which contains this type, or null if this was not
+ /// created from a fully-qualified name.
///
- ///
- /// If returns null, simply returns null.
- ///
- public string? AssemblySimpleName => _assemblyName?.Name;
+ public AssemblyNameInfo? AssemblyName { get; }
///
/// If this type is a nested type (see ), gets
@@ -224,8 +221,8 @@ public bool Equals(TypeName? other)
=> other is not null
&& other._rankOrModifier == _rankOrModifier
// try to prevent from allocations if possible (AssemblyQualifiedName can allocate)
- && ((other._assemblyName is null && _assemblyName is null)
- || (other._assemblyName is not null && _assemblyName is not null))
+ && ((other.AssemblyName is null && AssemblyName is null)
+ || (other.AssemblyName is not null && AssemblyName is not null))
&& other.AssemblyQualifiedName == AssemblyQualifiedName;
public override bool Equals(object? obj) => Equals(obj as TypeName);
@@ -353,25 +350,6 @@ public int GetArrayRank()
_ => throw TypeNameParserHelpers.InvalidOperation_HasToBeArrayClass()
};
- ///
- /// Returns assembly name which contains this type, or null if this was not
- /// created from a fully-qualified name.
- ///
- /// Since is mutable, this method returns a copy of it.
- public AssemblyName? GetAssemblyName()
- {
- if (_assemblyName is null)
- {
- return null;
- }
-
-#if SYSTEM_PRIVATE_CORELIB
- return _assemblyName; // no need for a copy in CoreLib (it's internal)
-#else
- return (AssemblyName)_assemblyName.Clone();
-#endif
- }
-
///
/// If this represents a constructed generic type, returns an array
/// of all the generic arguments. Otherwise it returns an empty array.
diff --git a/src/libraries/Common/src/System/Reflection/Metadata/TypeNameParser.cs b/src/libraries/Common/src/System/Reflection/Metadata/TypeNameParser.cs
index 602a78255ffd3..84a6147e154ce 100644
--- a/src/libraries/Common/src/System/Reflection/Metadata/TypeNameParser.cs
+++ b/src/libraries/Common/src/System/Reflection/Metadata/TypeNameParser.cs
@@ -193,7 +193,7 @@ private TypeNameParser(ReadOnlySpan name, bool throwOnError, TypeNameParse
previousDecorator = parsedDecorator;
}
- AssemblyName? assemblyName = null;
+ AssemblyNameInfo? assemblyName = null;
if (allowFullyQualifiedName && !TryParseAssemblyName(ref assemblyName))
{
#if SYSTEM_PRIVATE_CORELIB
@@ -232,7 +232,7 @@ private TypeNameParser(ReadOnlySpan name, bool throwOnError, TypeNameParse
}
/// false means the input was invalid and parsing has failed. Empty input is valid and returns true.
- private bool TryParseAssemblyName(ref AssemblyName? assemblyName)
+ private bool TryParseAssemblyName(ref AssemblyNameInfo? assemblyName)
{
ReadOnlySpan capturedBeforeProcessing = _inputString;
if (TryStripFirstCharAndTrailingSpaces(ref _inputString, ','))
@@ -247,33 +247,12 @@ private bool TryParseAssemblyName(ref AssemblyName? assemblyName)
// Otherwise EOL serves as the terminator.
int assemblyNameLength = (int)Math.Min((uint)_inputString.IndexOf(']'), (uint)_inputString.Length);
ReadOnlySpan candidate = _inputString.Slice(0, assemblyNameLength);
- AssemblyNameParser.AssemblyNameParts parts = default;
- if (!AssemblyNameParser.TryParse(candidate, ref parts))
+ if (!AssemblyNameInfo.TryParse(candidate, out assemblyName))
{
return false;
}
- assemblyName = new AssemblyName();
-#if SYSTEM_PRIVATE_CORELIB
- assemblyName.Init(parts);
-#else
- assemblyName.Name = parts._name;
- assemblyName.CultureName = parts._cultureName;
- assemblyName.Version = parts._version;
-
- if (parts._publicKeyOrToken is not null)
- {
- if ((parts._flags & AssemblyNameFlags.PublicKey) != 0)
- {
- assemblyName.SetPublicKey(parts._publicKeyOrToken);
- }
- else
- {
- assemblyName.SetPublicKeyToken(parts._publicKeyOrToken);
- }
- }
-#endif
_inputString = _inputString.Slice(assemblyNameLength);
return true;
}
@@ -281,7 +260,7 @@ private bool TryParseAssemblyName(ref AssemblyName? assemblyName)
return true;
}
- private static TypeName? GetDeclaringType(string fullTypeName, List? nestedNameLengths, AssemblyName? assemblyName)
+ private static TypeName? GetDeclaringType(string fullTypeName, List? nestedNameLengths, AssemblyNameInfo? assemblyName)
{
if (nestedNameLengths is null)
{
diff --git a/src/libraries/Common/src/System/Reflection/TypeNameParser.Helpers.cs b/src/libraries/Common/src/System/Reflection/TypeNameParser.Helpers.cs
index be5a784a40f75..6fae810b6bd0e 100644
--- a/src/libraries/Common/src/System/Reflection/TypeNameParser.Helpers.cs
+++ b/src/libraries/Common/src/System/Reflection/TypeNameParser.Helpers.cs
@@ -128,7 +128,7 @@ private static (string typeNamespace, string name) SplitFullTypeName(string type
}
string nonNestedParentName = current!.FullName;
- Type? type = GetType(nonNestedParentName, nestedTypeNames, typeName.GetAssemblyName(), typeName.FullName);
+ Type? type = GetType(nonNestedParentName, nestedTypeNames, typeName.AssemblyName?.ToAssemblyName(), typeName.FullName);
return Make(type, typeName);
}
else if (typeName.IsConstructedGenericType)
@@ -141,7 +141,7 @@ private static (string typeNamespace, string name) SplitFullTypeName(string type
}
else
{
- Type? type = GetType(typeName.FullName, nestedTypeNames: ReadOnlySpan.Empty, typeName.GetAssemblyName(), typeName.FullName);
+ Type? type = GetType(typeName.FullName, nestedTypeNames: ReadOnlySpan.Empty, typeName.AssemblyName?.ToAssemblyName(), typeName.FullName);
return Make(type, typeName);
}
diff --git a/src/libraries/System.Collections.Immutable/src/Resources/Strings.resx b/src/libraries/System.Collections.Immutable/src/Resources/Strings.resx
index 5abd00a70bd3b..e76cc9b3aa577 100644
--- a/src/libraries/System.Collections.Immutable/src/Resources/Strings.resx
+++ b/src/libraries/System.Collections.Immutable/src/Resources/Strings.resx
@@ -105,4 +105,7 @@
Non-negative number required.
-
+
+ Invalid assembly public key.
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index c534b2f299207..a2af87f01dd26 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -647,9 +647,7 @@
-
-
@@ -1477,6 +1475,15 @@
Common\System\Reflection\AssemblyNameParser.cs
+
+ Common\System\Reflection\AssemblyNameFormatter.cs
+
+
+ Common\System\Reflection\AssemblyNameHelpers.StrongName.cs
+
+
+ Common\System\Reflection\Metadata\AssemblyNameInfo.cs
+
Common\System\Reflection\Metadata\TypeName.cs
diff --git a/src/libraries/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs b/src/libraries/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs
index 2cc2644e2c8ae..7908892726241 100644
--- a/src/libraries/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs
+++ b/src/libraries/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs
@@ -2408,6 +2408,24 @@ public readonly partial struct TypeLayout
public int PackingSize { get { throw null; } }
public int Size { get { throw null; } }
}
+ public sealed partial class AssemblyNameInfo : System.IEquatable
+ {
+ public AssemblyNameInfo(string name, System.Version? version = null, string? cultureName = null, System.Reflection.AssemblyNameFlags flags = AssemblyNameFlags.None,
+ Collections.Immutable.ImmutableArray publicKey = default, Collections.Immutable.ImmutableArray publicKeyToken = default) { }
+ public string Name { get { throw null; } }
+ public string? CultureName { get { throw null; } }
+ public string FullName { get { throw null; } }
+ public System.Version? Version { get { throw null; } }
+ public System.Reflection.AssemblyNameFlags Flags { get { throw null; } }
+ public System.Collections.Immutable.ImmutableArray PublicKey { get { throw null; } }
+ public System.Collections.Immutable.ImmutableArray PublicKeyToken { get { throw null; } }
+ public static System.Reflection.Metadata.AssemblyNameInfo Parse(System.ReadOnlySpan assemblyName) { throw null; }
+ public static bool TryParse(System.ReadOnlySpan assemblyName, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Reflection.Metadata.AssemblyNameInfo? result) { throw null; }
+ public override bool Equals(object? obj) { throw null; }
+ public bool Equals(System.Reflection.Metadata.AssemblyNameInfo? other) { throw null; }
+ public override int GetHashCode() { throw null; }
+ public System.Reflection.AssemblyName ToAssemblyName() { throw null; }
+ }
public sealed partial class TypeName : System.IEquatable
{
internal TypeName() { }
diff --git a/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj b/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj
index 7c683a07db249..44998d8c76983 100644
--- a/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj
+++ b/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj
@@ -253,12 +253,17 @@ The System.Reflection.Metadata library is built-in as part of the shared framewo
+
+
+
+
+
diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/AssemblyNameInfoTests.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/AssemblyNameInfoTests.cs
new file mode 100644
index 0000000000000..0bf63a12a2ac5
--- /dev/null
+++ b/src/libraries/System.Reflection.Metadata/tests/Metadata/AssemblyNameInfoTests.cs
@@ -0,0 +1,55 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.Reflection.Metadata.Tests.Metadata
+{
+ public class AssemblyNameInfoTests
+ {
+ [Theory]
+ [InlineData("MyAssemblyName, Version=1.0.0.0, PublicKeyToken=b77a5c561934e089", "MyAssemblyName, Version=1.0.0.0, PublicKeyToken=b77a5c561934e089")]
+ [InlineData("MyAssemblyName, Version=1.0.0.0, PublicKey=00000000000000000400000000000000", "MyAssemblyName, Version=1.0.0.0, PublicKeyToken=b77a5c561934e089")]
+ [InlineData("TerraFX.Interop.Windows, PublicKey=" +
+ "002400000c800000940000000602000000240000525341310004000001000100897039f5ff762b25b9ba982c3f5836c34e299279c33df505bf806a07bccdf0e1216e661943f557b954cb18422ed522a5" +
+ "b3174b85385052677f39c4ce19f30a1ddbaa507054bc5943461651f396afc612cd80419c5ee2b5277571ff65f51d14ba99e4e4196de0f393e89850a465f019dbdc365ed5e81bbafe1370f54efd254ba8",
+ "TerraFX.Interop.Windows, PublicKeyToken=35b01b53313a6f7e")]
+ public void WithPublicKeyOrToken(string name, string expectedName)
+ {
+ AssemblyName assemblyName = new AssemblyName(name);
+
+ AssemblyNameInfo assemblyNameInfo = AssemblyNameInfo.Parse(name.AsSpan());
+
+ Assert.Equal(expectedName, assemblyName.FullName);
+ Assert.Equal(expectedName, assemblyNameInfo.FullName);
+
+ Roundtrip(assemblyName);
+ }
+
+ [Fact]
+ public void NoPublicKeyOrToken()
+ {
+ AssemblyName source = new AssemblyName();
+ source.Name = "test";
+ source.Version = new Version(1, 2, 3, 4);
+ source.CultureName = "en-US";
+
+ Roundtrip(source);
+ }
+
+ static void Roundtrip(AssemblyName source)
+ {
+ AssemblyNameInfo parsed = AssemblyNameInfo.Parse(source.FullName.AsSpan());
+ Assert.Equal(source.Name, parsed.Name);
+ Assert.Equal(source.Version, parsed.Version);
+ Assert.Equal(source.CultureName, parsed.CultureName);
+ Assert.Equal(source.FullName, parsed.FullName);
+
+ AssemblyName fromParsed = parsed.ToAssemblyName();
+ Assert.Equal(source.Name, fromParsed.Name);
+ Assert.Equal(source.Version, fromParsed.Version);
+ Assert.Equal(source.CultureName, fromParsed.CultureName);
+ Assert.Equal(source.FullName, fromParsed.FullName);
+ }
+ }
+}
diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameParserSamples.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameParserSamples.cs
index 3176d6e8196f0..0c48cdc2641d9 100644
--- a/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameParserSamples.cs
+++ b/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameParserSamples.cs
@@ -70,7 +70,7 @@ public SampleSerializationBinder(Type[]? allowedTypes = null)
throw new InvalidOperationException($"Invalid type name: '{typeName}'");
}
- if (parsed.GetAssemblyName() is not null)
+ if (parsed.AssemblyName is not null)
{
// The attackers may create such a payload,
// where "typeName" passed to BindToType contains the assembly name
diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameTests.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameTests.cs
index bbd8344367e01..14d0037cfed04 100644
--- a/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameTests.cs
+++ b/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameTests.cs
@@ -113,19 +113,16 @@ static void Verify(Type type, AssemblyName expectedAssemblyName, TypeName parsed
Assert.Equal(type.FullName, parsed.FullName);
Assert.Equal(type.Name, parsed.Name);
- AssemblyName parsedAssemblyName = parsed.GetAssemblyName();
+ AssemblyNameInfo parsedAssemblyName = parsed.AssemblyName;
Assert.NotNull(parsedAssemblyName);
Assert.Equal(expectedAssemblyName.Name, parsedAssemblyName.Name);
- Assert.Equal(expectedAssemblyName.Name, parsed.AssemblySimpleName);
Assert.Equal(expectedAssemblyName.Version, parsedAssemblyName.Version);
Assert.Equal(expectedAssemblyName.CultureName, parsedAssemblyName.CultureName);
- Assert.Equal(expectedAssemblyName.GetPublicKeyToken(), parsedAssemblyName.GetPublicKeyToken());
+ Assert.Equal(expectedAssemblyName.GetPublicKeyToken(), parsedAssemblyName.PublicKeyToken.ToArray());
Assert.Equal(expectedAssemblyName.FullName, parsedAssemblyName.FullName);
- Assert.Equal(default, parsedAssemblyName.ContentType);
Assert.Equal(default, parsedAssemblyName.Flags);
- Assert.Equal(default, parsedAssemblyName.ProcessorArchitecture);
}
}
@@ -375,12 +372,12 @@ public void GenericArgumentsAreSupported(string input, string name, string fullN
{
if (assemblyNames[i] is null)
{
- Assert.Null(genericArg.GetAssemblyName());
+ Assert.Null(genericArg.AssemblyName);
}
else
{
- Assert.Equal(assemblyNames[i].FullName, genericArg.GetAssemblyName().FullName);
- Assert.Equal(assemblyNames[i].Name, genericArg.AssemblySimpleName);
+ Assert.Equal(assemblyNames[i].FullName, genericArg.AssemblyName.FullName);
+ Assert.Equal(assemblyNames[i].Name, genericArg.AssemblyName.Name);
}
}
}
@@ -691,7 +688,7 @@ static void Verify(Type type, TypeName typeName, bool ignoreCase)
{
Assert.True(typeName.IsSimple);
- AssemblyName? assemblyName = typeName.GetAssemblyName();
+ AssemblyName? assemblyName = typeName.AssemblyName.ToAssemblyName();
Type? type = assemblyName is null
? Type.GetType(typeName.FullName, throwOnError, ignoreCase)
: Assembly.Load(assemblyName).GetType(typeName.FullName, throwOnError, ignoreCase);
diff --git a/src/libraries/System.Reflection.Metadata/tests/System.Reflection.Metadata.Tests.csproj b/src/libraries/System.Reflection.Metadata/tests/System.Reflection.Metadata.Tests.csproj
index 134c82d238b6a..f739ac0789ff8 100644
--- a/src/libraries/System.Reflection.Metadata/tests/System.Reflection.Metadata.Tests.csproj
+++ b/src/libraries/System.Reflection.Metadata/tests/System.Reflection.Metadata.Tests.csproj
@@ -27,6 +27,7 @@
Link="Common\System\IO\TempFile.cs" />
+