// dnlib: See LICENSE.txt for more info
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using dnlib.Utils;
using dnlib.DotNet.MD;
using dnlib.DotNet.Writer;
using System.Text.RegularExpressions;
using dnlib.DotNet.Pdb;
using System.Collections.Generic;
using System.Diagnostics;
namespace dnlib.DotNet {
///
/// A high-level representation of a row in the Assembly table
///
public abstract class AssemblyDef : IHasCustomAttribute, IHasDeclSecurity, IHasCustomDebugInformation, IAssembly, IListListener, ITypeDefFinder, IDnlibDef {
///
/// The row id in its table
///
protected uint rid;
///
public MDToken MDToken => new MDToken(Table.Assembly, rid);
///
public uint Rid {
get => rid;
set => rid = value;
}
///
public int HasCustomAttributeTag => 14;
///
public int HasDeclSecurityTag => 2;
///
/// From column Assembly.HashAlgId
///
public AssemblyHashAlgorithm HashAlgorithm {
get => hashAlgorithm;
set => hashAlgorithm = value;
}
///
protected AssemblyHashAlgorithm hashAlgorithm;
///
/// From columns Assembly.MajorVersion, Assembly.MinorVersion, Assembly.BuildNumber,
/// Assembly.RevisionNumber.
///
/// If is null
public Version Version {
get => version;
set => version = value ?? throw new ArgumentNullException(nameof(value));
}
///
protected Version version;
///
/// From column Assembly.Flags
///
public AssemblyAttributes Attributes {
get => (AssemblyAttributes)attributes;
set => attributes = (int)value;
}
/// Attributes
protected int attributes;
///
/// From column Assembly.PublicKey
///
/// An empty is created if the caller writes null
public PublicKey PublicKey {
get => publicKey;
set => publicKey = value ?? new PublicKey();
}
///
protected PublicKey publicKey;
///
/// Gets the public key token which is calculated from
///
public PublicKeyToken PublicKeyToken => publicKey.Token;
///
/// From column Assembly.Name
///
public UTF8String Name {
get => name;
set => name = value;
}
/// Name
protected UTF8String name;
///
/// From column Assembly.Locale
///
public UTF8String Culture {
get => culture;
set => culture = value;
}
/// Name
protected UTF8String culture;
///
public IList DeclSecurities {
get {
if (declSecurities is null)
InitializeDeclSecurities();
return declSecurities;
}
}
///
protected IList declSecurities;
/// Initializes
protected virtual void InitializeDeclSecurities() =>
Interlocked.CompareExchange(ref declSecurities, new List(), null);
///
public PublicKeyBase PublicKeyOrToken => publicKey;
///
public string FullName => GetFullNameWithPublicKeyToken();
///
public string FullNameToken => GetFullNameWithPublicKeyToken();
///
/// Gets all modules. The first module is always the .
///
public IList Modules {
get {
if (modules is null)
InitializeModules();
return modules;
}
}
///
protected LazyList modules;
/// Initializes
protected virtual void InitializeModules() =>
Interlocked.CompareExchange(ref modules, new LazyList(this), null);
///
/// Gets all custom attributes
///
public CustomAttributeCollection CustomAttributes {
get {
if (customAttributes is null)
InitializeCustomAttributes();
return customAttributes;
}
}
///
protected CustomAttributeCollection customAttributes;
/// Initializes
protected virtual void InitializeCustomAttributes() =>
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);
///
public bool HasCustomAttributes => CustomAttributes.Count > 0;
///
public int HasCustomDebugInformationTag => 14;
///
public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
///
/// Gets all custom debug infos
///
public IList CustomDebugInfos {
get {
if (customDebugInfos is null)
InitializeCustomDebugInfos();
return customDebugInfos;
}
}
///
protected IList customDebugInfos;
/// Initializes
protected virtual void InitializeCustomDebugInfos() =>
Interlocked.CompareExchange(ref customDebugInfos, new List(), null);
///
public bool HasDeclSecurities => DeclSecurities.Count > 0;
///
/// true if is not empty
///
public bool HasModules => Modules.Count > 0;
///
/// Gets the manifest (main) module. This is always the first module in .
/// null is returned if is empty.
///
public ModuleDef ManifestModule => Modules.Count == 0 ? null : Modules[0];
///
/// Modify property: =
/// ( & ) | .
///
/// Value to AND
/// Value to OR
void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) =>
attributes = (attributes & (int)andMask) | (int)orMask;
///
/// Set or clear flags in
///
/// true if flags should be set, false if flags should
/// be cleared
/// Flags to set or clear
void ModifyAttributes(bool set, AssemblyAttributes flags) {
if (set)
attributes |= (int)flags;
else
attributes &= ~(int)flags;
}
///
/// Gets/sets the bit
///
public bool HasPublicKey {
get => ((AssemblyAttributes)attributes & AssemblyAttributes.PublicKey) != 0;
set => ModifyAttributes(value, AssemblyAttributes.PublicKey);
}
///
/// Gets/sets the processor architecture
///
public AssemblyAttributes ProcessorArchitecture {
get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask;
set => ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask);
}
///
/// Gets/sets the processor architecture
///
public AssemblyAttributes ProcessorArchitectureFull {
get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_FullMask;
set => ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask);
}
///
/// true if unspecified processor architecture
///
public bool IsProcessorArchitectureNone => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None;
///
/// true if neutral (PE32) architecture
///
public bool IsProcessorArchitectureMSIL => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL;
///
/// true if x86 (PE32) architecture
///
public bool IsProcessorArchitectureX86 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86;
///
/// true if IA-64 (PE32+) architecture
///
public bool IsProcessorArchitectureIA64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64;
///
/// true if x64 (PE32+) architecture
///
public bool IsProcessorArchitectureX64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64;
///
/// true if ARM (PE32) architecture
///
public bool IsProcessorArchitectureARM => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM;
///
/// true if eg. reference assembly (not runnable)
///
public bool IsProcessorArchitectureNoPlatform => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform;
///
/// Gets/sets the bit
///
public bool IsProcessorArchitectureSpecified {
get => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Specified) != 0;
set => ModifyAttributes(value, AssemblyAttributes.PA_Specified);
}
///
/// Gets/sets the bit
///
public bool EnableJITcompileTracking {
get => ((AssemblyAttributes)attributes & AssemblyAttributes.EnableJITcompileTracking) != 0;
set => ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking);
}
///
/// Gets/sets the bit
///
public bool DisableJITcompileOptimizer {
get => ((AssemblyAttributes)attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0;
set => ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer);
}
///
/// Gets/sets the bit
///
public bool IsRetargetable {
get => ((AssemblyAttributes)attributes & AssemblyAttributes.Retargetable) != 0;
set => ModifyAttributes(value, AssemblyAttributes.Retargetable);
}
///
/// Gets/sets the content type
///
public AssemblyAttributes ContentType {
get => (AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask;
set => ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask);
}
///
/// true if content type is Default
///
public bool IsContentTypeDefault => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default;
///
/// true if content type is WindowsRuntime
///
public bool IsContentTypeWindowsRuntime => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime;
///
/// Finds a module in this assembly
///
/// Name of module
/// A instance or null if it wasn't found.
public ModuleDef FindModule(UTF8String name) {
var modules = Modules;
int count = modules.Count;
for (int i = 0; i < count; i++) {
var module = modules[i];
if (module is null)
continue;
if (UTF8String.CaseInsensitiveEquals(module.Name, name))
return module;
}
return null;
}
///
/// Creates an instance from a file
///
/// File name of an existing .NET assembly
/// Module context or null
/// A new instance
/// If is null
/// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
public static AssemblyDef Load(string fileName, ModuleContext context) =>
Load(fileName, new ModuleCreationOptions(context));
///
/// Creates an instance from a file
///
/// File name of an existing .NET assembly
/// Module creation options or null
/// A new instance
/// If is null
/// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
public static AssemblyDef Load(string fileName, ModuleCreationOptions options = null) {
if (fileName is null)
throw new ArgumentNullException(nameof(fileName));
ModuleDef module = null;
try {
module = ModuleDefMD.Load(fileName, options);
var asm = module.Assembly;
if (asm is null)
throw new BadImageFormatException($"{fileName} is only a .NET module, not a .NET assembly. Use ModuleDef.Load().");
return asm;
}
catch {
if (module is not null)
module.Dispose();
throw;
}
}
///
/// Creates an instance from a byte[]
///
/// Contents of a .NET assembly
/// Module context or null
/// A new instance
/// If is null
/// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
public static AssemblyDef Load(byte[] data, ModuleContext context) =>
Load(data, new ModuleCreationOptions(context));
///
/// Creates an instance from a byte[]
///
/// Contents of a .NET assembly
/// Module creation options or null
/// A new instance
/// If is null
/// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
public static AssemblyDef Load(byte[] data, ModuleCreationOptions options = null) {
if (data is null)
throw new ArgumentNullException(nameof(data));
ModuleDef module = null;
try {
module = ModuleDefMD.Load(data, options);
var asm = module.Assembly;
if (asm is null)
throw new BadImageFormatException($"{module.ToString()} is only a .NET module, not a .NET assembly. Use ModuleDef.Load().");
return asm;
}
catch {
if (module is not null)
module.Dispose();
throw;
}
}
///
/// Creates an instance from a memory location
///
/// Address of a .NET assembly
/// Module context or null
/// A new instance
/// If is null
/// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
public static AssemblyDef Load(IntPtr addr, ModuleContext context) =>
Load(addr, new ModuleCreationOptions(context));
///
/// Creates an instance from a memory location
///
/// Address of a .NET assembly
/// Module creation options or null
/// A new instance
/// If is null
/// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
public static AssemblyDef Load(IntPtr addr, ModuleCreationOptions options = null) {
if (addr == IntPtr.Zero)
throw new ArgumentNullException(nameof(addr));
ModuleDef module = null;
try {
module = ModuleDefMD.Load(addr, options);
var asm = module.Assembly;
if (asm is null)
throw new BadImageFormatException($"{module.ToString()} (addr: {addr.ToInt64():X8}) is only a .NET module, not a .NET assembly. Use ModuleDef.Load().");
return asm;
}
catch {
if (module is not null)
module.Dispose();
throw;
}
}
///
/// Creates an instance from a stream
///
/// This will read all bytes from the stream and call .
/// It's better to use one of the other Load() methods.
/// The stream
/// Module context or null
/// A new instance
/// If is null
/// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
public static AssemblyDef Load(Stream stream, ModuleContext context) =>
Load(stream, new ModuleCreationOptions(context));
///
/// Creates an instance from a stream
///
/// This will read all bytes from the stream and call .
/// It's better to use one of the other Load() methods.
/// The stream
/// Module creation options or null
/// A new instance
/// If is null
/// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
public static AssemblyDef Load(Stream stream, ModuleCreationOptions options = null) {
if (stream is null)
throw new ArgumentNullException(nameof(stream));
ModuleDef module = null;
try {
module = ModuleDefMD.Load(stream, options);
var asm = module.Assembly;
if (asm is null)
throw new BadImageFormatException($"{module.ToString()} is only a .NET module, not a .NET assembly. Use ModuleDef.Load().");
return asm;
}
catch {
if (module is not null)
module.Dispose();
throw;
}
}
///
/// Gets the assembly name with the public key
///
public string GetFullNameWithPublicKey() => FullNameFactory.AssemblyFullName(this, false);
///
/// Gets the assembly name with the public key token
///
public string GetFullNameWithPublicKeyToken() => FullNameFactory.AssemblyFullName(this, true);
///
/// Finds a . For speed, enable
/// if possible (read the documentation first).
///
/// Full name of the type (no assembly information)
/// true if it's a reflection name, and nested
/// type names are separated by a + character. If false, nested type names
/// are separated by a / character.
/// An existing or null if it wasn't found.
public TypeDef Find(string fullName, bool isReflectionName) {
var modules = Modules;
int count = modules.Count;
for (int i = 0; i < count; i++) {
var module = modules[i];
if (module is null)
continue;
var type = module.Find(fullName, isReflectionName);
if (type is not null)
return type;
}
return null;
}
///
/// Finds a . Its scope (i.e., module or assembly) is ignored when
/// looking up the type. For speed, enable
/// if possible (read the documentation first).
///
/// The type ref
/// An existing or null if it wasn't found.
public TypeDef Find(TypeRef typeRef) {
var modules = Modules;
int count = modules.Count;
for (int i = 0; i < count; i++) {
var module = modules[i];
if (module is null)
continue;
var type = module.Find(typeRef);
if (type is not null)
return type;
}
return null;
}
///
/// Writes the assembly to a file on disk. If the file exists, it will be truncated.
///
/// Filename
/// Writer options
public void Write(string filename, ModuleWriterOptions options = null) =>
ManifestModule.Write(filename, options);
///
/// Writes the assembly to a stream.
///
/// Destination stream
/// Writer options
public void Write(Stream dest, ModuleWriterOptions options = null) =>
ManifestModule.Write(dest, options);
///
/// Checks whether this assembly is a friend assembly of
///
/// Target assembly
public bool IsFriendAssemblyOf(AssemblyDef targetAsm) {
if (targetAsm is null)
return false;
if (this == targetAsm)
return true;
// Both must be unsigned or both must be signed according to the
// InternalsVisibleToAttribute documentation.
if (PublicKeyBase.IsNullOrEmpty2(publicKey) != PublicKeyBase.IsNullOrEmpty2(targetAsm.PublicKey))
return false;
foreach (var ca in targetAsm.CustomAttributes.FindAll("System.Runtime.CompilerServices.InternalsVisibleToAttribute")) {
if (ca.ConstructorArguments.Count != 1)
continue;
var arg = ca.ConstructorArguments.Count == 0 ? default : ca.ConstructorArguments[0];
if (arg.Type.GetElementType() != ElementType.String)
continue;
var asmName = arg.Value as UTF8String;
if (UTF8String.IsNull(asmName))
continue;
var asmInfo = new AssemblyNameInfo(asmName);
if (asmInfo.Name != name)
continue;
if (!PublicKeyBase.IsNullOrEmpty2(publicKey)) {
if (!PublicKey.Equals(asmInfo.PublicKeyOrToken as PublicKey))
continue;
}
else if (!PublicKeyBase.IsNullOrEmpty2(asmInfo.PublicKeyOrToken))
continue;
return true;
}
return false;
}
///
/// Adds or updates an existing System.Reflection.AssemblySignatureKeyAttribute
/// attribute. This attribute is used in enhanced strong naming with key migration.
/// See http://msdn.microsoft.com/en-us/library/hh415055.aspx
///
/// Identity public key
/// Identity strong name key pair
/// Signature public key
public void UpdateOrCreateAssemblySignatureKeyAttribute(StrongNamePublicKey identityPubKey, StrongNameKey identityKey, StrongNamePublicKey signaturePubKey) {
var manifestModule = ManifestModule;
if (manifestModule is null)
return;
// Remove all existing attributes
CustomAttribute ca = null;
for (int i = 0; i < CustomAttributes.Count; i++) {
var caTmp = CustomAttributes[i];
if (caTmp.TypeFullName != "System.Reflection.AssemblySignatureKeyAttribute")
continue;
CustomAttributes.RemoveAt(i);
i--;
if (ca is null)
ca = caTmp;
}
if (IsValidAssemblySignatureKeyAttribute(ca))
ca.NamedArguments.Clear();
else
ca = CreateAssemblySignatureKeyAttribute();
var counterSig = StrongNameKey.CreateCounterSignatureAsString(identityPubKey, identityKey, signaturePubKey);
ca.ConstructorArguments[0] = new CAArgument(manifestModule.CorLibTypes.String, new UTF8String(signaturePubKey.ToString()));
ca.ConstructorArguments[1] = new CAArgument(manifestModule.CorLibTypes.String, new UTF8String(counterSig));
CustomAttributes.Add(ca);
}
bool IsValidAssemblySignatureKeyAttribute(CustomAttribute ca) {
if (dnlib.Settings.IsThreadSafe)
return false;
if (ca is null)
return false;
var ctor = ca.Constructor;
if (ctor is null)
return false;
var sig = ctor.MethodSig;
if (sig is null || sig.Params.Count != 2)
return false;
if (sig.Params[0].GetElementType() != ElementType.String)
return false;
if (sig.Params[1].GetElementType() != ElementType.String)
return false;
if (ca.ConstructorArguments.Count != 2)
return false;
return true;
}
CustomAttribute CreateAssemblySignatureKeyAttribute() {
var manifestModule = ManifestModule;
var owner = manifestModule.UpdateRowId(new TypeRefUser(manifestModule, "System.Reflection", "AssemblySignatureKeyAttribute", manifestModule.CorLibTypes.AssemblyRef));
var methodSig = MethodSig.CreateInstance(manifestModule.CorLibTypes.Void, manifestModule.CorLibTypes.String, manifestModule.CorLibTypes.String);
var ctor = manifestModule.UpdateRowId(new MemberRefUser(manifestModule, MethodDef.InstanceConstructorName, methodSig, owner));
var ca = new CustomAttribute(ctor);
ca.ConstructorArguments.Add(new CAArgument(manifestModule.CorLibTypes.String, UTF8String.Empty));
ca.ConstructorArguments.Add(new CAArgument(manifestModule.CorLibTypes.String, UTF8String.Empty));
return ca;
}
///
/// Gets the original System.Runtime.Versioning.TargetFrameworkAttribute custom attribute information if possible.
/// It reads this from the original metadata and doesn't use .
/// Returns false if the custom attribute isn't present or if it is invalid.
///
/// Framework name
/// Version
/// Profile
///
public virtual bool TryGetOriginalTargetFrameworkAttribute(out string framework, out Version version, out string profile) {
framework = null;
version = null;
profile = null;
return false;
}
///
void IListListener.OnLazyAdd(int index, ref ModuleDef module) {
if (module is null)
return;
#if DEBUG
if (module.Assembly is null)
throw new InvalidOperationException("Module.Assembly is null");
#endif
}
///
void IListListener.OnAdd(int index, ModuleDef module) {
if (module is null)
return;
if (module.Assembly is not null)
throw new InvalidOperationException("Module already has an assembly. Remove it from that assembly before adding it to this assembly.");
module.Assembly = this;
}
///
void IListListener.OnRemove(int index, ModuleDef module) {
if (module is not null)
module.Assembly = null;
}
///
void IListListener.OnResize(int index) {
}
///
void IListListener.OnClear() {
foreach (var module in modules.GetEnumerable_NoLock()) {
if (module is not null)
module.Assembly = null;
}
}
///
public override string ToString() => FullName;
}
///
/// An Assembly row created by the user and not present in the original .NET file
///
public class AssemblyDefUser : AssemblyDef {
///
/// Default constructor
///
public AssemblyDefUser()
: this(UTF8String.Empty, new Version(0, 0, 0, 0)) {
}
///
/// Constructor
///
/// Simple name
/// If any of the args is invalid
public AssemblyDefUser(UTF8String name)
: this(name, new Version(0, 0, 0, 0), new PublicKey()) {
}
///
/// Constructor
///
/// Simple name
/// Version
/// If any of the args is invalid
public AssemblyDefUser(UTF8String name, Version version)
: this(name, version, new PublicKey()) {
}
///
/// Constructor
///
/// Simple name
/// Version
/// Public key
/// If any of the args is invalid
public AssemblyDefUser(UTF8String name, Version version, PublicKey publicKey)
: this(name, version, publicKey, UTF8String.Empty) {
}
///
/// Constructor
///
/// Simple name
/// Version
/// Public key
/// Locale
/// If any of the args is invalid
public AssemblyDefUser(UTF8String name, Version version, PublicKey publicKey, UTF8String locale) {
if (name is null)
throw new ArgumentNullException(nameof(name));
if (locale is null)
throw new ArgumentNullException(nameof(locale));
modules = new LazyList(this);
this.name = name;
this.version = version ?? throw new ArgumentNullException(nameof(version));
this.publicKey = publicKey ?? new PublicKey();
culture = locale;
attributes = (int)AssemblyAttributes.None;
}
///
/// Constructor
///
/// Assembly name info
/// If is null
public AssemblyDefUser(AssemblyName asmName)
: this(new AssemblyNameInfo(asmName)) {
hashAlgorithm = (AssemblyHashAlgorithm)asmName.HashAlgorithm;
attributes = (int)asmName.Flags;
}
///
/// Constructor
///
/// Assembly name info
/// If is null
public AssemblyDefUser(IAssembly asmName) {
if (asmName is null)
throw new ArgumentNullException(nameof(asmName));
modules = new LazyList(this);
name = asmName.Name;
version = asmName.Version ?? new Version(0, 0, 0, 0);
publicKey = asmName.PublicKeyOrToken as PublicKey ?? new PublicKey();
culture = asmName.Culture;
attributes = (int)AssemblyAttributes.None;
hashAlgorithm = AssemblyHashAlgorithm.SHA1;
}
}
///
/// Created from a row in the Assembly table
///
sealed class AssemblyDefMD : AssemblyDef, IMDTokenProviderMD {
/// The module where this instance is located
readonly ModuleDefMD readerModule;
readonly uint origRid;
///
public uint OrigRid => origRid;
///
protected override void InitializeDeclSecurities() {
var list = readerModule.Metadata.GetDeclSecurityRidList(Table.Assembly, origRid);
var tmp = new LazyList(list.Count, list, (list2, index) => readerModule.ResolveDeclSecurity(list2[index]));
Interlocked.CompareExchange(ref declSecurities, tmp, null);
}
///
protected override void InitializeModules() {
var list = readerModule.GetModuleRidList();
var tmp = new LazyList(list.Count + 1, this, list, (list2, index) => {
ModuleDef module;
if (index == 0)
module = readerModule;
else
module = readerModule.ReadModule(list2[index - 1], this);
if (module is null)
module = new ModuleDefUser("INVALID", Guid.NewGuid());
module.Assembly = this;
return module;
});
Interlocked.CompareExchange(ref modules, tmp, null);
}
///
protected override void InitializeCustomAttributes() {
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Assembly, origRid);
var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index]));
Interlocked.CompareExchange(ref customAttributes, tmp, null);
}
///
protected override void InitializeCustomDebugInfos() {
var list = new List();
readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list);
Interlocked.CompareExchange(ref customDebugInfos, list, null);
}
///
public override bool TryGetOriginalTargetFrameworkAttribute(out string framework, out Version version, out string profile) {
if (!hasInitdTFA)
InitializeTargetFrameworkAttribute();
framework = tfaFramework;
version = tfaVersion;
profile = tfaProfile;
return tfaReturnValue;
}
volatile bool hasInitdTFA;
string tfaFramework;
Version tfaVersion;
string tfaProfile;
bool tfaReturnValue;
void InitializeTargetFrameworkAttribute() {
if (hasInitdTFA)
return;
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Assembly, origRid);
var gpContext = new GenericParamContext();
for (int i = 0; i < list.Count; i++) {
var caRid = list[i];
if (!readerModule.TablesStream.TryReadCustomAttributeRow(caRid, out var caRow))
continue;
var caType = readerModule.ResolveCustomAttributeType(caRow.Type, gpContext);
if (!TryGetName(caType, out var ns, out var name))
continue;
if (ns != nameSystemRuntimeVersioning || name != nameTargetFrameworkAttribute)
continue;
var ca = CustomAttributeReader.Read(readerModule, caType, caRow.Value, gpContext);
if (ca is null || ca.ConstructorArguments.Count != 1)
continue;
var s = ca.ConstructorArguments[0].Value as UTF8String;
if (s is null)
continue;
if (TryCreateTargetFrameworkInfo(s, out var tmpFramework, out var tmpVersion, out var tmpProfile)) {
tfaFramework = tmpFramework;
tfaVersion = tmpVersion;
tfaProfile = tmpProfile;
tfaReturnValue = true;
break;
}
}
hasInitdTFA = true;
}
static readonly UTF8String nameSystemRuntimeVersioning = new UTF8String("System.Runtime.Versioning");
static readonly UTF8String nameTargetFrameworkAttribute = new UTF8String("TargetFrameworkAttribute");
static bool TryGetName(ICustomAttributeType caType, out UTF8String ns, out UTF8String name) {
ITypeDefOrRef type;
if (caType is MemberRef mr)
type = mr.DeclaringType;
else
type = (caType as MethodDef)?.DeclaringType;
if (type is TypeRef tr) {
ns = tr.Namespace;
name = tr.Name;
return true;
}
if (type is TypeDef td) {
ns = td.Namespace;
name = td.Name;
return true;
}
ns = null;
name = null;
return false;
}
static bool TryCreateTargetFrameworkInfo(string attrString, out string framework, out Version version, out string profile) {
framework = null;
version = null;
profile = null;
// See corclr/src/mscorlib/src/System/Runtime/Versioning/BinaryCompatibility.cs
var values = attrString.Split(new char[] { ',' });
if (values.Length < 2 || values.Length > 3)
return false;
var frameworkRes = values[0].Trim();
if (frameworkRes.Length == 0)
return false;
Version versionRes = null;
string profileRes = null;
for (int i = 1; i < values.Length; i++) {
var kvp = values[i].Split('=');
if (kvp.Length != 2)
return false;
var key = kvp[0].Trim();
var value = kvp[1].Trim();
if (key.Equals("Version", StringComparison.OrdinalIgnoreCase)) {
if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase))
value = value.Substring(1);
if (!TryParse(value, out versionRes))
return false;
versionRes = new Version(versionRes.Major, versionRes.Minor, versionRes.Build == -1 ? 0 : versionRes.Build, 0);
}
else if (key.Equals("Profile", StringComparison.OrdinalIgnoreCase)) {
if (!string.IsNullOrEmpty(value))
profileRes = value;
}
}
if (versionRes is null)
return false;
framework = frameworkRes;
version = versionRes;
profile = profileRes;
return true;
}
static int ParseInt32(string s) => int.TryParse(s, out int res) ? res : 0;
static bool TryParse(string s, out Version version) {
Match m;
m = Regex.Match(s, @"^(\d+)\.(\d+)$");
if (m.Groups.Count == 3) {
version = new Version(ParseInt32(m.Groups[1].Value), ParseInt32(m.Groups[2].Value));
return true;
}
m = Regex.Match(s, @"^(\d+)\.(\d+)\.(\d+)$");
if (m.Groups.Count == 4) {
version = new Version(ParseInt32(m.Groups[1].Value), ParseInt32(m.Groups[2].Value), ParseInt32(m.Groups[3].Value));
return true;
}
m = Regex.Match(s, @"^(\d+)\.(\d+)\.(\d+)\.(\d+)$");
if (m.Groups.Count == 5) {
version = new Version(ParseInt32(m.Groups[1].Value), ParseInt32(m.Groups[2].Value), ParseInt32(m.Groups[3].Value), ParseInt32(m.Groups[4].Value));
return true;
}
version = null;
return false;
}
///
/// Constructor
///
/// The module which contains this Assembly row
/// Row ID
/// If is null
/// If is invalid
public AssemblyDefMD(ModuleDefMD readerModule, uint rid) {
#if DEBUG
if (readerModule is null)
throw new ArgumentNullException("readerModule");
if (readerModule.TablesStream.AssemblyTable.IsInvalidRID(rid))
throw new BadImageFormatException($"Assembly rid {rid} does not exist");
#endif
origRid = rid;
this.rid = rid;
this.readerModule = readerModule;
if (rid != 1)
modules = new LazyList(this);
bool b = readerModule.TablesStream.TryReadAssemblyRow(origRid, out var row);
Debug.Assert(b);
hashAlgorithm = (AssemblyHashAlgorithm)row.HashAlgId;
version = new Version(row.MajorVersion, row.MinorVersion, row.BuildNumber, row.RevisionNumber);
attributes = (int)row.Flags;
name = readerModule.StringsStream.ReadNoNull(row.Name);
culture = readerModule.StringsStream.ReadNoNull(row.Locale);
publicKey = new PublicKey(readerModule.BlobStream.Read(row.PublicKey));
}
}
}