// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
namespace dnlib.DotNet {
///
/// A custom attribute
///
public sealed class CustomAttribute : ICustomAttribute {
ICustomAttributeType ctor;
byte[] rawData;
readonly IList arguments;
readonly IList namedArguments;
uint caBlobOffset;
///
/// Gets/sets the custom attribute constructor
///
public ICustomAttributeType Constructor {
get => ctor;
set => ctor = value;
}
///
/// Gets the attribute type
///
public ITypeDefOrRef AttributeType => ctor?.DeclaringType;
///
/// Gets the full name of the attribute type
///
public string TypeFullName {
get {
if (ctor is MemberRef mrCtor)
return mrCtor.GetDeclaringTypeFullName() ?? string.Empty;
if (ctor is MethodDef mdCtor) {
var declType = mdCtor.DeclaringType;
if (declType is not null)
return declType.FullName;
}
return string.Empty;
}
}
///
/// Gets the name of the attribute type
///
internal string TypeName {
get {
if (ctor is MemberRef mrCtor)
return mrCtor.GetDeclaringTypeName() ?? string.Empty;
if (ctor is MethodDef mdCtor) {
var declType = mdCtor.DeclaringType;
if (declType is not null)
return declType.Name;
}
return string.Empty;
}
}
///
/// true if the raw custom attribute blob hasn't been parsed
///
public bool IsRawBlob => rawData is not null;
///
/// Gets the raw custom attribute blob or null if the CA was successfully parsed.
///
public byte[] RawData => rawData;
///
/// Gets all constructor arguments
///
public IList ConstructorArguments => arguments;
///
/// true if is not empty
///
public bool HasConstructorArguments => arguments.Count > 0;
///
/// Gets all named arguments (field and property values)
///
public IList NamedArguments => namedArguments;
///
/// true if is not empty
///
public bool HasNamedArguments => namedArguments.Count > 0;
///
/// Gets all s that are field arguments
///
public IEnumerable Fields {
get {
var namedArguments = this.namedArguments;
int count = namedArguments.Count;
for (int i = 0; i < count; i++) {
var namedArg = namedArguments[i];
if (namedArg.IsField)
yield return namedArg;
}
}
}
///
/// Gets all s that are property arguments
///
public IEnumerable Properties {
get {
var namedArguments = this.namedArguments;
int count = namedArguments.Count;
for (int i = 0; i < count; i++) {
var namedArg = namedArguments[i];
if (namedArg.IsProperty)
yield return namedArg;
}
}
}
///
/// Gets the #Blob offset or 0 if unknown
///
public uint BlobOffset => caBlobOffset;
///
/// Constructor
///
/// Custom attribute constructor
/// Raw custom attribute blob
public CustomAttribute(ICustomAttributeType ctor, byte[] rawData)
: this(ctor, null, null, 0) => this.rawData = rawData;
///
/// Constructor
///
/// Custom attribute constructor
public CustomAttribute(ICustomAttributeType ctor)
: this(ctor, null, null, 0) {
}
///
/// Constructor
///
/// Custom attribute constructor
/// Constructor arguments or null if none
public CustomAttribute(ICustomAttributeType ctor, IEnumerable arguments)
: this(ctor, arguments, null) {
}
///
/// Constructor
///
/// Custom attribute constructor
/// Named arguments or null if none
public CustomAttribute(ICustomAttributeType ctor, IEnumerable namedArguments)
: this(ctor, null, namedArguments) {
}
///
/// Constructor
///
/// Custom attribute constructor
/// Constructor arguments or null if none
/// Named arguments or null if none
public CustomAttribute(ICustomAttributeType ctor, IEnumerable arguments, IEnumerable namedArguments)
: this(ctor, arguments, namedArguments, 0) {
}
///
/// Constructor
///
/// Custom attribute constructor
/// Constructor arguments or null if none
/// Named arguments or null if none
/// Original custom attribute #Blob offset or 0
public CustomAttribute(ICustomAttributeType ctor, IEnumerable arguments, IEnumerable namedArguments, uint caBlobOffset) {
this.ctor = ctor;
this.arguments = arguments is null ? new List() : new List(arguments);
this.namedArguments = namedArguments is null ? new List() : new List(namedArguments);
this.caBlobOffset = caBlobOffset;
}
///
/// Constructor
///
/// Custom attribute constructor
/// Constructor arguments. The list is now owned by this instance.
/// Named arguments. The list is now owned by this instance.
/// Original custom attribute #Blob offset or 0
internal CustomAttribute(ICustomAttributeType ctor, List arguments, List namedArguments, uint caBlobOffset) {
this.ctor = ctor;
this.arguments = arguments ?? new List();
this.namedArguments = namedArguments ?? new List();
this.caBlobOffset = caBlobOffset;
}
///
/// Gets the field named
///
/// Name of field
/// A instance or null if not found
public CANamedArgument GetField(string name) => GetNamedArgument(name, true);
///
/// Gets the field named
///
/// Name of field
/// A instance or null if not found
public CANamedArgument GetField(UTF8String name) => GetNamedArgument(name, true);
///
/// Gets the property named
///
/// Name of property
/// A instance or null if not found
public CANamedArgument GetProperty(string name) => GetNamedArgument(name, false);
///
/// Gets the property named
///
/// Name of property
/// A instance or null if not found
public CANamedArgument GetProperty(UTF8String name) => GetNamedArgument(name, false);
///
/// Gets the property/field named
///
/// Name of property/field
/// true if it's a field, false if it's a property
/// A instance or null if not found
public CANamedArgument GetNamedArgument(string name, bool isField) {
var namedArguments = this.namedArguments;
int count = namedArguments.Count;
for (int i = 0; i < count; i++) {
var namedArg = namedArguments[i];
if (namedArg.IsField == isField && UTF8String.ToSystemStringOrEmpty(namedArg.Name) == name)
return namedArg;
}
return null;
}
///
/// Gets the property/field named
///
/// Name of property/field
/// true if it's a field, false if it's a property
/// A instance or null if not found
public CANamedArgument GetNamedArgument(UTF8String name, bool isField) {
var namedArguments = this.namedArguments;
int count = namedArguments.Count;
for (int i = 0; i < count; i++) {
var namedArg = namedArguments[i];
if (namedArg.IsField == isField && UTF8String.Equals(namedArg.Name, name))
return namedArg;
}
return null;
}
///
public override string ToString() => TypeFullName;
}
///
/// A custom attribute constructor argument
///
public struct CAArgument : ICloneable {
TypeSig type;
object value;
///
/// Gets/sets the argument type
///
public TypeSig Type {
readonly get => type;
set => type = value;
}
///
/// Gets/sets the argument value
///
public object Value {
readonly get => value;
set => this.value = value;
}
///
/// Constructor
///
/// Argument type
public CAArgument(TypeSig type) {
this.type = type;
value = null;
}
///
/// Constructor
///
/// Argument type
/// Argument value
public CAArgument(TypeSig type, object value) {
this.type = type;
this.value = value;
}
readonly object ICloneable.Clone() => Clone();
///
/// Clones this instance and any s and s
/// referenced from this instance.
///
///
public readonly CAArgument Clone() {
var value = this.value;
if (value is CAArgument)
value = ((CAArgument)value).Clone();
else if (value is IList args) {
var newArgs = new List(args.Count);
int count = args.Count;
for (int i = 0; i < count; i++) {
var arg = args[i];
newArgs.Add(arg.Clone());
}
value = newArgs;
}
return new CAArgument(type, value);
}
///
public override readonly string ToString() => $"{value ?? "null"} ({type})";
}
///
/// A custom attribute field/property argument
///
public sealed class CANamedArgument : ICloneable {
bool isField;
TypeSig type;
UTF8String name;
CAArgument argument;
///
/// true if it's a field
///
public bool IsField {
get => isField;
set => isField = value;
}
///
/// true if it's a property
///
public bool IsProperty {
get => !isField;
set => isField = !value;
}
///
/// Gets/sets the field/property type
///
public TypeSig Type {
get => type;
set => type = value;
}
///
/// Gets/sets the property/field name
///
public UTF8String Name {
get => name;
set => name = value;
}
///
/// Gets/sets the argument
///
public CAArgument Argument {
get => argument;
set => argument = value;
}
///
/// Gets/sets the argument type
///
public TypeSig ArgumentType {
get => argument.Type;
set => argument.Type = value;
}
///
/// Gets/sets the argument value
///
public object Value {
get => argument.Value;
set => argument.Value = value;
}
///
/// Default constructor
///
public CANamedArgument() {
}
///
/// Constructor
///
/// true if field, false if property
public CANamedArgument(bool isField) => this.isField = isField;
///
/// Constructor
///
/// true if field, false if property
/// Field/property type
public CANamedArgument(bool isField, TypeSig type) {
this.isField = isField;
this.type = type;
}
///
/// Constructor
///
/// true if field, false if property
/// Field/property type
/// Name of field/property
public CANamedArgument(bool isField, TypeSig type, UTF8String name) {
this.isField = isField;
this.type = type;
this.name = name;
}
///
/// Constructor
///
/// true if field, false if property
/// Field/property type
/// Name of field/property
/// Field/property argument
public CANamedArgument(bool isField, TypeSig type, UTF8String name, CAArgument argument) {
this.isField = isField;
this.type = type;
this.name = name;
this.argument = argument;
}
object ICloneable.Clone() => Clone();
///
/// Clones this instance and any s referenced from this instance.
///
///
public CANamedArgument Clone() => new CANamedArgument(isField, type, name, argument.Clone());
///
public override string ToString() => $"({(isField ? "field" : "property")}) {type} {name} = {Value ?? "null"} ({ArgumentType})";
}
}