/IronPython_2_0/Src/IronPython/Runtime/Binding/SlotOrFunction.cs
C# | 340 lines | 264 code | 40 blank | 36 comment | 63 complexity | be8b92fcd52f21d669c807fdadd80a3e MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0
- /* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Microsoft Public License. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Microsoft Public License, please send an email to
- * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Microsoft Public License.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
-
- using System; using Microsoft;
- using System.Collections.Generic;
- using System.Diagnostics;
- using Microsoft.Linq.Expressions;
- using System.Reflection;
- using Microsoft.Scripting.Actions;
- using IronPython.Runtime.Types;
- using Microsoft.Scripting;
- using Microsoft.Scripting.Actions.Calls;
- using Microsoft.Scripting.Generation;
- using Microsoft.Scripting.Utils;
-
- namespace IronPython.Runtime.Binding {
- using Ast = Microsoft.Linq.Expressions.Expression;
-
- /// <summary>
- /// Provides an abstraction for calling something which might be a builtin function or
- /// might be some arbitrary user defined slot. If the object is a builtin function the
- /// call will go directly to the underlying .NET method. If the object is an arbitrary
- /// callable object we will setup a nested dynamic site for performing the additional
- /// dispatch.
- ///
- /// TODO: Wwe could probably do a specific binding to the object if it's another IDyanmicObject.
- /// </summary>
- sealed class SlotOrFunction {
- private readonly BindingTarget _function;
- private readonly MetaObject/*!*/ _target;
- public static readonly SlotOrFunction/*!*/ Empty = new SlotOrFunction(new MetaObject(Ast.Empty(), Restrictions.Empty));
-
- private SlotOrFunction() {
- }
-
- public SlotOrFunction(BindingTarget/*!*/ function, MetaObject/*!*/ target) {
- _target = target;
- _function = function;
- }
-
- public SlotOrFunction(MetaObject/*!*/ target) {
- _target = target;
- }
-
- public NarrowingLevel NarrowingLevel {
- get {
- if (_function != null) {
- return _function.NarrowingLevel;
- }
-
- return NarrowingLevel.None;
- }
- }
-
- public Type/*!*/ ReturnType {
- get {
- return _target.LimitType;
- }
- }
-
- public bool MaybeNotImplemented {
- get {
- if (_function != null) {
- MethodInfo mi = _function.Method as MethodInfo;
- if (mi != null) {
- return mi.ReturnTypeCustomAttributes.IsDefined(typeof(MaybeNotImplementedAttribute), false);
- }
- return false;
- }
-
- return true;
- }
- }
-
- public bool Success {
- get {
- if (_function != null) {
- return _function.Success;
- }
-
- return this != Empty;
- }
- }
-
- public MetaObject/*!*/ Target {
- get {
- return _target;
- }
- }
-
- /// <summary>
- /// Combines two methods, which came from two different binary types, selecting the method which has the best
- /// set of conversions (the conversions which result in the least narrowing).
- /// </summary>
- public static bool GetCombinedTargets(SlotOrFunction fCand, SlotOrFunction rCand, out SlotOrFunction fTarget, out SlotOrFunction rTarget) {
- fTarget = rTarget = Empty;
-
- if (fCand.Success) {
- if (rCand.Success) {
- if (fCand.NarrowingLevel <= rCand.NarrowingLevel) {
- fTarget = fCand;
- rTarget = rCand;
- } else {
- fTarget = Empty;
- rTarget = rCand;
- }
- } else {
- fTarget = fCand;
- }
- } else if (rCand.Success) {
- rTarget = rCand;
- } else {
- return false;
- }
-
- return true;
- }
-
- public static SlotOrFunction/*!*/ GetSlotOrFunction(BinderState/*!*/ state, SymbolId op, params MetaObject[] types) {
- PythonTypeSlot slot;
- SlotOrFunction res;
- if (TryGetBinder(state, types, op, SymbolId.Empty, out res)) {
- if (res != SlotOrFunction.Empty) {
- return res;
- }
- } else if (MetaUserObject.GetPythonType(types[0]).TryResolveSlot(state.Context, op, out slot)) {
- ParameterExpression tmp = Ast.Variable(typeof(object), "slotVal");
-
- Expression[] args = new Expression[types.Length - 1];
- for (int i = 1; i < types.Length; i++) {
- args[i - 1] = types[i].Expression;
- }
- return new SlotOrFunction(
- new MetaObject(
- Ast.Scope(
- Ast.Comma(
- MetaPythonObject.MakeTryGetTypeMember(
- state,
- slot,
- tmp,
- types[0].Expression,
- Ast.Call(
- typeof(DynamicHelpers).GetMethod("GetPythonType"),
- types[0].Expression
- )
- ),
- Ast.Dynamic(
- new InvokeBinder(
- state,
- new CallSignature(args.Length)
- ),
- typeof(object),
- ArrayUtils.Insert<Expression>(
- Ast.Constant(state.Context),
- tmp,
- args
- )
- )
- ),
- tmp
- ),
- Restrictions.Combine(types).Merge(Restrictions.TypeRestriction(types[0].Expression, types[0].LimitType))
- )
- );
- }
-
- return SlotOrFunction.Empty;
- }
-
- internal static bool TryGetBinder(BinderState/*!*/ state, MetaObject/*!*/[]/*!*/ types, SymbolId op, SymbolId rop, out SlotOrFunction/*!*/ res) {
- PythonType declType;
- return TryGetBinder(state, types, op, rop, out res, out declType);
- }
-
- /// <summary>
- /// Trys to geta MethodBinder associated the slot for the specified type.
- ///
- /// If a method is found the binder is set and true is returned.
- /// If nothing is found binder is null and true is returned.
- /// If something other than a method is found false is returned.
- ///
- /// TODO: Remove rop
- /// </summary>
- internal static bool TryGetBinder(BinderState/*!*/ state, MetaObject/*!*/[]/*!*/ types, SymbolId op, SymbolId rop, out SlotOrFunction/*!*/ res, out PythonType declaringType) {
- declaringType = null;
-
- MetaObject xType = types[0];
- BuiltinFunction xBf;
- if (!BindingHelpers.TryGetStaticFunction(state, op, xType, out xBf)) {
- res = SlotOrFunction.Empty;
- return false;
- }
-
- xBf = CheckAlwaysNotImplemented(xBf);
-
- BindingTarget bt;
- MetaObject binder;
- MetaObject yType = null;
- BuiltinFunction yBf = null;
-
- if (types.Length > 1) {
- yType = types[1];
- if (!BindingHelpers.IsSubclassOf(xType, yType) && !BindingHelpers.TryGetStaticFunction(state, rop, yType, out yBf)) {
- res = SlotOrFunction.Empty;
- return false;
- }
-
- yBf = CheckAlwaysNotImplemented(yBf);
- }
-
- if (yBf == xBf) {
- yBf = null;
- } else if (yBf != null && BindingHelpers.IsSubclassOf(yType, xType)) {
- xBf = null;
- }
-
- var mc = new ParameterBinderWithCodeContext(state.Binder, Ast.Constant(state.Context));
-
- if (xBf == null) {
- if (yBf == null) {
- binder = null;
- bt = null;
- } else {
- declaringType = DynamicHelpers.GetPythonTypeFromType(yBf.DeclaringType);
- binder = state.Binder.CallMethod(
- mc,
- yBf.Targets,
- types,
- new CallSignature(types.Length),
- Restrictions.Empty,
- PythonNarrowing.None,
- PythonNarrowing.BinaryOperator,
- out bt
- );
- }
- } else {
- if (yBf == null) {
- declaringType = DynamicHelpers.GetPythonTypeFromType(xBf.DeclaringType);
- binder = state.Binder.CallMethod(
- mc,
- xBf.Targets,
- types,
- new CallSignature(types.Length),
- Restrictions.Empty,
- PythonNarrowing.None,
- PythonNarrowing.BinaryOperator,
- out bt
- );
- } else {
- List<MethodBase> targets = new List<MethodBase>();
- targets.AddRange(xBf.Targets);
- foreach (MethodBase mb in yBf.Targets) {
- if (!ContainsMethodSignature(targets, mb)) targets.Add(mb);
- }
-
- binder = state.Binder.CallMethod(
- mc,
- targets.ToArray(),
- types,
- new CallSignature(types.Length),
- Restrictions.Empty,
- PythonNarrowing.None,
- PythonNarrowing.BinaryOperator,
- out bt
- );
-
- foreach (MethodBase mb in yBf.Targets) {
- if (bt.Method == mb) {
- declaringType = DynamicHelpers.GetPythonTypeFromType(yBf.DeclaringType);
- break;
- }
- }
-
- if (declaringType == null) {
- declaringType = DynamicHelpers.GetPythonTypeFromType(xBf.DeclaringType);
- }
- }
- }
-
- if (binder != null) {
- res = new SlotOrFunction(bt, binder);
- } else {
- res = SlotOrFunction.Empty;
- }
-
- Debug.Assert(res != null);
- return true;
- }
-
- private static BuiltinFunction CheckAlwaysNotImplemented(BuiltinFunction xBf) {
- if (xBf != null) {
- bool returnsValue = false;
- foreach (MethodBase mb in xBf.Targets) {
- if (CompilerHelpers.GetReturnType(mb) != typeof(NotImplementedType)) {
- returnsValue = true;
- break;
- }
- }
-
- if (!returnsValue) {
- xBf = null;
- }
- }
- return xBf;
- }
-
- private static bool ContainsMethodSignature(IList<MethodBase/*!*/>/*!*/ existing, MethodBase/*!*/ check) {
- ParameterInfo[] pis = check.GetParameters();
- foreach (MethodBase mb in existing) {
- if (MatchesMethodSignature(pis, mb)) return true;
- }
- return false;
- }
-
- private static bool MatchesMethodSignature(ParameterInfo/*!*/[]/*!*/ pis, MethodBase/*!*/ mb) {
- ParameterInfo[] pis1 = mb.GetParameters();
- if (pis.Length == pis1.Length) {
- for (int i = 0; i < pis.Length; i++) {
- if (pis[i].ParameterType != pis1[i].ParameterType) return false;
- }
- return true;
- } else {
- return false;
- }
- }
- }
- }
-