/* 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 . */ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; namespace dnSpy.Debugger.DotNet.Metadata { /// /// Custom attribute data /// public sealed class DmdCustomAttributeData { /// /// Gets the custom attribute type /// public DmdType AttributeType => Constructor.DeclaringType!; /// /// Gets the custom attribute constructor /// public DmdConstructorInfo Constructor { get; } /// /// Gets the constructor arguments /// public IList ConstructorArguments { get; } /// /// Gets all named arguments (properties and fields) /// public IList NamedArguments { get; } /// /// true if this custom attribute was not part of the #Blob but created from some other info /// public bool IsPseudoCustomAttribute { get; } /// /// Constructor /// /// Custom attribute constructor /// Constructor arguments or null /// Custom attribute named arguments (fields and properties) or null /// true if this custom attribute was not part of the #Blob but created from some other info public DmdCustomAttributeData(DmdConstructorInfo constructor, IList? constructorArguments, IList? namedArguments, bool isPseudoCustomAttribute) { Constructor = constructor ?? throw new ArgumentNullException(nameof(constructor)); ConstructorArguments = ReadOnlyCollectionHelpers.Create(constructorArguments); NamedArguments = ReadOnlyCollectionHelpers.Create(namedArguments); IsPseudoCustomAttribute = isPseudoCustomAttribute; } /// /// ToString() /// /// 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); } } /// /// Custom attribute typed argument /// public readonly struct DmdCustomAttributeTypedArgument : IEquatable { /// /// Gets the argument type /// public DmdType ArgumentType { get; } /// /// Gets the argument value /// public object? Value { get; } /// /// Constructor /// /// Argument type /// Argument value 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) 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"); } /// /// ToString() /// /// 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)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 /// /// Equals() /// /// /// public bool Equals(DmdCustomAttributeTypedArgument other) => DmdMemberInfoEqualityComparer.DefaultType.Equals(ArgumentType) && Equals(Value, other.Value); /// /// Equals() /// /// /// public override bool Equals(object? obj) => obj is DmdCustomAttributeTypedArgument other && Equals(other); /// /// GetHashCode() /// /// public override int GetHashCode() => DmdMemberInfoEqualityComparer.DefaultType.GetHashCode(ArgumentType) ^ (Value?.GetHashCode() ?? 0); } /// /// Custom attribute named argument /// public readonly struct DmdCustomAttributeNamedArgument : IEquatable { /// /// Gets the member (a property or a field) /// public DmdMemberInfo MemberInfo { get; } /// /// Gets the value /// public DmdCustomAttributeTypedArgument TypedValue { get; } /// /// Gets the member name /// public string MemberName => MemberInfo.Name; /// /// true if it's a field, false if it's a property /// public bool IsField => MemberInfo.MemberType == DmdMemberTypes.Field; /// /// Constructor /// /// A property or a field /// 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 /// /// Equals() /// /// /// public bool Equals(DmdCustomAttributeNamedArgument other) => MemberInfo == other.MemberInfo && TypedValue == other.TypedValue; /// /// Equals() /// /// /// public override bool Equals(object? obj) => obj is DmdCustomAttributeNamedArgument other && Equals(other); /// /// GetHashCode() /// /// public override int GetHashCode() => (MemberInfo?.GetHashCode() ?? 0) ^ TypedValue.GetHashCode(); /// /// ToString() /// /// 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); } } }