Merge pull request #1659 from alexanderkyte/stringbuilder-referencesource
[mono.git] / mcs / class / corlib / System.Reflection / MonoProperty.cs
1 //
2 // MonoProperty.cs: The class used to represent Properties from the mono runtime.
3 //
4 // Authors:
5 //   Paolo Molaro (lupus@ximian.com)
6 //   Patrik Torstensson (patrik.torstensson@labs2.com)
7 //   Marek Safar (marek.safar@gmail.com)
8 //
9 // (C) 2001 Ximian, Inc.  http://www.ximian.com
10 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
11 // Copyright 2013 Xamarin, Inc (http://www.xamarin.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System.Collections.Generic;
34 using System.Globalization;
35 using System.Runtime.CompilerServices;
36 using System.Runtime.InteropServices;
37 using System.Runtime.Serialization;
38 using System.Security;
39 using System.Text;
40 using System.Diagnostics.Contracts;
41
42 namespace System.Reflection {
43         
44         internal struct MonoPropertyInfo {
45                 public Type parent;
46                 public Type declaring_type;
47                 public String name;
48                 public MethodInfo get_method;
49                 public MethodInfo set_method;
50                 public PropertyAttributes attrs;
51                 
52                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
53                 internal static extern void get_property_info (MonoProperty prop, ref MonoPropertyInfo info,
54                                                                PInfo req_info);
55
56                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
57                 internal static extern Type[] GetTypeModifiers (MonoProperty prop, bool optional);
58
59                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
60                 internal static extern object get_default_value (MonoProperty prop);
61         }
62
63         [Flags]
64         internal enum PInfo {
65                 Attributes = 1,
66                 GetMethod  = 1 << 1,
67                 SetMethod  = 1 << 2,
68                 ReflectedType = 1 << 3,
69                 DeclaringType = 1 << 4,
70                 Name = 1 << 5
71                 
72         }
73
74         internal delegate object GetterAdapter (object _this);
75         internal delegate R Getter<T,R> (T _this);
76
77         abstract class RuntimePropertyInfo : PropertyInfo, ISerializable
78         {
79                 internal BindingFlags BindingFlags {
80                         get {
81                                 return 0;
82                         }
83                 }
84
85                 RuntimeType ReflectedTypeInternal {
86                         get {
87                                 return (RuntimeType) ReflectedType;
88                         }
89                 }
90
91         #region Object Overrides
92         public override String ToString()
93         {
94             return FormatNameAndSig(false);
95         }
96
97         private string FormatNameAndSig(bool serialization)
98         {
99             StringBuilder sbName = new StringBuilder(PropertyType.FormatTypeName(serialization));
100
101             sbName.Append(" ");
102             sbName.Append(Name);
103
104                         var pi = GetIndexParameters ();
105                         if (pi.Length > 0) {
106                                 sbName.Append (" [");
107                                 ParameterInfo.FormatParameters (sbName, pi, 0, serialization);
108                                 sbName.Append ("]");
109                         }
110
111             return sbName.ToString();
112         }
113         #endregion              
114
115         #region ISerializable Implementation
116         public void GetObjectData(SerializationInfo info, StreamingContext context)
117         {
118             if (info == null)
119                 throw new ArgumentNullException("info");
120             Contract.EndContractBlock();
121
122             MemberInfoSerializationHolder.GetSerializationInfo(
123                 info,
124                 Name,
125                 ReflectedTypeInternal,
126                 ToString(),
127                 SerializationToString(),
128                 MemberTypes.Property,
129                 null);
130         }
131
132         internal string SerializationToString()
133         {
134             return FormatNameAndSig(true);
135         }
136         #endregion
137         }
138
139         [Serializable]
140         [StructLayout (LayoutKind.Sequential)]
141         internal class MonoProperty : RuntimePropertyInfo {
142 #pragma warning disable 649
143                 internal IntPtr klass;
144                 internal IntPtr prop;
145                 MonoPropertyInfo info;
146                 PInfo cached;
147                 GetterAdapter cached_getter;
148
149 #pragma warning restore 649
150
151                 void CachePropertyInfo (PInfo flags)
152                 {
153                         if ((cached & flags) != flags) {
154                                 MonoPropertyInfo.get_property_info (this, ref info, flags);
155                                 cached |= flags;
156                         }
157                 }
158                 
159                 public override PropertyAttributes Attributes {
160                         get {
161                                 CachePropertyInfo (PInfo.Attributes);
162                                 return info.attrs;
163                         }
164                 }
165                 
166                 public override bool CanRead {
167                         get {
168                                 CachePropertyInfo (PInfo.GetMethod);
169                                 return (info.get_method != null);
170                         }
171                 }
172                 
173                 public override bool CanWrite {
174                         get {
175                                 CachePropertyInfo (PInfo.SetMethod);
176                                 return (info.set_method != null);
177                         }
178                 }
179
180                 public override Type PropertyType {
181                         get {
182                                 CachePropertyInfo (PInfo.GetMethod | PInfo.SetMethod);
183
184                                 if (info.get_method != null) {
185                                         return info.get_method.ReturnType;
186                                 } else {
187                                         ParameterInfo[] parameters = info.set_method.GetParametersInternal ();
188                                         
189                                         return parameters [parameters.Length - 1].ParameterType;
190                                 }
191                         }
192                 }
193
194                 public override Type ReflectedType {
195                         get {
196                                 CachePropertyInfo (PInfo.ReflectedType);
197                                 return info.parent;
198                         }
199                 }
200                 
201                 public override Type DeclaringType {
202                         get {
203                                 CachePropertyInfo (PInfo.DeclaringType);
204                                 return info.declaring_type;
205                         }
206                 }
207                 
208                 public override string Name {
209                         get {
210                                 CachePropertyInfo (PInfo.Name);
211                                 return info.name;
212                         }
213                 }
214
215                 public override MethodInfo[] GetAccessors (bool nonPublic)
216                 {
217                         int nget = 0;
218                         int nset = 0;
219                         
220                         CachePropertyInfo (PInfo.GetMethod | PInfo.SetMethod);
221
222                         if (info.set_method != null && (nonPublic || info.set_method.IsPublic))
223                                 nset = 1;
224                         if (info.get_method != null && (nonPublic || info.get_method.IsPublic))
225                                 nget = 1;
226
227                         MethodInfo[] res = new MethodInfo [nget + nset];
228                         int n = 0;
229                         if (nset != 0)
230                                 res [n++] = info.set_method;
231                         if (nget != 0)
232                                 res [n++] = info.get_method;
233                         return res;
234                 }
235
236                 public override MethodInfo GetGetMethod (bool nonPublic)
237                 {
238                         CachePropertyInfo (PInfo.GetMethod);
239                         if (info.get_method != null && (nonPublic || info.get_method.IsPublic))
240                                 return info.get_method;
241                         else
242                                 return null;
243                 }
244
245                 public override ParameterInfo[] GetIndexParameters ()
246                 {
247                         CachePropertyInfo (PInfo.GetMethod | PInfo.SetMethod);
248                         ParameterInfo[] src;
249                         int length;
250                         if (info.get_method != null) {
251                                 src = info.get_method.GetParametersInternal ();
252                                 length = src.Length;
253                         } else if (info.set_method != null) {
254                                 src = info.set_method.GetParametersInternal ();
255                                 length = src.Length - 1;
256                         } else
257                                 return EmptyArray<ParameterInfo>.Value;
258
259                         var dest = new ParameterInfo [length];
260                         for (int i = 0; i < length; ++i) {
261                                 dest [i] = ParameterInfo.New (src [i], this);
262                         }
263                         return dest;    
264                 }
265                 
266                 public override MethodInfo GetSetMethod (bool nonPublic)
267                 {
268                         CachePropertyInfo (PInfo.SetMethod);
269                         if (info.set_method != null && (nonPublic || info.set_method.IsPublic))
270                                 return info.set_method;
271                         else
272                                 return null;
273                 }
274
275
276                 /*TODO verify for attribute based default values, just like ParameterInfo*/
277                 public override object GetConstantValue ()
278                 {
279                         return MonoPropertyInfo.get_default_value (this);
280                 }
281
282                 public override object GetRawConstantValue() {
283                         return MonoPropertyInfo.get_default_value (this);
284                 }
285
286                 // According to MSDN the inherit parameter is ignored here and
287                 // the behavior always defaults to inherit = false
288                 //
289                 public override bool IsDefined (Type attributeType, bool inherit)
290                 {
291                         return MonoCustomAttrs.IsDefined (this, attributeType, false);
292                 }
293
294                 public override object[] GetCustomAttributes (bool inherit)
295                 {
296                         return MonoCustomAttrs.GetCustomAttributes (this, false);
297                 }
298                 
299                 public override object[] GetCustomAttributes (Type attributeType, bool inherit)
300                 {
301                         return MonoCustomAttrs.GetCustomAttributes (this, attributeType, false);
302                 }
303
304
305                 delegate object GetterAdapter (object _this);
306                 delegate R Getter<T,R> (T _this);
307                 delegate R StaticGetter<R> ();
308
309 #pragma warning disable 169
310                 // Used via reflection
311                 static object GetterAdapterFrame<T,R> (Getter<T,R> getter, object obj)
312                 {
313                         return getter ((T)obj);
314                 }
315
316                 static object StaticGetterAdapterFrame<R> (StaticGetter<R> getter, object obj)
317                 {
318                         return getter ();
319                 }
320 #pragma warning restore 169
321
322                 /*
323                  * The idea behing this optimization is to use a pair of delegates to simulate the same effect of doing a reflection call.
324                  * The first delegate cast the this argument to the right type and the second does points to the target method.
325                  */
326                 static GetterAdapter CreateGetterDelegate (MethodInfo method)
327                 {
328                         Type[] typeVector;
329                         Type getterType;
330                         object getterDelegate;
331                         MethodInfo adapterFrame;
332                         Type getterDelegateType;
333                         string frameName;
334
335                         if (method.IsStatic) {
336                                 typeVector = new Type[] { method.ReturnType };
337                                 getterDelegateType = typeof (StaticGetter<>);
338                                 frameName = "StaticGetterAdapterFrame";
339                         } else {
340                                 typeVector = new Type[] { method.DeclaringType, method.ReturnType };
341                                 getterDelegateType = typeof (Getter<,>);
342                                 frameName = "GetterAdapterFrame";
343                         }
344
345                         getterType = getterDelegateType.MakeGenericType (typeVector);
346 #if NET_2_1
347                         // with Silverlight a coreclr failure (e.g. Transparent caller creating a delegate on a Critical method)
348                         // would normally throw an ArgumentException, so we set throwOnBindFailure to false and check for a null
349                         // delegate that we can transform into a MethodAccessException
350                         getterDelegate = Delegate.CreateDelegate (getterType, method, false);
351                         if (getterDelegate == null)
352                                 throw new MethodAccessException ();
353 #else
354                         getterDelegate = Delegate.CreateDelegate (getterType, method);
355 #endif
356                         adapterFrame = typeof (MonoProperty).GetMethod (frameName, BindingFlags.Static | BindingFlags.NonPublic);
357                         adapterFrame = adapterFrame.MakeGenericMethod (typeVector);
358                         return (GetterAdapter)Delegate.CreateDelegate (typeof (GetterAdapter), getterDelegate, adapterFrame, true);
359                 }
360                         
361                 public override object GetValue (object obj, object[] index)
362                 {
363                         if (index == null || index.Length == 0) {
364                                 /*FIXME we should check if the number of arguments matches the expected one, otherwise the error message will be pretty criptic.*/
365 #if !FULL_AOT_RUNTIME
366                                 if (cached_getter == null) {
367                                         MethodInfo method = GetGetMethod (true);
368                                         if (!DeclaringType.IsValueType && !method.ContainsGenericParameters) { //FIXME find a way to build an invoke delegate for value types.
369                                                 if (method == null)
370                                                         throw new ArgumentException ("Get Method not found for '" + Name + "'");
371                                                 cached_getter = CreateGetterDelegate (method);
372                                                 // The try-catch preserves the .Invoke () behaviour
373                                                 try {
374                                                         return cached_getter (obj);
375                                                 } catch (Exception ex) {
376                                                         throw new TargetInvocationException (ex);
377                                                 }
378                                         }
379                                 } else {
380                                         try {
381                                                 return cached_getter (obj);
382                                         } catch (Exception ex) {
383                                                 throw new TargetInvocationException (ex);
384                                         }
385                                 }
386 #endif
387                         }
388
389                         return GetValue (obj, BindingFlags.Default, null, index, null);
390                 }
391
392                 public override object GetValue (object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)
393                 {
394                         object ret = null;
395
396                         MethodInfo method = GetGetMethod (true);
397                         if (method == null)
398                                 throw new ArgumentException ("Get Method not found for '" + Name + "'");
399
400                         try {
401                                 if (index == null || index.Length == 0) 
402                                         ret = method.Invoke (obj, invokeAttr, binder, null, culture);
403                                 else
404                                         ret = method.Invoke (obj, invokeAttr, binder, index, culture);
405                         }
406                         catch (SecurityException se) {
407                                 throw new TargetInvocationException (se);
408                         }
409
410                         return ret;
411                 }
412
413                 public override void SetValue (object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)
414                 {
415                         MethodInfo method = GetSetMethod (true);
416                         if (method == null)
417                                 throw new ArgumentException ("Set Method not found for '" + Name + "'");
418                         
419                         object [] parms;
420                         if (index == null || index.Length == 0) 
421                                 parms = new object [] {value};
422                         else {
423                                 int ilen = index.Length;
424                                 parms = new object [ilen+ 1];
425                                 index.CopyTo (parms, 0);
426                                 parms [ilen] = value;
427                         }
428
429                         method.Invoke (obj, invokeAttr, binder, parms, culture);
430                 }
431
432                 public override Type[] GetOptionalCustomModifiers () {
433                         Type[] types = MonoPropertyInfo.GetTypeModifiers (this, true);
434                         if (types == null)
435                                 return Type.EmptyTypes;
436                         return types;
437                 }
438
439                 public override Type[] GetRequiredCustomModifiers () {
440                         Type[] types = MonoPropertyInfo.GetTypeModifiers (this, false);
441                         if (types == null)
442                                 return Type.EmptyTypes;
443                         return types;
444                 }
445
446                 public override IList<CustomAttributeData> GetCustomAttributesData () {
447                         return CustomAttributeData.GetCustomAttributes (this);
448                 }
449         }
450 }