1
1
using System ;
2
+ using System . Linq . Expressions ;
2
3
using System . Reflection ;
3
4
using System . Security . Permissions ;
4
5
@@ -12,6 +13,10 @@ internal class PropertyObject : ExtensionType
12
13
private PropertyInfo info ;
13
14
private MethodInfo getter ;
14
15
private MethodInfo setter ;
16
+ private bool getterCacheFailed ;
17
+ private bool setterCacheFailed ;
18
+ private Func < object , object > getterCache ;
19
+ private Action < object , object > setterCache ;
15
20
16
21
[ StrongNameIdentityPermission ( SecurityAction . Assert ) ]
17
22
public PropertyObject ( PropertyInfo md )
@@ -67,7 +72,21 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
67
72
68
73
try
69
74
{
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 ) ;
71
90
return Converter . ToPython ( result , self . info . PropertyType ) ;
72
91
}
73
92
catch ( Exception e )
@@ -81,7 +100,6 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
81
100
}
82
101
}
83
102
84
-
85
103
/// <summary>
86
104
/// Descriptor __set__ implementation. This method sets the value of
87
105
/// 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)
132
150
Exceptions . RaiseTypeError ( "invalid target" ) ;
133
151
return - 1 ;
134
152
}
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
+ }
136
174
}
137
175
else
138
176
{
@@ -160,5 +198,54 @@ public static IntPtr tp_repr(IntPtr ob)
160
198
var self = ( PropertyObject ) GetManagedObject ( ob ) ;
161
199
return Runtime . PyString_FromString ( $ "<property '{ self . info . Name } '>") ;
162
200
}
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
+ }
163
250
}
164
251
}
0 commit comments