// dnlib: See LICENSE.txt for more info
using System;
using System.Diagnostics;
using System.Threading;
using dnlib.Utils;
using dnlib.DotNet.MD;
using dnlib.DotNet.Pdb;
using System.Collections.Generic;
namespace dnlib.DotNet {
///
/// A high-level representation of a row in the GenericParam table
///
[DebuggerDisplay("{Name.String}")]
public abstract class GenericParam : IHasCustomAttribute, IHasCustomDebugInformation, IMemberDef, IListListener {
///
/// The row id in its table
///
protected uint rid;
///
public MDToken MDToken => new MDToken(Table.GenericParam, rid);
///
public uint Rid {
get => rid;
set => rid = value;
}
///
public int HasCustomAttributeTag => 19;
///
/// Gets the owner type/method
///
public ITypeOrMethodDef Owner {
get => owner;
internal set => owner = value;
}
///
protected ITypeOrMethodDef owner;
///
/// Gets the declaring type or null if none or if is
/// not a
///
public TypeDef DeclaringType => owner as TypeDef;
///
ITypeDefOrRef IMemberRef.DeclaringType => owner as TypeDef;
///
/// Gets the declaring method or null if none or if is
/// not a
///
public MethodDef DeclaringMethod => owner as MethodDef;
///
/// From column GenericParam.Number
///
public ushort Number {
get => number;
set => number = value;
}
///
protected ushort number;
///
/// From column GenericParam.Flags
///
public GenericParamAttributes Flags {
get => (GenericParamAttributes)attributes;
set => attributes = (int)value;
}
/// Attributes
protected int attributes;
///
/// From column GenericParam.Name
///
public UTF8String Name {
get => name;
set => name = value;
}
/// Name
protected UTF8String name;
///
/// From column GenericParam.Kind (v1.1 only)
///
public ITypeDefOrRef Kind {
get => kind;
set => kind = value;
}
///
protected ITypeDefOrRef kind;
///
/// Gets the generic param constraints
///
public IList GenericParamConstraints {
get {
if (genericParamConstraints is null)
InitializeGenericParamConstraints();
return genericParamConstraints;
}
}
///
protected LazyList genericParamConstraints;
/// Initializes
protected virtual void InitializeGenericParamConstraints() =>
Interlocked.CompareExchange(ref genericParamConstraints, 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 => 19;
///
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);
///
/// true if is not empty
///
public bool HasGenericParamConstraints => GenericParamConstraints.Count > 0;
///
public ModuleDef Module => owner?.Module;
///
public string FullName => UTF8String.ToSystemStringOrEmpty(name);
bool IIsTypeOrMethod.IsType => false;
bool IIsTypeOrMethod.IsMethod => false;
bool IMemberRef.IsField => false;
bool IMemberRef.IsTypeSpec => false;
bool IMemberRef.IsTypeRef => false;
bool IMemberRef.IsTypeDef => false;
bool IMemberRef.IsMethodSpec => false;
bool IMemberRef.IsMethodDef => false;
bool IMemberRef.IsMemberRef => false;
bool IMemberRef.IsFieldDef => false;
bool IMemberRef.IsPropertyDef => false;
bool IMemberRef.IsEventDef => false;
bool IMemberRef.IsGenericParam => true;
///
/// Modify property: =
/// ( & ) | .
///
/// Value to AND
/// Value to OR
void ModifyAttributes(GenericParamAttributes andMask, GenericParamAttributes 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, GenericParamAttributes flags) {
if (set)
attributes |= (int)flags;
else
attributes &= ~(int)flags;
}
///
/// Gets/sets variance (non, contra, co)
///
public GenericParamAttributes Variance {
get => (GenericParamAttributes)attributes & GenericParamAttributes.VarianceMask;
set => ModifyAttributes(~GenericParamAttributes.VarianceMask, value & GenericParamAttributes.VarianceMask);
}
///
/// true if is set
///
public bool IsNonVariant => Variance == GenericParamAttributes.NonVariant;
///
/// true if is set
///
public bool IsCovariant => Variance == GenericParamAttributes.Covariant;
///
/// true if is set
///
public bool IsContravariant => Variance == GenericParamAttributes.Contravariant;
///
/// Gets/sets the special constraint
///
public GenericParamAttributes SpecialConstraint {
get => (GenericParamAttributes)attributes & GenericParamAttributes.SpecialConstraintMask;
set => ModifyAttributes(~GenericParamAttributes.SpecialConstraintMask, value & GenericParamAttributes.SpecialConstraintMask);
}
///
/// true if there are no special constraints
///
public bool HasNoSpecialConstraint => ((GenericParamAttributes)attributes & GenericParamAttributes.SpecialConstraintMask) == GenericParamAttributes.NoSpecialConstraint;
///
/// Gets/sets the bit
///
public bool HasReferenceTypeConstraint {
get => ((GenericParamAttributes)attributes & GenericParamAttributes.ReferenceTypeConstraint) != 0;
set => ModifyAttributes(value, GenericParamAttributes.ReferenceTypeConstraint);
}
///
/// Gets/sets the bit
///
public bool HasNotNullableValueTypeConstraint {
get => ((GenericParamAttributes)attributes & GenericParamAttributes.NotNullableValueTypeConstraint) != 0;
set => ModifyAttributes(value, GenericParamAttributes.NotNullableValueTypeConstraint);
}
///
/// Gets/sets the bit
///
public bool HasDefaultConstructorConstraint {
get => ((GenericParamAttributes)attributes & GenericParamAttributes.DefaultConstructorConstraint) != 0;
set => ModifyAttributes(value, GenericParamAttributes.DefaultConstructorConstraint);
}
///
void IListListener.OnLazyAdd(int index, ref GenericParamConstraint value) => OnLazyAdd2(index, ref value);
internal virtual void OnLazyAdd2(int index, ref GenericParamConstraint value) {
#if DEBUG
if (value.Owner != this)
throw new InvalidOperationException("Added generic param constraint's Owner != this");
#endif
}
///
void IListListener.OnAdd(int index, GenericParamConstraint value) {
if (value.Owner is not null)
throw new InvalidOperationException("Generic param constraint is already owned by another generic param. Set Owner to null first.");
value.Owner = this;
}
///
void IListListener.OnRemove(int index, GenericParamConstraint value) => value.Owner = null;
///
void IListListener.OnResize(int index) {
}
///
void IListListener.OnClear() {
foreach (var gpc in genericParamConstraints.GetEnumerable_NoLock())
gpc.Owner = null;
}
///
public override string ToString() {
var o = owner;
if (o is TypeDef)
return $"!{number}";
if (o is MethodDef)
return $"!!{number}";
return $"??{number}";
}
}
///
/// A GenericParam row created by the user and not present in the original .NET file
///
public class GenericParamUser : GenericParam {
///
/// Default constructor
///
public GenericParamUser() {
}
///
/// Constructor
///
/// The generic param number
public GenericParamUser(ushort number)
: this(number, 0) {
}
///
/// Constructor
///
/// The generic param number
/// Flags
public GenericParamUser(ushort number, GenericParamAttributes flags)
: this(number, flags, UTF8String.Empty) {
}
///
/// Constructor
///
/// The generic param number
/// Flags
/// Name
public GenericParamUser(ushort number, GenericParamAttributes flags, UTF8String name) {
genericParamConstraints = new LazyList(this);
this.number = number;
attributes = (int)flags;
this.name = name;
}
}
///
/// Created from a row in the GenericParam table
///
sealed class GenericParamMD : GenericParam, IMDTokenProviderMD {
/// The module where this instance is located
readonly ModuleDefMD readerModule;
readonly uint origRid;
///
public uint OrigRid => origRid;
///
protected override void InitializeCustomAttributes() {
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.GenericParam, 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), GetGenericParamContext(owner), list);
Interlocked.CompareExchange(ref customDebugInfos, list, null);
}
///
protected override void InitializeGenericParamConstraints() {
var list = readerModule.Metadata.GetGenericParamConstraintRidList(origRid);
var tmp = new LazyList(list.Count, this, list, (list2, index) => readerModule.ResolveGenericParamConstraint(list2[index], GetGenericParamContext(owner)));
Interlocked.CompareExchange(ref genericParamConstraints, tmp, null);
}
static GenericParamContext GetGenericParamContext(ITypeOrMethodDef tmOwner) {
if (tmOwner is MethodDef md)
return GenericParamContext.Create(md);
return new GenericParamContext(tmOwner as TypeDef);
}
///
/// Constructor
///
/// The module which contains this GenericParam row
/// Row ID
/// If is null
/// If is invalid
public GenericParamMD(ModuleDefMD readerModule, uint rid) {
#if DEBUG
if (readerModule is null)
throw new ArgumentNullException("readerModule");
if (readerModule.TablesStream.GenericParamTable.IsInvalidRID(rid))
throw new BadImageFormatException($"GenericParam rid {rid} does not exist");
#endif
origRid = rid;
this.rid = rid;
this.readerModule = readerModule;
bool b = readerModule.TablesStream.TryReadGenericParamRow(origRid, out var row);
Debug.Assert(b);
number = row.Number;
attributes = row.Flags;
name = readerModule.StringsStream.ReadNoNull(row.Name);
owner = readerModule.GetOwner(this);
if (row.Kind != 0)
kind = readerModule.ResolveTypeDefOrRef(row.Kind, GetGenericParamContext(owner));
}
internal GenericParamMD InitializeAll() {
MemberMDInitializer.Initialize(Owner);
MemberMDInitializer.Initialize(Number);
MemberMDInitializer.Initialize(Flags);
MemberMDInitializer.Initialize(Name);
MemberMDInitializer.Initialize(Kind);
MemberMDInitializer.Initialize(CustomAttributes);
MemberMDInitializer.Initialize(GenericParamConstraints);
return this;
}
///
internal override void OnLazyAdd2(int index, ref GenericParamConstraint value) {
if (value.Owner != this) {
// More than one owner... This module has invalid metadata.
value = readerModule.ForceUpdateRowId(readerModule.ReadGenericParamConstraint(value.Rid, GetGenericParamContext(owner)).InitializeAll());
value.Owner = this;
}
}
}
}