|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Metadata;
namespace System.Reflection
{
//
// This is a private assembly name abstraction that's more suitable for use as keys in our caches.
//
// - Immutable, unlike the public AssemblyName
// - Has a useful Equals() override, unlike the public AssemblyName.
//
// We use this as our internal interchange type and only convert to and from the public AssemblyName class at public boundaries.
//
public sealed class RuntimeAssemblyName : IEquatable<RuntimeAssemblyName>
{
public RuntimeAssemblyName(string name, Version? version, string? cultureName, AssemblyNameFlags flags, byte[]? publicKeyOrToken)
{
Debug.Assert(name != null);
this.Name = name;
// Optional version.
this.Version = version;
// Optional culture name.
this.CultureName = cultureName;
// Optional flags (this is actually an OR of the classic flags and the ContentType.)
this.Flags = flags;
// Optional public key (if Flags.PublicKey == true) or public key token.
this.PublicKeyOrToken = publicKeyOrToken;
}
public static RuntimeAssemblyName Parse(string name)
{
AssemblyNameParser.AssemblyNameParts parts = AssemblyNameParser.Parse(name);
return new RuntimeAssemblyName(parts._name, parts._version, parts._cultureName, parts._flags, parts._publicKeyOrToken);
}
// Simple name.
public string Name { get; }
// Optional version.
public Version? Version { get; }
// Optional culture name.
public string? CultureName { get; }
// Optional flags (this is actually an OR of the classic flags and the ContentType.)
public AssemblyNameFlags Flags { get; }
// Optional public key (if Flags.PublicKey == true) or public key token.
public byte[]? PublicKeyOrToken { get; }
// Equality - this compares every bit of data in the RuntimeAssemblyName which is acceptable for use as keys in a cache
// where semantic duplication is permissible. This method is *not* meant to define ref->def binding rules or
// assembly binding unification rules.
public bool Equals(RuntimeAssemblyName? other)
{
if (other == null)
return false;
if (!this.Name.Equals(other.Name))
return false;
if (this.Version == null)
{
if (other.Version != null)
return false;
}
else
{
if (!this.Version.Equals(other.Version))
return false;
}
if (!string.Equals(this.CultureName, other.CultureName))
return false;
if (this.Flags != other.Flags)
return false;
byte[]? thisPK = this.PublicKeyOrToken;
byte[]? otherPK = other.PublicKeyOrToken;
if (thisPK == null)
{
if (otherPK != null)
return false;
}
else if (otherPK == null)
{
return false;
}
else if (thisPK.Length != otherPK.Length)
{
return false;
}
else
{
for (int i = 0; i < thisPK.Length; i++)
{
if (thisPK[i] != otherPK[i])
return false;
}
}
return true;
}
public sealed override bool Equals(object? obj)
{
RuntimeAssemblyName? other = obj as RuntimeAssemblyName;
if (other == null)
return false;
return Equals(other);
}
public sealed override int GetHashCode()
{
return this.Name.GetHashCode();
}
//
// Converts an RuntimeAssemblyName to a freshly allocated AssemblyName with no data aliasing to any other object.
//
public AssemblyName ToAssemblyName()
{
AssemblyName assemblyName = new AssemblyName();
CopyToAssemblyName(assemblyName);
return assemblyName;
}
internal static RuntimeAssemblyName FromAssemblyNameInfo(AssemblyNameInfo source)
{
return new(source.Name, source.Version, source.CultureName, source._flags, source.PublicKeyOrToken);
}
//
// Copies a RuntimeAssemblyName into a freshly allocated AssemblyName with no data aliasing to any other object.
//
public void CopyToAssemblyName(AssemblyName blank)
{
blank.Name = this.Name;
if (this.Version != null)
blank.Version = this.Version;
if (this.CultureName != null)
blank.CultureName = this.CultureName;
// Our "Flags" contain both the classic flags and the ProcessorArchitecture + ContentType bits. The public AssemblyName has separate properties for
// these. The setters for these properties quietly mask out any bits intended for the other one, so we needn't do that ourselves..
blank.Flags = AssemblyNameInfo.ExtractAssemblyNameFlags(this.Flags);
blank.ContentType = AssemblyNameInfo.ExtractAssemblyContentType(this.Flags);
#pragma warning disable SYSLIB0037 // AssemblyName.ProcessorArchitecture is obsolete
blank.ProcessorArchitecture = AssemblyNameInfo.ExtractProcessorArchitecture(this.Flags);
#pragma warning restore SYSLIB0037
if (this.PublicKeyOrToken != null)
{
// We must not hand out our own copy of the PKT to AssemblyName as AssemblyName is amazingly trusting and gives untrusted callers
// full freedom to scribble on its PKT array. (As do we but we only have trusted callers!)
var pkCopy = (byte[])this.PublicKeyOrToken.Clone();
if (0 != (this.Flags & AssemblyNameFlags.PublicKey))
blank.SetPublicKey(pkCopy);
else
blank.SetPublicKeyToken(pkCopy);
}
return;
}
public string FullName
{
get
{
byte[]? pkt = (0 != (Flags & AssemblyNameFlags.PublicKey)) ? AssemblyNameHelpers.ComputePublicKeyToken(PublicKeyOrToken) : PublicKeyOrToken;
return AssemblyNameFormatter.ComputeDisplayName(Name, Version, CultureName, pkt, AssemblyNameInfo.ExtractAssemblyNameFlags(Flags), AssemblyNameInfo.ExtractAssemblyContentType(Flags));
}
}
}
}
|