11using System ;
2+ using System . Linq . Expressions ;
23using System . Reflection ;
34using System . Security . Permissions ;
45
@@ -12,6 +13,10 @@ internal class PropertyObject : ExtensionType
1213 private PropertyInfo info ;
1314 private MethodInfo getter ;
1415 private MethodInfo setter ;
16+ private bool getterCacheFailed ;
17+ private bool setterCacheFailed ;
18+ private Func < object , object > getterCache ;
19+ private Action < object , object > setterCache ;
1520
1621 [ StrongNameIdentityPermission ( SecurityAction . Assert ) ]
1722 public PropertyObject ( PropertyInfo md )
@@ -67,7 +72,21 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
6772
6873 try
6974 {
70- result = self . info . GetValue ( co . inst , null ) ;
75+ if ( self . getterCache == null && ! self . getterCacheFailed )
76+ {
77+ // if the getter is not public 'GetGetMethod' will not find it
78+ // but calling 'GetValue' will work, so for backwards compatibility
79+ // we will use it instead
80+ self . getterCache = BuildGetter ( self . info ) ;
81+ if ( self . getterCache == null )
82+ {
83+ self . getterCacheFailed = true ;
84+ }
85+ }
86+
87+ result = self . getterCacheFailed ?
88+ self . info . GetValue ( co . inst , null )
89+ : self . getterCache ( co . inst ) ;
7190 return Converter . ToPython ( result , self . info . PropertyType ) ;
7291 }
7392 catch ( Exception e )
@@ -81,7 +100,6 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
81100 }
82101 }
83102
84-
85103 /// <summary>
86104 /// Descriptor __set__ implementation. This method sets the value of
87105 /// a property based on the given Python value. The Python value must
@@ -132,7 +150,27 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
132150 Exceptions . RaiseTypeError ( "invalid target" ) ;
133151 return - 1 ;
134152 }
135- self . info . SetValue ( co . inst , newval , null ) ;
153+
154+ if ( self . setterCache == null && ! self . setterCacheFailed )
155+ {
156+ // if the setter is not public 'GetSetMethod' will not find it
157+ // but calling 'SetValue' will work, so for backwards compatibility
158+ // we will use it instead
159+ self . setterCache = BuildSetter ( self . info ) ;
160+ if ( self . setterCache == null )
161+ {
162+ self . setterCacheFailed = true ;
163+ }
164+ }
165+
166+ if ( self . setterCacheFailed )
167+ {
168+ self . info . SetValue ( co . inst , newval , null ) ;
169+ }
170+ else
171+ {
172+ self . setterCache ( co . inst , newval ) ;
173+ }
136174 }
137175 else
138176 {
@@ -160,5 +198,54 @@ public static IntPtr tp_repr(IntPtr ob)
160198 var self = ( PropertyObject ) GetManagedObject ( ob ) ;
161199 return Runtime . PyString_FromString ( $ "<property '{ self . info . Name } '>") ;
162200 }
201+
202+ private static Func < object , object > BuildGetter ( PropertyInfo propertyInfo )
203+ {
204+ var methodInfo = propertyInfo . GetGetMethod ( ) ;
205+ if ( methodInfo == null )
206+ {
207+ // if the getter is not public 'GetGetMethod' will not find it
208+ return null ;
209+ }
210+ var obj = Expression . Parameter ( typeof ( object ) , "o" ) ;
211+ // Require because we will know at runtime the declaring type
212+ // so 'obj' is declared as typeof(object)
213+ var instance = Expression . Convert ( obj , methodInfo . DeclaringType ) ;
214+
215+ var expressionCall = Expression . Call ( instance , methodInfo ) ;
216+
217+ return Expression . Lambda < Func < object , object > > (
218+ Expression . Convert ( expressionCall , typeof ( object ) ) ,
219+ obj ) . Compile ( ) ;
220+ }
221+
222+ private static Action < object , object > BuildSetter ( PropertyInfo propertyInfo )
223+ {
224+ var methodInfo = propertyInfo . GetSetMethod ( ) ;
225+ if ( methodInfo == null )
226+ {
227+ // if the setter is not public 'GetSetMethod' will not find it
228+ return null ;
229+ }
230+ var obj = Expression . Parameter ( typeof ( object ) , "o" ) ;
231+ // Require because we will know at runtime the declaring type
232+ // so 'obj' is declared as typeof(object)
233+ var instance = Expression . Convert ( obj , methodInfo . DeclaringType ) ;
234+
235+ var parameters = methodInfo . GetParameters ( ) ;
236+ if ( parameters . Length != 1 )
237+ {
238+ return null ;
239+ }
240+ var value = Expression . Parameter ( typeof ( object ) ) ;
241+ var argument = Expression . Convert ( value , parameters [ 0 ] . ParameterType ) ;
242+
243+ var expressionCall = Expression . Call ( instance , methodInfo , argument ) ;
244+
245+ return Expression . Lambda < Action < object , object > > (
246+ expressionCall ,
247+ obj ,
248+ value ) . Compile ( ) ;
249+ }
163250 }
164251}
0 commit comments