281 lines
9.5 KiB
C#
281 lines
9.5 KiB
C#
![]() |
/*
|
||
|
Copyright (C) 2014-2019 de4dot@gmail.com
|
||
|
|
||
|
This file is part of dnSpy
|
||
|
|
||
|
dnSpy is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
dnSpy is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Diagnostics;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Text;
|
||
|
|
||
|
namespace dnSpy.Debugger.DotNet.Metadata {
|
||
|
/// <summary>
|
||
|
/// Custom attribute data
|
||
|
/// </summary>
|
||
|
public sealed class DmdCustomAttributeData {
|
||
|
/// <summary>
|
||
|
/// Gets the custom attribute type
|
||
|
/// </summary>
|
||
|
public DmdType AttributeType => Constructor.DeclaringType!;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the custom attribute constructor
|
||
|
/// </summary>
|
||
|
public DmdConstructorInfo Constructor { get; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the constructor arguments
|
||
|
/// </summary>
|
||
|
public IList<DmdCustomAttributeTypedArgument> ConstructorArguments { get; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets all named arguments (properties and fields)
|
||
|
/// </summary>
|
||
|
public IList<DmdCustomAttributeNamedArgument> NamedArguments { get; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// true if this custom attribute was not part of the #Blob but created from some other info
|
||
|
/// </summary>
|
||
|
public bool IsPseudoCustomAttribute { get; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// Constructor
|
||
|
/// </summary>
|
||
|
/// <param name="constructor">Custom attribute constructor</param>
|
||
|
/// <param name="constructorArguments">Constructor arguments or null</param>
|
||
|
/// <param name="namedArguments">Custom attribute named arguments (fields and properties) or null</param>
|
||
|
/// <param name="isPseudoCustomAttribute">true if this custom attribute was not part of the #Blob but created from some other info</param>
|
||
|
public DmdCustomAttributeData(DmdConstructorInfo constructor, IList<DmdCustomAttributeTypedArgument>? constructorArguments, IList<DmdCustomAttributeNamedArgument>? namedArguments, bool isPseudoCustomAttribute) {
|
||
|
Constructor = constructor ?? throw new ArgumentNullException(nameof(constructor));
|
||
|
ConstructorArguments = ReadOnlyCollectionHelpers.Create(constructorArguments);
|
||
|
NamedArguments = ReadOnlyCollectionHelpers.Create(namedArguments);
|
||
|
IsPseudoCustomAttribute = isPseudoCustomAttribute;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// ToString()
|
||
|
/// </summary>
|
||
|
/// <returns></returns>
|
||
|
public override string ToString() {
|
||
|
StringBuilder? sb = ObjectPools.AllocStringBuilder();
|
||
|
sb.Append('[');
|
||
|
sb.Append(AttributeType.FullName);
|
||
|
sb.Append('(');
|
||
|
bool comma = false;
|
||
|
foreach (var arg in ConstructorArguments) {
|
||
|
if (comma)
|
||
|
sb.Append(", ");
|
||
|
comma = true;
|
||
|
sb.Append(arg.ToString());
|
||
|
}
|
||
|
foreach (var arg in NamedArguments) {
|
||
|
if (comma)
|
||
|
sb.Append(", ");
|
||
|
comma = true;
|
||
|
sb.Append(arg.ToString());
|
||
|
}
|
||
|
sb.Append(")]");
|
||
|
return ObjectPools.FreeAndToString(ref sb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Custom attribute typed argument
|
||
|
/// </summary>
|
||
|
public readonly struct DmdCustomAttributeTypedArgument : IEquatable<DmdCustomAttributeTypedArgument> {
|
||
|
/// <summary>
|
||
|
/// Gets the argument type
|
||
|
/// </summary>
|
||
|
public DmdType ArgumentType { get; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the argument value
|
||
|
/// </summary>
|
||
|
public object? Value { get; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// Constructor
|
||
|
/// </summary>
|
||
|
/// <param name="argumentType">Argument type</param>
|
||
|
/// <param name="value">Argument value</param>
|
||
|
public DmdCustomAttributeTypedArgument(DmdType argumentType, object? value) {
|
||
|
VerifyValue(value);
|
||
|
ArgumentType = argumentType ?? throw new ArgumentNullException(nameof(argumentType));
|
||
|
Value = value;
|
||
|
}
|
||
|
|
||
|
[Conditional("DEBUG")]
|
||
|
static void VerifyValue(object? value) {
|
||
|
if (value is null || value is DmdType || value is IList<DmdCustomAttributeTypedArgument>)
|
||
|
return;
|
||
|
switch (Type.GetTypeCode(value.GetType())) {
|
||
|
case TypeCode.Boolean:
|
||
|
case TypeCode.Char:
|
||
|
case TypeCode.SByte:
|
||
|
case TypeCode.Byte:
|
||
|
case TypeCode.Int16:
|
||
|
case TypeCode.UInt16:
|
||
|
case TypeCode.Int32:
|
||
|
case TypeCode.UInt32:
|
||
|
case TypeCode.Int64:
|
||
|
case TypeCode.UInt64:
|
||
|
case TypeCode.Single:
|
||
|
case TypeCode.Double:
|
||
|
case TypeCode.String:
|
||
|
return;
|
||
|
}
|
||
|
Debug.Fail("Invalid value");
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// ToString()
|
||
|
/// </summary>
|
||
|
/// <returns></returns>
|
||
|
public override string? ToString() => ToString(false);
|
||
|
|
||
|
internal string? ToString(bool typed) {
|
||
|
if (Value is null)
|
||
|
return typed ? "null" : "(" + ArgumentType?.Name + ")null";
|
||
|
if (ArgumentType.IsEnum)
|
||
|
return typed ? Value.ToString() : "(" + ArgumentType.FullName + ")" + Value.ToString();
|
||
|
if (Value is string s)
|
||
|
return "\"" + s + "\"";
|
||
|
if (Value is char c)
|
||
|
return "'" + c.ToString() + "'";
|
||
|
if (Value is DmdType type)
|
||
|
return "typeof(" + type.FullName + ")";
|
||
|
if (ArgumentType.IsArray) {
|
||
|
var list = (IList<DmdCustomAttributeTypedArgument>)Value;
|
||
|
var elementType = ArgumentType.GetElementType()!;
|
||
|
StringBuilder? sb = ObjectPools.AllocStringBuilder();
|
||
|
sb.Append("new ");
|
||
|
sb.Append(elementType.IsEnum ? elementType.FullName : elementType.Name);
|
||
|
sb.Append('[');
|
||
|
sb.Append(list.Count);
|
||
|
sb.Append("] { ");
|
||
|
for (int i = 0; i < list.Count; i++) {
|
||
|
if (i != 0)
|
||
|
sb.Append(", ");
|
||
|
sb.Append(list[i].ToString(elementType != elementType.AppDomain.System_Object));
|
||
|
}
|
||
|
sb.Append(" }");
|
||
|
return ObjectPools.FreeAndToString(ref sb);
|
||
|
}
|
||
|
return typed ? Value.ToString() : "(" + ArgumentType.Name + ")" + Value.ToString();
|
||
|
}
|
||
|
|
||
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||
|
public static bool operator ==(DmdCustomAttributeTypedArgument left, DmdCustomAttributeTypedArgument right) => left.Equals(right);
|
||
|
public static bool operator !=(DmdCustomAttributeTypedArgument left, DmdCustomAttributeTypedArgument right) => !left.Equals(right);
|
||
|
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
|
||
|
|
||
|
/// <summary>
|
||
|
/// Equals()
|
||
|
/// </summary>
|
||
|
/// <param name="other"></param>
|
||
|
/// <returns></returns>
|
||
|
public bool Equals(DmdCustomAttributeTypedArgument other) => DmdMemberInfoEqualityComparer.DefaultType.Equals(ArgumentType) && Equals(Value, other.Value);
|
||
|
|
||
|
/// <summary>
|
||
|
/// Equals()
|
||
|
/// </summary>
|
||
|
/// <param name="obj"></param>
|
||
|
/// <returns></returns>
|
||
|
public override bool Equals(object? obj) => obj is DmdCustomAttributeTypedArgument other && Equals(other);
|
||
|
|
||
|
/// <summary>
|
||
|
/// GetHashCode()
|
||
|
/// </summary>
|
||
|
/// <returns></returns>
|
||
|
public override int GetHashCode() => DmdMemberInfoEqualityComparer.DefaultType.GetHashCode(ArgumentType) ^ (Value?.GetHashCode() ?? 0);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Custom attribute named argument
|
||
|
/// </summary>
|
||
|
public readonly struct DmdCustomAttributeNamedArgument : IEquatable<DmdCustomAttributeNamedArgument> {
|
||
|
/// <summary>
|
||
|
/// Gets the member (a property or a field)
|
||
|
/// </summary>
|
||
|
public DmdMemberInfo MemberInfo { get; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the value
|
||
|
/// </summary>
|
||
|
public DmdCustomAttributeTypedArgument TypedValue { get; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the member name
|
||
|
/// </summary>
|
||
|
public string MemberName => MemberInfo.Name;
|
||
|
|
||
|
/// <summary>
|
||
|
/// true if it's a field, false if it's a property
|
||
|
/// </summary>
|
||
|
public bool IsField => MemberInfo.MemberType == DmdMemberTypes.Field;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Constructor
|
||
|
/// </summary>
|
||
|
/// <param name="memberInfo">A property or a field</param>
|
||
|
/// <param name="typedArgument"></param>
|
||
|
public DmdCustomAttributeNamedArgument(DmdMemberInfo? memberInfo, DmdCustomAttributeTypedArgument typedArgument) {
|
||
|
if (typedArgument.ArgumentType is null)
|
||
|
throw new ArgumentException();
|
||
|
MemberInfo = memberInfo ?? throw new ArgumentNullException(nameof(memberInfo));
|
||
|
TypedValue = typedArgument;
|
||
|
}
|
||
|
|
||
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||
|
public static bool operator ==(DmdCustomAttributeNamedArgument left, DmdCustomAttributeNamedArgument right) => left.Equals(right);
|
||
|
public static bool operator !=(DmdCustomAttributeNamedArgument left, DmdCustomAttributeNamedArgument right) => !left.Equals(right);
|
||
|
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
|
||
|
|
||
|
/// <summary>
|
||
|
/// Equals()
|
||
|
/// </summary>
|
||
|
/// <param name="other"></param>
|
||
|
/// <returns></returns>
|
||
|
public bool Equals(DmdCustomAttributeNamedArgument other) => MemberInfo == other.MemberInfo && TypedValue == other.TypedValue;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Equals()
|
||
|
/// </summary>
|
||
|
/// <param name="obj"></param>
|
||
|
/// <returns></returns>
|
||
|
public override bool Equals(object? obj) => obj is DmdCustomAttributeNamedArgument other && Equals(other);
|
||
|
|
||
|
/// <summary>
|
||
|
/// GetHashCode()
|
||
|
/// </summary>
|
||
|
/// <returns></returns>
|
||
|
public override int GetHashCode() => (MemberInfo?.GetHashCode() ?? 0) ^ TypedValue.GetHashCode();
|
||
|
|
||
|
/// <summary>
|
||
|
/// ToString()
|
||
|
/// </summary>
|
||
|
/// <returns></returns>
|
||
|
public override string? ToString() {
|
||
|
if (MemberInfo is null)
|
||
|
return base.ToString();
|
||
|
var argType = MemberInfo is DmdFieldInfo field ? field.FieldType : MemberInfo is DmdPropertyInfo property ? property.PropertyType : null;
|
||
|
return MemberInfo.Name + " = " + TypedValue.ToString(argType != MemberInfo.AppDomain.System_Object);
|
||
|
}
|
||
|
}
|
||
|
}
|