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