Binder.cs
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license.
4 //
5 // Microsoft Bot Framework: http://botframework.com
6 //
7 // Bot Builder SDK GitHub:
8 // https://github.com/Microsoft/BotBuilder
9 //
10 // Copyright (c) Microsoft Corporation
11 // All rights reserved.
12 //
13 // MIT License:
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 
34 using System;
35 using System.Collections.Generic;
36 using System.Linq;
37 using System.Reflection;
38 using System.Runtime.CompilerServices;
39 using System.Text;
40 using System.Threading;
41 using System.Threading.Tasks;
43 
44 namespace Microsoft.Bot.Builder.Scorables.Internals
45 {
46  public interface IBinder
47  {
48  bool TryBind(MethodBase method, IResolver resolver, out IBinding binding);
49  bool TryBind(Delegate lambda, IResolver resolver, out IBinding binding);
50  bool TryBind<R>(MethodInfo method, IResolver resolver, out IBinding<R> binding);
51  bool TryBind<R>(Delegate lambda, IResolver resolver, out IBinding<R> binding);
52  }
53 
54  public static partial class Extensions
55  {
56  private static readonly ConditionalWeakTable<MethodBase, IReadOnlyList<ParameterInfo>> ParametersByMethod =
57  new ConditionalWeakTable<MethodBase, IReadOnlyList<ParameterInfo>>();
58  public static IReadOnlyList<ParameterInfo> CachedParameters(this MethodBase method)
59  {
60  return ParametersByMethod.GetValue(method, m => m.GetParameters());
61  }
62 
63  private static readonly ConditionalWeakTable<MethodBase, IReadOnlyList<Type>> ParameterTypesByMethod =
64  new ConditionalWeakTable<MethodBase, IReadOnlyList<Type>>();
65  public static IReadOnlyList<Type> CachedParameterTypes(this MethodBase method)
66  {
67  return ParameterTypesByMethod.GetValue(method, m => m.GetParameters().ToList(p => p.ParameterType));
68  }
69  }
70 
71  public sealed class Binder : IBinder
72  {
73  public static readonly IBinder Instance = new Binder();
74  private Binder()
75  {
76  }
77 
78  public static bool TryResolveReturnType<R>(MethodInfo method)
79  {
80  var type = method.ReturnType;
81  if (typeof(R).IsAssignableFrom(type))
82  {
83  return true;
84  }
85 
86  if (type.IsGenericType)
87  {
88  var definition = type.GetGenericTypeDefinition();
89  if (definition == typeof(Task<>))
90  {
91  var arguments = type.GetGenericArguments();
92  if (typeof(R).IsAssignableFrom(arguments[0]))
93  {
94  return true;
95  }
96  }
97  }
98 
99  return false;
100  }
101 
102  public static bool TryResolveInstance(IResolver resolver, MethodBase method, object target, out object instance)
103  {
104  if (target != null)
105  {
106  var type = target.GetType();
107  if (method.DeclaringType.IsAssignableFrom(type))
108  {
109  instance = target;
110  return true;
111  }
112  }
113 
114  if (method.IsStatic)
115  {
116  instance = null;
117  return true;
118  }
119 
120  return resolver.TryResolve(method.DeclaringType, null, out instance);
121  }
122 
123  public static bool TryResolveArgument(IResolver resolver, ParameterInfo parameter, out object argument)
124  {
125  var type = parameter.ParameterType;
126 
127  var entity = parameter.GetCustomAttribute<EntityAttribute>();
128  if (entity != null)
129  {
130  if (resolver.TryResolve(type, entity.Name, out argument))
131  {
132  return true;
133  }
134  }
135 
136  if (resolver.TryResolve(type, parameter.Name, out argument))
137  {
138  return true;
139  }
140 
141  return resolver.TryResolve(type, null, out argument);
142  }
143 
144  public static bool TryResolveArguments(IResolver resolver, MethodBase method, out object[] arguments)
145  {
146  var parameters = method.CachedParameters();
147  if (parameters.Count == 0)
148  {
149  arguments = Array.Empty<object>();
150  return true;
151  }
152 
153  arguments = null;
154  for (int index = 0; index < parameters.Count; ++index)
155  {
156  var parameter = parameters[index];
157 
158  object argument;
159 
160  if (!TryResolveArgument(resolver, parameter, out argument))
161  {
162  arguments = null;
163  return false;
164  }
165 
166  if (arguments == null)
167  {
168  arguments = new object[parameters.Count];
169  }
170 
171  arguments[index] = argument;
172  }
173 
174  return arguments != null;
175  }
176 
177  public static bool TryBind(MethodBase method, object target, IResolver resolver, out IBinding binding)
178  {
179  object instance;
180  if (!TryResolveInstance(resolver, method, target, out instance))
181  {
182  binding = null;
183  return false;
184  }
185 
186  object[] arguments;
187  if (!TryResolveArguments(resolver, method, out arguments))
188  {
189  binding = null;
190  return false;
191  }
192 
193  binding = new Binding(method, instance, arguments);
194  return true;
195  }
196 
197  public static bool TryBind<R>(MethodInfo method, object target, IResolver resolver, out IBinding<R> binding)
198  {
199  if (!TryResolveReturnType<R>(method))
200  {
201  binding = null;
202  return false;
203  }
204 
205  object instance;
206  if (!TryResolveInstance(resolver, method, target, out instance))
207  {
208  binding = null;
209  return false;
210  }
211 
212  object[] arguments;
213  if (!TryResolveArguments(resolver, method, out arguments))
214  {
215  binding = null;
216  return false;
217  }
218 
219  binding = new Binding<R>(method, instance, arguments);
220  return true;
221  }
222 
223  bool IBinder.TryBind(MethodBase method, IResolver resolver, out IBinding binding)
224  {
225  return TryBind(method, null, resolver, out binding);
226  }
227 
228  bool IBinder.TryBind(Delegate lambda, IResolver resolver, out IBinding binding)
229  {
230  return TryBind(lambda.Method, lambda.Target, resolver, out binding);
231  }
232 
233  bool IBinder.TryBind<R>(MethodInfo method, IResolver resolver, out IBinding<R> binding)
234  {
235  return TryBind(method, null, resolver, out binding);
236  }
237 
238  bool IBinder.TryBind<R>(Delegate lambda, IResolver resolver, out IBinding<R> binding)
239  {
240  return TryBind(lambda.Method, lambda.Target, resolver, out binding);
241  }
242  }
243 }
bool TryBind(MethodBase method, IResolver resolver, out IBinding binding)
static bool TryResolveInstance(IResolver resolver, MethodBase method, object target, out object instance)
Definition: Binder.cs:102
static bool TryBind(MethodBase method, object target, IResolver resolver, out IBinding binding)
Definition: Binder.cs:177
static bool TryResolveArguments(IResolver resolver, MethodBase method, out object[] arguments)
Definition: Binder.cs:144
Allow the resolution of values based on type and optionally tag.
Namespace for internal machinery that is not useful for most developers and may change in the future...
Represents a binding of arguments to a method&#39;s parameters.
Definition: Binding.cs:47
static IReadOnlyList< Type > CachedParameterTypes(this MethodBase method)
Definition: Binder.cs:65
static bool TryResolveArgument(IResolver resolver, ParameterInfo parameter, out object argument)
Definition: Binder.cs:123
This attribute is used to specify that a method parameter is bound to an entity that can be resolved ...
bool TryResolve(Type type, object tag, out object value)
Namespace for the internal fibers machinery that is not useful for most developers and may change in ...
static IReadOnlyList< ParameterInfo > CachedParameters(this MethodBase method)
Definition: Binder.cs:58
Root namespace for the Microsoft Bot Builder SDK.