Fix null sessions in HttpContextWrapper.Session
[mono.git] / mcs / class / IKVM.Reflection / Binder.cs
1 /*
2   Copyright (C) 2010-2012 Jeroen Frijters
3
4   This software is provided 'as-is', without any express or implied
5   warranty.  In no event will the authors be held liable for any damages
6   arising from the use of this software.
7
8   Permission is granted to anyone to use this software for any purpose,
9   including commercial applications, and to alter it and redistribute it
10   freely, subject to the following restrictions:
11
12   1. The origin of this software must not be misrepresented; you must not
13      claim that you wrote the original software. If you use this software
14      in a product, an acknowledgment in the product documentation would be
15      appreciated but is not required.
16   2. Altered source versions must be plainly marked as such, and must not be
17      misrepresented as being the original software.
18   3. This notice may not be removed or altered from any source distribution.
19
20   Jeroen Frijters
21   jeroen@frijters.net
22   
23 */
24 using System;
25 using System.Globalization;
26
27 namespace IKVM.Reflection
28 {
29         public abstract class Binder
30         {
31                 protected Binder()
32                 {
33                 }
34
35                 public virtual MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state)
36                 {
37                         throw new InvalidOperationException();
38                 }
39
40                 public virtual FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture)
41                 {
42                         throw new InvalidOperationException();
43                 }
44
45                 public virtual object ChangeType(object value, Type type, CultureInfo culture)
46                 {
47                         throw new InvalidOperationException();
48                 }
49
50                 public virtual void ReorderArgumentArray(ref object[] args, object state)
51                 {
52                         throw new InvalidOperationException();
53                 }
54
55                 public abstract MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers);
56                 public abstract PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers);
57         }
58
59         sealed class DefaultBinder : Binder
60         {
61                 public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
62                 {
63                         int matchCount = 0;
64                         foreach (MethodBase method in match)
65                         {
66                                 if (MatchParameterTypes(method.GetParameters(), types))
67                                 {
68                                         match[matchCount++] = method;
69                                 }
70                         }
71
72                         if (matchCount == 0)
73                         {
74                                 return null;
75                         }
76
77                         if (matchCount == 1)
78                         {
79                                 return match[0];
80                         }
81
82                         MethodBase bestMatch = match[0];
83                         bool ambiguous = false;
84                         for (int i = 1; i < matchCount; i++)
85                         {
86                                 bestMatch = SelectBestMatch(bestMatch, match[i], types, ref ambiguous);
87                         }
88                         if (ambiguous)
89                         {
90                                 throw new AmbiguousMatchException();
91                         }
92                         return bestMatch;
93                 }
94
95                 private static bool MatchParameterTypes(ParameterInfo[] parameters, Type[] types)
96                 {
97                         if (parameters.Length != types.Length)
98                         {
99                                 return false;
100                         }
101                         for (int i = 0; i < parameters.Length; i++)
102                         {
103                                 Type sourceType = types[i];
104                                 Type targetType = parameters[i].ParameterType;
105                                 if (sourceType != targetType
106                                         && !targetType.IsAssignableFrom(sourceType)
107                                         && !IsAllowedPrimitiveConversion(sourceType, targetType))
108                                 {
109                                         return false;
110                                 }
111                         }
112                         return true;
113                 }
114
115                 private static MethodBase SelectBestMatch(MethodBase mb1, MethodBase mb2, Type[] types, ref bool ambiguous)
116                 {
117                         switch (MatchSignatures(mb1.MethodSignature, mb2.MethodSignature, types))
118                         {
119                                 case 1:
120                                         return mb1;
121                                 case 2:
122                                         return mb2;
123                         }
124
125                         if (mb1.MethodSignature.MatchParameterTypes(mb2.MethodSignature))
126                         {
127                                 int depth1 = GetInheritanceDepth(mb1.DeclaringType);
128                                 int depth2 = GetInheritanceDepth(mb2.DeclaringType);
129                                 if (depth1 > depth2)
130                                 {
131                                         return mb1;
132                                 }
133                                 else if (depth1 < depth2)
134                                 {
135                                         return mb2;
136                                 }
137                         }
138
139                         ambiguous = true;
140                         return mb1;
141                 }
142
143                 private static int GetInheritanceDepth(Type type)
144                 {
145                         int depth = 0;
146                         while (type != null)
147                         {
148                                 depth++;
149                                 type = type.BaseType;
150                         }
151                         return depth;
152                 }
153
154                 private static int MatchSignatures(MethodSignature sig1, MethodSignature sig2, Type[] types)
155                 {
156                         for (int i = 0; i < sig1.GetParameterCount(); i++)
157                         {
158                                 Type type1 = sig1.GetParameterType(i);
159                                 Type type2 = sig2.GetParameterType(i);
160                                 if (type1 != type2)
161                                 {
162                                         return MatchTypes(type1, type2, types[i]);
163                                 }
164                         }
165                         return 0;
166                 }
167
168                 private static int MatchSignatures(PropertySignature sig1, PropertySignature sig2, Type[] types)
169                 {
170                         for (int i = 0; i < sig1.ParameterCount; i++)
171                         {
172                                 Type type1 = sig1.GetParameter(i);
173                                 Type type2 = sig2.GetParameter(i);
174                                 if (type1 != type2)
175                                 {
176                                         return MatchTypes(type1, type2, types[i]);
177                                 }
178                         }
179                         return 0;
180                 }
181
182                 private static int MatchTypes(Type type1, Type type2, Type type)
183                 {
184                         if (type1 == type)
185                         {
186                                 return 1;
187                         }
188                         if (type2 == type)
189                         {
190                                 return 2;
191                         }
192                         bool conv = type1.IsAssignableFrom(type2);
193                         return conv == type2.IsAssignableFrom(type1) ? 0 : conv ? 2 : 1;
194                 }
195
196                 private static bool IsAllowedPrimitiveConversion(Type source, Type target)
197                 {
198                         // we need to check for primitives, because GetTypeCode will return the underlying type for enums
199                         if (!source.IsPrimitive || !target.IsPrimitive)
200                         {
201                                 return false;
202                         }
203                         TypeCode sourceType = Type.GetTypeCode(source);
204                         TypeCode targetType = Type.GetTypeCode(target);
205                         switch (sourceType)
206                         {
207                                 case TypeCode.Char:
208                                         switch (targetType)
209                                         {
210                                                 case TypeCode.UInt16:
211                                                 case TypeCode.UInt32:
212                                                 case TypeCode.Int32:
213                                                 case TypeCode.UInt64:
214                                                 case TypeCode.Int64:
215                                                 case TypeCode.Single:
216                                                 case TypeCode.Double:
217                                                         return true;
218                                                 default:
219                                                         return false;
220                                         }
221                                 case TypeCode.Byte:
222                                         switch (targetType)
223                                         {
224                                                 case TypeCode.Char:
225                                                 case TypeCode.UInt16:
226                                                 case TypeCode.Int16:
227                                                 case TypeCode.UInt32:
228                                                 case TypeCode.Int32:
229                                                 case TypeCode.UInt64:
230                                                 case TypeCode.Int64:
231                                                 case TypeCode.Single:
232                                                 case TypeCode.Double:
233                                                         return true;
234                                                 default:
235                                                         return false;
236                                         }
237                                 case TypeCode.SByte:
238                                         switch (targetType)
239                                         {
240                                                 case TypeCode.Int16:
241                                                 case TypeCode.Int32:
242                                                 case TypeCode.Int64:
243                                                 case TypeCode.Single:
244                                                 case TypeCode.Double:
245                                                         return true;
246                                                 default:
247                                                         return false;
248                                         }
249                                 case TypeCode.UInt16:
250                                         switch (targetType)
251                                         {
252                                                 case TypeCode.UInt32:
253                                                 case TypeCode.Int32:
254                                                 case TypeCode.UInt64:
255                                                 case TypeCode.Int64:
256                                                 case TypeCode.Single:
257                                                 case TypeCode.Double:
258                                                         return true;
259                                                 default:
260                                                         return false;
261                                         }
262                                 case TypeCode.Int16:
263                                         switch (targetType)
264                                         {
265                                                 case TypeCode.Int32:
266                                                 case TypeCode.Int64:
267                                                 case TypeCode.Single:
268                                                 case TypeCode.Double:
269                                                         return true;
270                                                 default:
271                                                         return false;
272                                         }
273                                 case TypeCode.UInt32:
274                                         switch (targetType)
275                                         {
276                                                 case TypeCode.UInt64:
277                                                 case TypeCode.Int64:
278                                                 case TypeCode.Single:
279                                                 case TypeCode.Double:
280                                                         return true;
281                                                 default:
282                                                         return false;
283                                         }
284                                 case TypeCode.Int32:
285                                         switch (targetType)
286                                         {
287                                                 case TypeCode.Int64:
288                                                 case TypeCode.Single:
289                                                 case TypeCode.Double:
290                                                         return true;
291                                                 default:
292                                                         return false;
293                                         }
294                                 case TypeCode.UInt64:
295                                         switch (targetType)
296                                         {
297                                                 case TypeCode.Single:
298                                                 case TypeCode.Double:
299                                                         return true;
300                                                 default:
301                                                         return false;
302                                         }
303                                 case TypeCode.Int64:
304                                         switch (targetType)
305                                         {
306                                                 case TypeCode.Single:
307                                                 case TypeCode.Double:
308                                                         return true;
309                                                 default:
310                                                         return false;
311                                         }
312                                 case TypeCode.Single:
313                                         switch (targetType)
314                                         {
315                                                 case TypeCode.Double:
316                                                         return true;
317                                                 default:
318                                                         return false;
319                                         }
320                                 default:
321                                         return false;
322                         }
323                 }
324
325                 public override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers)
326                 {
327                         int matchCount = 0;
328                         foreach (PropertyInfo property in match)
329                         {
330                                 if (indexes == null || MatchParameterTypes(property.GetIndexParameters(), indexes))
331                                 {
332                                         if (returnType != null)
333                                         {
334                                                 if (property.PropertyType.IsPrimitive)
335                                                 {
336                                                         if (!IsAllowedPrimitiveConversion(returnType, property.PropertyType))
337                                                         {
338                                                                 continue;
339                                                         }
340                                                 }
341                                                 else
342                                                 {
343                                                         if (!property.PropertyType.IsAssignableFrom(returnType))
344                                                         {
345                                                                 continue;
346                                                         }
347                                                 }
348                                         }
349                                         match[matchCount++] = property;
350                                 }
351                         }
352
353                         if (matchCount == 0)
354                         {
355                                 return null;
356                         }
357
358                         if (matchCount == 1)
359                         {
360                                 return match[0];
361                         }
362
363                         PropertyInfo bestMatch = match[0];
364                         bool ambiguous = false;
365                         for (int i = 1; i < matchCount; i++)
366                         {
367                                 int best = MatchTypes(bestMatch.PropertyType, match[i].PropertyType, returnType);
368                                 if (best == 0 && indexes != null)
369                                 {
370                                         best = MatchSignatures(bestMatch.PropertySignature, match[i].PropertySignature, indexes);
371                                 }
372                                 if (best == 0)
373                                 {
374                                         int depth1 = GetInheritanceDepth(bestMatch.DeclaringType);
375                                         int depth2 = GetInheritanceDepth(match[i].DeclaringType);
376                                         if (bestMatch.Name == match[i].Name && depth1 != depth2)
377                                         {
378                                                 if (depth1 > depth2)
379                                                 {
380                                                         best = 1;
381                                                 }
382                                                 else
383                                                 {
384                                                         best = 2;
385                                                 }
386                                         }
387                                         else
388                                         {
389                                                 ambiguous = true;
390                                         }
391                                 }
392                                 if (best == 2)
393                                 {
394                                         ambiguous = false;
395                                         bestMatch = match[i];
396                                 }
397                         }
398                         if (ambiguous)
399                         {
400                                 throw new AmbiguousMatchException();
401                         }
402                         return bestMatch;
403                 }
404         }
405 }