PageRenderTime 23ms CodeModel.GetById 7ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

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

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