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;
6 using System.Text;
7 using System.Reflection;
8 using System.Diagnostics;
9 using System.Globalization;
10 using System.Collections.Generic;
11 using System.Runtime.CompilerServices;
12 using System.Reflection.Runtime.General;
13 using System.Reflection.Runtime.TypeInfos;
14 using System.Reflection.Runtime.MethodInfos;
15 using System.Reflection.Runtime.ParameterInfos;
16 using System.Reflection.Runtime.CustomAttributes;
17 
18 using Internal.Reflection.Core;
19 using Internal.Reflection.Core.Execution;
20 
21 using Internal.Reflection.Tracing;
22 
23 
24 namespace System.Reflection.Runtime.PropertyInfos
25 {
26     //
27     // The runtime's implementation of PropertyInfo's
28     //
29     [DebuggerDisplay("{_debugName}")]
30     internal abstract partial class RuntimePropertyInfo : PropertyInfo, ITraceableTypeMember
31     {
32         //
33         // propertyHandle - the "tkPropertyDef" that identifies the property.
34         // definingType   - the "tkTypeDef" that defined the field (this is where you get the metadata reader that created propertyHandle.)
35         // contextType    - the type that supplies the type context (i.e. substitutions for generic parameters.) Though you
36         //                  get your raw information from "definingType", you report "contextType" as your DeclaringType property.
37         //
38         //  For example:
39         //
40         //       typeof(Foo<>).GetTypeInfo().DeclaredMembers
41         //
42         //           The definingType and contextType are both Foo<>
43         //
44         //       typeof(Foo<int,String>).GetTypeInfo().DeclaredMembers
45         //
46         //          The definingType is "Foo<,>"
47         //          The contextType is "Foo<int,String>"
48         //
49         //  We don't report any DeclaredMembers for arrays or generic parameters so those don't apply.
50         //
RuntimePropertyInfo(RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType)51         protected RuntimePropertyInfo(RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType)
52         {
53             ContextTypeInfo = contextTypeInfo;
54             _reflectedType = reflectedType;
55         }
56 
57         public sealed override bool CanRead
58         {
59             get
60             {
61                 return Getter != null;
62             }
63         }
64 
65         public sealed override bool CanWrite
66         {
67             get
68             {
69                 return Setter != null;
70             }
71         }
72 
73         public sealed override Type DeclaringType
74         {
75             get
76             {
77 #if ENABLE_REFLECTION_TRACE
78                 if (ReflectionTrace.Enabled)
79                     ReflectionTrace.PropertyInfo_DeclaringType(this);
80 #endif
81 
82                 return ContextTypeInfo;
83             }
84         }
85 
GetIndexParameters()86         public sealed override ParameterInfo[] GetIndexParameters()
87         {
88             ParameterInfo[] indexParameters = _lazyIndexParameters;
89             if (indexParameters == null)
90             {
91                 bool useGetter = CanRead;
92                 RuntimeMethodInfo accessor = (useGetter ? Getter : Setter);
93                 RuntimeParameterInfo[] runtimeMethodParameterInfos = accessor.RuntimeParameters;
94                 int count = runtimeMethodParameterInfos.Length;
95                 if (!useGetter)
96                     count--;  // If we're taking the parameters off the setter, subtract one for the "value" parameter.
97                 if (count == 0)
98                 {
99                     _lazyIndexParameters = indexParameters = Array.Empty<ParameterInfo>();
100                 }
101                 else
102                 {
103                     indexParameters = new ParameterInfo[count];
104                     for (int i = 0; i < count; i++)
105                     {
106                         indexParameters[i] = RuntimePropertyIndexParameterInfo.GetRuntimePropertyIndexParameterInfo(this, runtimeMethodParameterInfos[i]);
107                     }
108                     _lazyIndexParameters = indexParameters;
109                 }
110             }
111 
112             int numParameters = indexParameters.Length;
113             if (numParameters == 0)
114                 return indexParameters;
115             ParameterInfo[] result = new ParameterInfo[numParameters];
116             for (int i = 0; i < numParameters; i++)
117             {
118                 result[i] = indexParameters[i];
119             }
120             return result;
121         }
122 
123         public sealed override MethodInfo GetMethod
124         {
125             get
126             {
127 #if ENABLE_REFLECTION_TRACE
128                 if (ReflectionTrace.Enabled)
129                     ReflectionTrace.PropertyInfo_GetMethod(this);
130 #endif
131 
132                 return Getter;
133             }
134         }
135 
GetOptionalCustomModifiers()136         public sealed override Type[] GetOptionalCustomModifiers() => PropertyTypeHandle.GetCustomModifiers(ContextTypeInfo.TypeContext, optional: true);
137 
GetRequiredCustomModifiers()138         public sealed override Type[] GetRequiredCustomModifiers() => PropertyTypeHandle.GetCustomModifiers(ContextTypeInfo.TypeContext, optional: false);
139 
GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)140         public sealed override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)
141         {
142 #if ENABLE_REFLECTION_TRACE
143             if (ReflectionTrace.Enabled)
144                 ReflectionTrace.PropertyInfo_GetValue(this, obj, index);
145 #endif
146             if (_lazyGetterInvoker == null)
147             {
148                 if (!CanRead)
149                     throw new ArgumentException();
150 
151                 _lazyGetterInvoker = Getter.GetUncachedMethodInvoker(Array.Empty<RuntimeTypeInfo>(), this);
152             }
153             if (index == null)
154                 index = Array.Empty<Object>();
155             return _lazyGetterInvoker.Invoke(obj, index, binder, invokeAttr, culture);
156         }
157 
HasSameMetadataDefinitionAs(MemberInfo other)158         public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other);
159 
160         public sealed override Module Module
161         {
162             get
163             {
164                 return DefiningTypeInfo.Module;
165             }
166         }
167 
168         public sealed override String Name
169         {
170             get
171             {
172 #if ENABLE_REFLECTION_TRACE
173                 if (ReflectionTrace.Enabled)
174                     ReflectionTrace.PropertyInfo_Name(this);
175 #endif
176 
177                 return MetadataName;
178             }
179         }
180 
181         public sealed override Type PropertyType
182         {
183             get
184             {
185 #if ENABLE_REFLECTION_TRACE
186                 if (ReflectionTrace.Enabled)
187                     ReflectionTrace.PropertyInfo_PropertyType(this);
188 #endif
189 
190                 TypeContext typeContext = ContextTypeInfo.TypeContext;
191                 return PropertyTypeHandle.Resolve(typeContext);
192             }
193         }
194 
195         public sealed override Type ReflectedType
196         {
197             get
198             {
199                 return _reflectedType;
200             }
201         }
202 
203         public sealed override MethodInfo SetMethod
204         {
205             get
206             {
207 #if ENABLE_REFLECTION_TRACE
208                 if (ReflectionTrace.Enabled)
209                     ReflectionTrace.PropertyInfo_SetMethod(this);
210 #endif
211 
212                 return Setter;
213             }
214         }
215 
SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)216         public sealed override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)
217         {
218 #if ENABLE_REFLECTION_TRACE
219             if (ReflectionTrace.Enabled)
220                 ReflectionTrace.PropertyInfo_SetValue(this, obj, value, index);
221 #endif
222             if (_lazySetterInvoker == null)
223             {
224                 if (!CanWrite)
225                     throw new ArgumentException();
226 
227                 _lazySetterInvoker = Setter.GetUncachedMethodInvoker(Array.Empty<RuntimeTypeInfo>(), this);
228             }
229             Object[] arguments;
230             if (index == null)
231             {
232                 arguments = new Object[] { value };
233             }
234             else
235             {
236                 arguments = new Object[index.Length + 1];
237                 for (int i = 0; i < index.Length; i++)
238                 {
239                     arguments[i] = index[i];
240                 }
241                 arguments[index.Length] = value;
242             }
243             _lazySetterInvoker.Invoke(obj, arguments, binder, invokeAttr, culture);
244         }
245 
ToString()246         public sealed override String ToString()
247         {
248             StringBuilder sb = new StringBuilder(30);
249 
250             TypeContext typeContext = ContextTypeInfo.TypeContext;
251             sb.Append(PropertyTypeHandle.FormatTypeName(typeContext));
252             sb.Append(' ');
253             sb.Append(this.Name);
254             ParameterInfo[] indexParameters = this.GetIndexParameters();
255             if (indexParameters.Length != 0)
256             {
257                 RuntimeParameterInfo[] indexRuntimeParameters = new RuntimeParameterInfo[indexParameters.Length];
258                 for (int i = 0; i < indexParameters.Length; i++)
259                     indexRuntimeParameters[i] = (RuntimeParameterInfo)(indexParameters[i]);
260                 sb.Append(" [");
261                 sb.Append(RuntimeMethodHelpers.ComputeParametersString(indexRuntimeParameters));
262                 sb.Append(']');
263             }
264 
265             return sb.ToString();
266         }
267 
268         String ITraceableTypeMember.MemberName
269         {
270             get
271             {
272                 return MetadataName;
273             }
274         }
275 
276         Type ITraceableTypeMember.ContainingType
277         {
278             get
279             {
280                 return ContextTypeInfo;
281             }
282         }
283 
284         private RuntimeNamedMethodInfo Getter
285         {
286             get
287             {
288                 RuntimeNamedMethodInfo getter = _lazyGetter;
289                 if (getter == null)
290                 {
291                     getter = GetPropertyMethod(PropertyMethodSemantics.Getter);
292 
293                     if (getter == null)
294                         getter = RuntimeDummyMethodInfo.Instance;
295 
296                     _lazyGetter = getter;
297                 }
298 
299                 return object.ReferenceEquals(getter, RuntimeDummyMethodInfo.Instance) ? null : getter;
300             }
301         }
302 
303         private RuntimeNamedMethodInfo Setter
304         {
305             get
306             {
307                 RuntimeNamedMethodInfo setter = _lazySetter;
308                 if (setter == null)
309                 {
310                     setter = GetPropertyMethod(PropertyMethodSemantics.Setter);
311 
312                     if (setter == null)
313                         setter = RuntimeDummyMethodInfo.Instance;
314 
315                     _lazySetter = setter;
316                 }
317 
318                 return object.ReferenceEquals(setter, RuntimeDummyMethodInfo.Instance) ? null : setter;
319             }
320         }
321 
WithDebugName()322         protected RuntimePropertyInfo WithDebugName()
323         {
324             bool populateDebugNames = DeveloperExperienceState.DeveloperExperienceModeEnabled;
325 #if DEBUG
326             populateDebugNames = true;
327 #endif
328             if (!populateDebugNames)
329                 return this;
330 
331             if (_debugName == null)
332             {
333                 _debugName = "Constructing..."; // Protect against any inadvertent reentrancy.
334                 _debugName = MetadataName;
335             }
336             return this;
337         }
338 
339         // Types that derive from RuntimePropertyInfo must implement the following public surface area members
340         public abstract override PropertyAttributes Attributes { get; }
341         public abstract override IEnumerable<CustomAttributeData> CustomAttributes { get; }
Equals(Object obj)342         public abstract override bool Equals(Object obj);
GetHashCode()343         public abstract override int GetHashCode();
344         public abstract override int MetadataToken { get; }
345 
GetConstantValue()346         public sealed override object GetConstantValue() => GetConstantValue(raw: false);
GetRawConstantValue()347         public sealed override object GetRawConstantValue() => GetConstantValue(raw: true);
348 
GetDefaultValueIfAny(bool raw, out object defaultValue)349         protected abstract bool GetDefaultValueIfAny(bool raw, out object defaultValue);
350 
351         /// <summary>
352         /// Return a qualified handle that can be used to get the type of the property.
353         /// </summary>
354         protected abstract QSignatureTypeHandle PropertyTypeHandle { get; }
355 
356         protected enum PropertyMethodSemantics
357         {
358             Getter,
359             Setter,
360         }
361 
362         /// <summary>
363         /// Override to return the Method that corresponds to the specified semantic.
364         /// Return null if a method of the appropriate semantic does not exist
365         /// </summary>
GetPropertyMethod(PropertyMethodSemantics whichMethod)366         protected abstract RuntimeNamedMethodInfo GetPropertyMethod(PropertyMethodSemantics whichMethod);
367 
368         /// <summary>
369         /// Override to provide the metadata based name of a property. (Different from the Name
370         /// property in that it does not go into the reflection trace logic.)
371         /// </summary>
372         protected abstract string MetadataName { get; }
373 
374         /// <summary>
375         /// Return the DefiningTypeInfo as a RuntimeTypeInfo (instead of as a format specific type info)
376         /// </summary>
377         protected abstract RuntimeTypeInfo DefiningTypeInfo { get; }
378 
379         protected readonly RuntimeTypeInfo ContextTypeInfo;
380         protected readonly RuntimeTypeInfo _reflectedType;
381 
GetConstantValue(bool raw)382         private object GetConstantValue(bool raw)
383         {
384 #if ENABLE_REFLECTION_TRACE
385             if (ReflectionTrace.Enabled)
386                 ReflectionTrace.PropertyInfo_GetConstantValue(this);
387 #endif
388 
389             object defaultValue;
390             if (!GetDefaultValueIfAny(raw, out defaultValue))
391             {
392                 throw new InvalidOperationException(SR.Arg_EnumLitValueNotFound);
393             }
394             return defaultValue;
395         }
396 
397         private volatile MethodInvoker _lazyGetterInvoker = null;
398         private volatile MethodInvoker _lazySetterInvoker = null;
399 
400         private volatile RuntimeNamedMethodInfo _lazyGetter;
401         private volatile RuntimeNamedMethodInfo _lazySetter;
402 
403         private volatile ParameterInfo[] _lazyIndexParameters;
404 
405         private String _debugName;
406     }
407 }
408 
409