PageRenderTime 18ms CodeModel.GetById 2ms app.highlight 12ms RepoModel.GetById 2ms app.codeStats 0ms

/IronPython_2_0/Src/IronPython/Runtime/Binding/SlotOrFunction.cs

#
C# | 340 lines | 264 code | 40 blank | 36 comment | 63 complexity | be8b92fcd52f21d669c807fdadd80a3e 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.Actions;
 22using IronPython.Runtime.Types;
 23using Microsoft.Scripting;
 24using Microsoft.Scripting.Actions.Calls;
 25using Microsoft.Scripting.Generation;
 26using Microsoft.Scripting.Utils;
 27
 28namespace IronPython.Runtime.Binding {
 29    using Ast = Microsoft.Linq.Expressions.Expression;
 30
 31    /// <summary>
 32    /// Provides an abstraction for calling something which might be a builtin function or
 33    /// might be some arbitrary user defined slot.  If the object is a builtin function the
 34    /// call will go directly to the underlying .NET method.  If the object is an arbitrary
 35    /// callable object we will setup a nested dynamic site for performing the additional
 36    /// dispatch.
 37    /// 
 38    /// TODO: Wwe could probably do a specific binding to the object if it's another IDyanmicObject.
 39    /// </summary>
 40    sealed class SlotOrFunction {
 41        private readonly BindingTarget _function;
 42        private readonly MetaObject/*!*/ _target;
 43        public static readonly SlotOrFunction/*!*/ Empty = new SlotOrFunction(new MetaObject(Ast.Empty(), Restrictions.Empty));
 44
 45        private SlotOrFunction() {
 46        }
 47
 48        public SlotOrFunction(BindingTarget/*!*/ function, MetaObject/*!*/ target) {
 49            _target = target;
 50            _function = function;
 51        }
 52
 53        public SlotOrFunction(MetaObject/*!*/ target) {
 54            _target = target;
 55        }
 56
 57        public NarrowingLevel NarrowingLevel {
 58            get {
 59                if (_function != null) {
 60                    return _function.NarrowingLevel;
 61                }
 62
 63                return NarrowingLevel.None;
 64            }
 65        }
 66
 67        public Type/*!*/ ReturnType {
 68            get {
 69                return _target.LimitType;
 70            }
 71        }
 72
 73        public bool MaybeNotImplemented {
 74            get {
 75                if (_function != null) {
 76                    MethodInfo mi = _function.Method as MethodInfo;
 77                    if (mi != null) {
 78                        return mi.ReturnTypeCustomAttributes.IsDefined(typeof(MaybeNotImplementedAttribute), false);
 79                    }
 80                    return false;
 81                }
 82
 83                return true;
 84            }
 85        }
 86
 87        public bool Success {
 88            get {
 89                if (_function != null) {
 90                    return _function.Success;
 91                }
 92
 93                return this != Empty;
 94            }
 95        }
 96
 97        public MetaObject/*!*/ Target {
 98            get {
 99                return _target;
100            }
101        }
102
103        /// <summary>
104        /// Combines two methods, which came from two different binary types, selecting the method which has the best
105        /// set of conversions (the conversions which result in the least narrowing).
106        /// </summary>
107        public static bool GetCombinedTargets(SlotOrFunction fCand, SlotOrFunction rCand, out SlotOrFunction fTarget, out SlotOrFunction rTarget) {
108            fTarget = rTarget = Empty;
109
110            if (fCand.Success) {
111                if (rCand.Success) {
112                    if (fCand.NarrowingLevel <= rCand.NarrowingLevel) {
113                        fTarget = fCand;
114                        rTarget = rCand;
115                    } else {
116                        fTarget = Empty;
117                        rTarget = rCand;
118                    }
119                } else {
120                    fTarget = fCand;
121                }
122            } else if (rCand.Success) {
123                rTarget = rCand;
124            } else {
125                return false;
126            }
127
128            return true;
129        }
130
131        public static SlotOrFunction/*!*/ GetSlotOrFunction(BinderState/*!*/ state, SymbolId op, params MetaObject[] types) {
132            PythonTypeSlot slot;
133            SlotOrFunction res;
134            if (TryGetBinder(state, types, op, SymbolId.Empty, out res)) {
135                if (res != SlotOrFunction.Empty) {
136                    return res;
137                }
138            } else if (MetaUserObject.GetPythonType(types[0]).TryResolveSlot(state.Context, op, out slot)) {
139                ParameterExpression tmp = Ast.Variable(typeof(object), "slotVal");
140
141                Expression[] args = new Expression[types.Length - 1];
142                for (int i = 1; i < types.Length; i++) {
143                    args[i - 1] = types[i].Expression;
144                }
145                return new SlotOrFunction(
146                    new MetaObject(
147                        Ast.Scope(
148                            Ast.Comma(
149                                MetaPythonObject.MakeTryGetTypeMember(
150                                    state,
151                                    slot,
152                                    tmp,
153                                    types[0].Expression,
154                                    Ast.Call(
155                                        typeof(DynamicHelpers).GetMethod("GetPythonType"),
156                                        types[0].Expression
157                                    )
158                                ),
159                                Ast.Dynamic(
160                                    new InvokeBinder(
161                                        state,
162                                        new CallSignature(args.Length)
163                                    ),
164                                    typeof(object),
165                                    ArrayUtils.Insert<Expression>(
166                                        Ast.Constant(state.Context),
167                                        tmp,
168                                        args
169                                    )
170                                )
171                            ),
172                            tmp
173                        ),
174                        Restrictions.Combine(types).Merge(Restrictions.TypeRestriction(types[0].Expression, types[0].LimitType))
175                    )
176                );
177            }
178
179            return SlotOrFunction.Empty;
180        }
181
182        internal static bool TryGetBinder(BinderState/*!*/ state, MetaObject/*!*/[]/*!*/ types, SymbolId op, SymbolId rop, out SlotOrFunction/*!*/ res) {
183            PythonType declType;
184            return TryGetBinder(state, types, op, rop, out res, out declType);
185        }
186
187        /// <summary>
188        /// Trys to geta MethodBinder associated the slot for the specified type.
189        /// 
190        /// If a method is found the binder is set and true is returned.
191        /// If nothing is found binder is null and true is returned.
192        /// If something other than a method is found false is returned.
193        /// 
194        /// TODO: Remove rop
195        /// </summary>
196        internal static bool TryGetBinder(BinderState/*!*/ state, MetaObject/*!*/[]/*!*/ types, SymbolId op, SymbolId rop, out SlotOrFunction/*!*/ res, out PythonType declaringType) {
197            declaringType = null;
198
199            MetaObject xType = types[0];
200            BuiltinFunction xBf;
201            if (!BindingHelpers.TryGetStaticFunction(state, op, xType, out xBf)) {
202                res = SlotOrFunction.Empty;
203                return false;
204            }
205
206            xBf = CheckAlwaysNotImplemented(xBf);
207
208            BindingTarget bt;
209            MetaObject binder;
210            MetaObject yType = null;
211            BuiltinFunction yBf = null;
212
213            if (types.Length > 1) {
214                yType = types[1];
215                if (!BindingHelpers.IsSubclassOf(xType, yType) && !BindingHelpers.TryGetStaticFunction(state, rop, yType, out yBf)) {
216                    res = SlotOrFunction.Empty;
217                    return false;
218                }
219
220                yBf = CheckAlwaysNotImplemented(yBf);
221            }
222
223            if (yBf == xBf) {
224                yBf = null;
225            } else if (yBf != null && BindingHelpers.IsSubclassOf(yType, xType)) {
226                xBf = null;
227            }
228
229            var mc = new ParameterBinderWithCodeContext(state.Binder, Ast.Constant(state.Context));
230
231            if (xBf == null) {
232                if (yBf == null) {
233                    binder = null;
234                    bt = null;
235                } else {
236                    declaringType = DynamicHelpers.GetPythonTypeFromType(yBf.DeclaringType);
237                    binder = state.Binder.CallMethod(
238                        mc,
239                        yBf.Targets,
240                        types,
241                        new CallSignature(types.Length),
242                        Restrictions.Empty,
243                        PythonNarrowing.None,
244                        PythonNarrowing.BinaryOperator,
245                        out bt
246                    );
247                }
248            } else {
249                if (yBf == null) {
250                    declaringType = DynamicHelpers.GetPythonTypeFromType(xBf.DeclaringType);
251                    binder = state.Binder.CallMethod(
252                        mc,
253                        xBf.Targets,
254                        types,
255                        new CallSignature(types.Length),
256                        Restrictions.Empty,
257                        PythonNarrowing.None,
258                        PythonNarrowing.BinaryOperator,
259                        out bt
260                    );
261                } else {
262                    List<MethodBase> targets = new List<MethodBase>();
263                    targets.AddRange(xBf.Targets);
264                    foreach (MethodBase mb in yBf.Targets) {
265                        if (!ContainsMethodSignature(targets, mb)) targets.Add(mb);
266                    }
267
268                    binder = state.Binder.CallMethod(
269                        mc,
270                        targets.ToArray(),
271                        types,
272                        new CallSignature(types.Length),
273                        Restrictions.Empty,
274                        PythonNarrowing.None,
275                        PythonNarrowing.BinaryOperator,
276                        out bt
277                    );
278
279                    foreach (MethodBase mb in yBf.Targets) {
280                        if (bt.Method == mb) {
281                            declaringType = DynamicHelpers.GetPythonTypeFromType(yBf.DeclaringType);
282                            break;
283                        }
284                    }
285
286                    if (declaringType == null) {
287                        declaringType = DynamicHelpers.GetPythonTypeFromType(xBf.DeclaringType);
288                    }
289                }
290            }
291
292            if (binder != null) {
293                res = new SlotOrFunction(bt, binder);
294            } else {
295                res = SlotOrFunction.Empty;
296            }
297
298            Debug.Assert(res != null);
299            return true;
300        }
301
302        private static BuiltinFunction CheckAlwaysNotImplemented(BuiltinFunction xBf) {
303            if (xBf != null) {
304                bool returnsValue = false;
305                foreach (MethodBase mb in xBf.Targets) {
306                    if (CompilerHelpers.GetReturnType(mb) != typeof(NotImplementedType)) {
307                        returnsValue = true;
308                        break;
309                    }
310                }
311
312                if (!returnsValue) {
313                    xBf = null;
314                }
315            }
316            return xBf;
317        }
318
319        private static bool ContainsMethodSignature(IList<MethodBase/*!*/>/*!*/ existing, MethodBase/*!*/ check) {
320            ParameterInfo[] pis = check.GetParameters();
321            foreach (MethodBase mb in existing) {
322                if (MatchesMethodSignature(pis, mb)) return true;
323            }
324            return false;
325        }
326
327        private static bool MatchesMethodSignature(ParameterInfo/*!*/[]/*!*/ pis, MethodBase/*!*/ mb) {
328            ParameterInfo[] pis1 = mb.GetParameters();
329            if (pis.Length == pis1.Length) {
330                for (int i = 0; i < pis.Length; i++) {
331                    if (pis[i].ParameterType != pis1[i].ParameterType) return false;
332                }
333                return true;
334            } else {
335                return false;
336            }
337        }
338    }
339}
340