1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Collections; 6 using System.Collections.Generic; 7 using System.Collections.ObjectModel; 8 9 using Internal.Reflection.Tracing; 10 11 namespace System.Reflection.Runtime.CustomAttributes 12 { 13 // 14 // Common base class for the Runtime's implementation of CustomAttributeData. 15 // 16 internal abstract partial class RuntimeCustomAttributeData : RuntimeImplementedCustomAttributeData 17 { 18 public abstract override Type AttributeType { get; } 19 20 public abstract override ConstructorInfo Constructor { get; } 21 22 public sealed override IList<CustomAttributeTypedArgument> ConstructorArguments 23 { 24 get 25 { 26 #if ENABLE_REFLECTION_TRACE 27 if (ReflectionTrace.Enabled) 28 ReflectionTrace.CustomAttributeData_ConstructorArguments(this); 29 #endif 30 31 return new ReadOnlyCollection<CustomAttributeTypedArgument>(GetConstructorArguments(throwIfMissingMetadata: true)); 32 } 33 } 34 35 // Equals/GetHashCode no need to override (they just implement reference equality but desktop never unified these things.) 36 37 public sealed override IList<CustomAttributeNamedArgument> NamedArguments 38 { 39 get 40 { 41 #if ENABLE_REFLECTION_TRACE 42 if (ReflectionTrace.Enabled) 43 ReflectionTrace.CustomAttributeData_NamedArguments(this); 44 #endif 45 46 return new ReadOnlyCollection<CustomAttributeNamedArgument>(GetNamedArguments(throwIfMissingMetadata: true)); 47 } 48 } 49 ToString()50 public sealed override String ToString() 51 { 52 try 53 { 54 String ctorArgs = ""; 55 IList<CustomAttributeTypedArgument> constructorArguments = GetConstructorArguments(throwIfMissingMetadata: false); 56 if (constructorArguments == null) 57 return LastResortToString; 58 for (int i = 0; i < constructorArguments.Count; i++) 59 ctorArgs += String.Format(i == 0 ? "{0}" : ", {0}", ComputeTypedArgumentString(constructorArguments[i], typed: false)); 60 61 String namedArgs = ""; 62 IList<CustomAttributeNamedArgument> namedArguments = GetNamedArguments(throwIfMissingMetadata: false); 63 if (namedArguments == null) 64 return LastResortToString; 65 for (int i = 0; i < namedArguments.Count; i++) 66 { 67 CustomAttributeNamedArgument namedArgument = namedArguments[i]; 68 69 // Legacy: Desktop sets "typed" to "namedArgument.ArgumentType != typeof(Object)" - on Project N, this property is not available 70 // (nor conveniently computable as it's not captured in the Project N metadata.) The only consequence is that for 71 // the rare case of fields and properties typed "Object", we won't decorate the argument value with its actual type name. 72 bool typed = true; 73 namedArgs += String.Format( 74 i == 0 && ctorArgs.Length == 0 ? "{0} = {1}" : ", {0} = {1}", 75 namedArgument.MemberName, 76 ComputeTypedArgumentString(namedArgument.TypedValue, typed)); 77 } 78 79 return String.Format("[{0}({1}{2})]", AttributeTypeString, ctorArgs, namedArgs); 80 } 81 catch (MissingMetadataException) 82 { 83 return LastResortToString; 84 } 85 } 86 ResolveAttributeConstructor(Type attributeType, Type[] parameterTypes)87 protected static ConstructorInfo ResolveAttributeConstructor(Type attributeType, Type[] parameterTypes) 88 { 89 int parameterCount = parameterTypes.Length; 90 foreach (ConstructorInfo candidate in attributeType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) 91 { 92 ParameterInfo[] candidateParameters = candidate.GetParametersNoCopy(); 93 if (parameterCount != candidateParameters.Length) 94 continue; 95 96 for (int i = 0; i < parameterCount; i++) 97 { 98 if (!parameterTypes[i].Equals(candidateParameters[i])) 99 continue; 100 } 101 102 return candidate; 103 } 104 105 throw new MissingMethodException(); 106 } 107 108 internal abstract String AttributeTypeString { get; } 109 110 // 111 // If throwIfMissingMetadata is false, returns null rather than throwing a MissingMetadataException. 112 // GetConstructorArguments(bool throwIfMissingMetadata)113 internal abstract IList<CustomAttributeTypedArgument> GetConstructorArguments(bool throwIfMissingMetadata); 114 115 // 116 // If throwIfMissingMetadata is false, returns null rather than throwing a MissingMetadataException. 117 // GetNamedArguments(bool throwIfMissingMetadata)118 internal abstract IList<CustomAttributeNamedArgument> GetNamedArguments(bool throwIfMissingMetadata); 119 120 // 121 // Computes the ToString() value for a CustomAttributeTypedArgument struct. 122 // ComputeTypedArgumentString(CustomAttributeTypedArgument cat, bool typed)123 private static String ComputeTypedArgumentString(CustomAttributeTypedArgument cat, bool typed) 124 { 125 Type argumentType = cat.ArgumentType; 126 if (argumentType == null) 127 return cat.ToString(); 128 129 Object value = cat.Value; 130 if (argumentType.IsEnum) 131 return String.Format(typed ? "{0}" : "({1}){0}", value, argumentType.FullName); 132 133 if (value == null) 134 return String.Format(typed ? "null" : "({0})null", argumentType.Name); 135 136 if (argumentType.Equals(CommonRuntimeTypes.String)) 137 return String.Format("\"{0}\"", value); 138 139 if (argumentType.Equals(CommonRuntimeTypes.Char)) 140 return String.Format("'{0}'", value); 141 142 if (argumentType.Equals(CommonRuntimeTypes.Type)) 143 return String.Format("typeof({0})", ((Type)value).FullName); 144 145 else if (argumentType.IsArray) 146 { 147 String result = null; 148 IList<CustomAttributeTypedArgument> array = value as IList<CustomAttributeTypedArgument>; 149 150 Type elementType = argumentType.GetElementType(); 151 result = String.Format(@"new {0}[{1}] {{ ", elementType.IsEnum ? elementType.FullName : elementType.Name, array.Count); 152 153 for (int i = 0; i < array.Count; i++) 154 result += String.Format(i == 0 ? "{0}" : ", {0}", ComputeTypedArgumentString(array[i], elementType != CommonRuntimeTypes.Object)); 155 156 return result += " }"; 157 } 158 159 return String.Format(typed ? "{0}" : "({1}){0}", value, argumentType.Name); 160 } 161 162 private string LastResortToString 163 { 164 get 165 { 166 // This emulates Object.ToString() for consistency with prior .Net Native implementations. 167 return GetType().ToString(); 168 } 169 } 170 171 // 172 // Wrap a custom attribute argument (or an element of an array-typed custom attribute argument) in a CustomAttributeTypeArgument structure 173 // for insertion into a CustomAttributeData value. 174 // WrapInCustomAttributeTypedArgument(Object value, Type argumentType)175 protected CustomAttributeTypedArgument WrapInCustomAttributeTypedArgument(Object value, Type argumentType) 176 { 177 if (argumentType.Equals(CommonRuntimeTypes.Object)) 178 { 179 // If the declared attribute type is System.Object, we must report the type based on the runtime value. 180 if (value == null) 181 argumentType = CommonRuntimeTypes.String; // Why is null reported as System.String? Because that's what the desktop CLR does. 182 else if (value is Type) 183 argumentType = CommonRuntimeTypes.Type; // value.GetType() will not actually be System.Type - rather it will be some internal implementation type. We only want to report it as System.Type. 184 else 185 argumentType = value.GetType(); 186 } 187 188 // Handle the array case 189 if (value is IEnumerable enumerableValue && !(value is string)) 190 { 191 if (!argumentType.IsArray) 192 throw new BadImageFormatException(); 193 Type reportedElementType = argumentType.GetElementType(); 194 LowLevelListWithIList<CustomAttributeTypedArgument> elementTypedArguments = new LowLevelListWithIList<CustomAttributeTypedArgument>(); 195 foreach (Object elementValue in enumerableValue) 196 { 197 CustomAttributeTypedArgument elementTypedArgument = WrapInCustomAttributeTypedArgument(elementValue, reportedElementType); 198 elementTypedArguments.Add(elementTypedArgument); 199 } 200 return new CustomAttributeTypedArgument(argumentType, new ReadOnlyCollection<CustomAttributeTypedArgument>(elementTypedArguments)); 201 } 202 else 203 { 204 return new CustomAttributeTypedArgument(argumentType, value); 205 } 206 } 207 } 208 } 209