PageRenderTime 205ms CodeModel.GetById 100ms app.highlight 15ms RepoModel.GetById 86ms app.codeStats 0ms

/Microsoft.Scripting/Actions/DefaultBinder.Invoke.cs

https://bitbucket.org/stefanrusek/xronos
C# | 309 lines | 199 code | 42 blank | 68 comment | 23 complexity | 4e9802fd3599dfafdfdba0d952596a97 MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Microsoft Public License. A 
  6 * copy of the license can be found in the License.html file at the root of this distribution. If 
  7 * you cannot locate the  Microsoft Public License, please send an email to 
  8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
  9 * by the terms of the Microsoft Public License.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15
 16#if CODEPLEX_40
 17using System;
 18#else
 19using System; using Microsoft;
 20#endif
 21using System.Collections.Generic;
 22using System.Diagnostics;
 23#if CODEPLEX_40
 24using System.Linq.Expressions;
 25#else
 26using Microsoft.Linq.Expressions;
 27#endif
 28using System.Reflection;
 29#if CODEPLEX_40
 30using System.Dynamic;
 31#else
 32using Microsoft.Scripting;
 33#endif
 34using Microsoft.Scripting.Generation;
 35using Microsoft.Scripting.Runtime;
 36using Microsoft.Scripting.Utils;
 37using Microsoft.Scripting.Actions.Calls;
 38using AstUtils = Microsoft.Scripting.Ast.Utils;
 39
 40namespace Microsoft.Scripting.Actions {
 41#if CODEPLEX_40
 42    using Ast = System.Linq.Expressions.Expression;
 43#else
 44    using Ast = Microsoft.Linq.Expressions.Expression;
 45#endif
 46
 47    public partial class DefaultBinder : ActionBinder {
 48
 49        /// <summary>
 50        /// Provides default binding for performing a call on the specified meta objects.
 51        /// </summary>
 52        /// <param name="signature">The signature describing the call</param>
 53        /// <param name="target">The object to be called</param>
 54        /// <param name="args">
 55        /// Additional meta objects are the parameters for the call as specified by the CallSignature in the CallAction.
 56        /// </param>
 57        /// <returns>A MetaObject representing the call or the error.</returns>
 58        public DynamicMetaObject Call(CallSignature signature, DynamicMetaObject target, params DynamicMetaObject[] args) {
 59            return Call(signature, new ParameterBinder(this), target, args);
 60        }
 61
 62        /// <summary>
 63        /// Provides default binding for performing a call on the specified meta objects.
 64        /// </summary>
 65        /// <param name="signature">The signature describing the call</param>
 66        /// <param name="target">The meta object to be called.</param>
 67        /// <param name="args">
 68        /// Additional meta objects are the parameters for the call as specified by the CallSignature in the CallAction.
 69        /// </param>
 70        /// <param name="parameterBinder">ParameterBinder used to map arguments to parameters.</param>
 71        /// <returns>A MetaObject representing the call or the error.</returns>
 72        public DynamicMetaObject Call(CallSignature signature, ParameterBinder parameterBinder, DynamicMetaObject target, params DynamicMetaObject[] args) {
 73            ContractUtils.RequiresNotNullItems(args, "args");
 74            ContractUtils.RequiresNotNull(parameterBinder, "parameterBinder");
 75
 76            TargetInfo targetInfo = GetTargetInfo(signature, target, args);
 77
 78            if (targetInfo != null) {
 79                // we're calling a well-known MethodBase
 80                return MakeMetaMethodCall(signature, parameterBinder, targetInfo);
 81            } else {
 82                // we can't call this object
 83                return MakeCannotCallRule(target, target.GetLimitType());
 84            }
 85        }
 86
 87        #region Method Call Rule
 88
 89        private DynamicMetaObject MakeMetaMethodCall(CallSignature signature, ParameterBinder parameterBinder, TargetInfo targetInfo) {
 90            BindingRestrictions restrictions = BindingRestrictions.Combine(targetInfo.Arguments).Merge(targetInfo.Restrictions);
 91            if (targetInfo.Instance != null) {
 92                restrictions = targetInfo.Instance.Restrictions.Merge(restrictions);
 93            }
 94
 95            if (targetInfo.Instance != null) {
 96                return CallInstanceMethod(
 97                    parameterBinder,
 98                    targetInfo.Targets,
 99                    targetInfo.Instance,
100                    targetInfo.Arguments,
101                    signature,
102                    restrictions
103                );
104            }
105
106            return CallMethod(
107                parameterBinder,
108                targetInfo.Targets,
109                targetInfo.Arguments,
110                signature,
111                restrictions);
112        }
113
114        #endregion
115
116        #region Target acquisition
117
118        /// <summary>
119        /// Gets a TargetInfo object for performing a call on this object.  
120        /// 
121        /// If this object is a delegate we bind to the Invoke method.
122        /// If this object is a MemberGroup or MethodGroup we bind to the methods in the member group.
123        /// If this object is a BoundMemberTracker we bind to the methods with the bound instance.
124        /// If the underlying type has defined an operator Call method we'll bind to that method.
125        /// </summary>
126        private TargetInfo GetTargetInfo(CallSignature signature, DynamicMetaObject target, DynamicMetaObject[] args) {
127            Debug.Assert(target.HasValue);
128            object objTarget = target.Value;
129
130            return
131                TryGetDelegateTargets(target, args, objTarget as Delegate) ??
132                TryGetMemberGroupTargets(target, args, objTarget as MemberGroup) ??
133                TryGetMethodGroupTargets(target, args, objTarget as MethodGroup) ??
134                TryGetBoundMemberTargets(target, args, objTarget as BoundMemberTracker) ??
135                TryGetOperatorTargets(target, args, target, signature);
136        }
137
138        /// <summary>
139        /// Binds to the methods in a method group.
140        /// </summary>
141        private static TargetInfo TryGetMethodGroupTargets(DynamicMetaObject target, DynamicMetaObject[] args, MethodGroup mthgrp) {
142            if (mthgrp != null) {
143                List<MethodBase> foundTargets = new List<MethodBase>();
144
145                foreach (MethodTracker mt in mthgrp.Methods) {
146                    foundTargets.Add(mt.Method);
147                }
148
149                return new TargetInfo(null, ArrayUtils.Insert(target, args), BindingRestrictions.GetInstanceRestriction(target.Expression, mthgrp), foundTargets.ToArray());
150            }
151            return null;
152        }
153
154        /// <summary>
155        /// Binds to the methods in a member group.  
156        /// 
157        /// TODO: We should really only have either MemberGroup or MethodGroup, not both.
158        /// </summary>
159        private static TargetInfo TryGetMemberGroupTargets(DynamicMetaObject target, DynamicMetaObject[] args, MemberGroup mg) {
160            if (mg != null) {
161                MethodBase[] targets;
162                List<MethodInfo> foundTargets = new List<MethodInfo>();
163                foreach (MemberTracker mt in mg) {
164                    if (mt.MemberType == TrackerTypes.Method) {
165                        foundTargets.Add(((MethodTracker)mt).Method);
166                    }
167                }
168                targets = foundTargets.ToArray();
169                return new TargetInfo(null, ArrayUtils.Insert(target, args), targets);
170            }
171            return null;
172        }
173
174        /// <summary>
175        /// Binds to the BoundMemberTracker and uses the instance in the tracker and restricts
176        /// based upon the object instance type.
177        /// </summary>
178        private TargetInfo TryGetBoundMemberTargets(DynamicMetaObject self, DynamicMetaObject[] args, BoundMemberTracker bmt) {
179            if (bmt != null) {
180                Debug.Assert(bmt.Instance == null); // should be null for trackers that leak to user code
181
182                MethodBase[] targets;
183
184                // instance is pulled from the BoundMemberTracker and restricted to the correct
185                // type.
186                DynamicMetaObject instance = new DynamicMetaObject(
187                    AstUtils.Convert(
188                        Ast.Property(
189                            Ast.Convert(self.Expression, typeof(BoundMemberTracker)),
190                            typeof(BoundMemberTracker).GetProperty("ObjectInstance")
191                        ),
192                        bmt.BoundTo.DeclaringType
193                    ),
194                    self.Restrictions
195                ).Restrict(CompilerHelpers.GetType(bmt.ObjectInstance));
196
197                // we also add a restriction to make sure we're going to the same BoundMemberTracker
198                BindingRestrictions restrictions = BindingRestrictions.GetExpressionRestriction(
199                    Ast.Equal(
200                        Ast.Property(
201                            Ast.Convert(self.Expression, typeof(BoundMemberTracker)),
202                            typeof(BoundMemberTracker).GetProperty("BoundTo")
203                        ),
204                        AstUtils.Constant(bmt.BoundTo)
205                    )
206                );
207
208                switch (bmt.BoundTo.MemberType) {
209                    case TrackerTypes.MethodGroup:
210                        targets = ((MethodGroup)bmt.BoundTo).GetMethodBases();
211                        break;
212                    case TrackerTypes.Method:
213                        targets = new MethodBase[] { ((MethodTracker)bmt.BoundTo).Method };
214                        break;
215                    default:
216                        throw new InvalidOperationException(); // nothing else binds yet
217                }
218
219                return new TargetInfo(instance, args, restrictions, targets);
220            }
221            return null;
222        }
223
224        /// <summary>
225        /// Binds to the Invoke method on a delegate if this is a delegate type.
226        /// </summary>
227        private static TargetInfo TryGetDelegateTargets(DynamicMetaObject target, DynamicMetaObject[] args, Delegate d) {
228            if (d != null) {
229                return new TargetInfo(target, args, d.GetType().GetMethod("Invoke"));
230            }
231            return null;
232        }
233
234        /// <summary>
235        /// Attempts to bind to an operator Call method.
236        /// </summary>
237        private TargetInfo TryGetOperatorTargets(DynamicMetaObject self, DynamicMetaObject[] args, object target, CallSignature signature) {
238            MethodBase[] targets;
239
240            Type targetType = CompilerHelpers.GetType(target);
241
242            MemberGroup callMembers = GetMember(OldCallAction.Make(this, signature), targetType, "Call");
243            List<MethodBase> callTargets = new List<MethodBase>();
244            foreach (MemberTracker mi in callMembers) {
245                if (mi.MemberType == TrackerTypes.Method) {
246                    MethodInfo method = ((MethodTracker)mi).Method;
247                    if (method.IsSpecialName) {
248                        callTargets.Add(method);
249                    }
250                }
251            }
252
253            Expression instance = null;
254            if (callTargets.Count > 0) {
255                targets = callTargets.ToArray();
256                instance = Ast.Convert(self.Expression, CompilerHelpers.GetType(target));
257                return new TargetInfo(null, ArrayUtils.Insert(self, args), targets);
258            }
259
260            return null;
261        }
262
263        #endregion
264
265        #region Error support
266
267        private DynamicMetaObject MakeCannotCallRule(DynamicMetaObject self, Type type) {
268            return MakeError(
269                ErrorInfo.FromException(
270                    Ast.New(
271                        typeof(ArgumentTypeException).GetConstructor(new Type[] { typeof(string) }),
272                        AstUtils.Constant(type.Name + " is not callable")
273                    )
274                ),
275                self.Restrictions.Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(self.Expression, type))
276            );
277        }
278
279
280        #endregion
281
282
283        /// <summary>
284        /// Encapsulates information about the target of the call.  This includes an implicit instance for the call,
285        /// the methods that we'll be calling as well as any restrictions required to perform the call.
286        /// </summary>
287        class TargetInfo {
288            public readonly DynamicMetaObject Instance;
289            public readonly DynamicMetaObject[] Arguments;
290            public readonly MethodBase[] Targets;
291            public readonly BindingRestrictions Restrictions;
292
293            public TargetInfo(DynamicMetaObject instance, DynamicMetaObject[] arguments, params MethodBase[] args) :
294                this(instance, arguments, BindingRestrictions.Empty, args) {
295            }
296
297            public TargetInfo(DynamicMetaObject instance, DynamicMetaObject[] arguments, BindingRestrictions restrictions, params MethodBase[] targets) {
298                Assert.NotNullItems(targets);
299                Assert.NotNull(restrictions);
300
301                Instance = instance;
302                Arguments = arguments;
303                Targets = targets;
304                Restrictions = restrictions;
305            }
306        }
307
308    }
309}