/Languages/IronPython/IronPython/Runtime/Types/TypeInfo.cs
C# | 1371 lines | 923 code | 211 blank | 237 comment | 292 complexity | 62580c38bd987ade39de8d0bb02e0855 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
Large files files are truncated, but you can click here to view the full file
- /* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Apache License, Version 2.0, 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 Apache License, Version 2.0.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
-
- using System;
- using System.Linq;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Reflection;
- using System.Dynamic;
- using System.Threading;
-
- using Microsoft.Scripting;
- using Microsoft.Scripting.Actions;
- using Microsoft.Scripting.Generation;
- using Microsoft.Scripting.Runtime;
- using Microsoft.Scripting.Utils;
-
- using IronPython.Runtime.Binding;
- using IronPython.Runtime.Exceptions;
- using IronPython.Runtime.Operations;
-
- #if FEATURE_NUMERICS
- using System.Numerics;
- #else
- using Microsoft.Scripting.Math;
- using Complex = Microsoft.Scripting.Math.Complex64;
- #endif
-
- namespace IronPython.Runtime.Types {
- /// <summary>
- /// Helpers for interacting w/ .NET types. This includes:
- ///
- /// Member resolution via GetMember/GetMembers. This performs a member lookup which includes the registered
- /// extension types in the PythonBinder. Internally the class has many MemberResolver's which provide
- /// the various resolution behaviors.
- ///
- /// Cached member access - this is via static classes such as Object and provides various MemberInfo's so we're
- /// not constantly looking up via reflection.
- /// </summary>
- internal static partial class TypeInfo {
- /// <summary> list of resolvers which we run to resolve items </summary>
- private static readonly MemberResolver/*!*/[]/*!*/ _resolvers = MakeResolverTable();
- [MultiRuntimeAware]
- private static DocumentationDescriptor _docDescr;
- [MultiRuntimeAware]
- internal static Dictionary<string, PythonOperationKind> _pythonOperatorTable;
-
- #region Public member resolution
-
- /// <summary>
- /// Gets the statically known member from the type with the specific name. Searches the entire type hierarchy to find the specified member.
- /// </summary>
- public static MemberGroup/*!*/ GetMemberAll(PythonBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type, string/*!*/ name) {
- Assert.NotNull(binder, action, type, name);
-
- PerfTrack.NoteEvent(PerfTrack.Categories.ReflectedTypes, String.Format("ResolveMember: {0} {1}", type.Name, name));
- return GetMemberGroup(new ResolveBinder(binder), action, type, name);
- }
-
- /// <summary>
- /// Gets all the statically known members from the specified type. Searches the entire type hierarchy to get all possible members.
- ///
- /// The result may include multiple resolution. It is the callers responsibility to only treat the 1st one by name as existing.
- /// </summary>
- public static IList<ResolvedMember/*!*/>/*!*/ GetMembersAll(PythonBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type) {
- Assert.NotNull(binder, action, type);
-
- return GetResolvedMembers(new ResolveBinder(binder), action, type);
- }
-
- /// <summary>
- /// Gets the statically known member from the type with the specific name. Searches only the specified type to find the member.
- /// </summary>
- public static MemberGroup/*!*/ GetMember(PythonBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type, string/*!*/ name) {
- Assert.NotNull(binder, action, type, name);
-
- PerfTrack.NoteEvent(PerfTrack.Categories.ReflectedTypes, String.Format("LookupMember: {0} {1}", type.Name, name));
- return GetMemberGroup(new LookupBinder(binder), action, type, name);
- }
-
- /// <summary>
- /// Gets all the statically known members from the specified type. Searches only the specified type to find the members.
- ///
- /// The result may include multiple resolution. It is the callers responsibility to only treat the 1st one by name as existing.
- /// </summary>
- public static IList<ResolvedMember/*!*/>/*!*/ GetMembers(PythonBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type) {
- Assert.NotNull(binder, action, type);
-
- return GetResolvedMembers(new LookupBinder(binder), action, type);
- }
-
- #endregion
-
- #region Cached type members
-
- public static class _Object {
- public new static readonly MethodInfo/*!*/ GetType = typeof(object).GetMethod("GetType");
- }
-
- public static class _IPythonObject {
- public static readonly PropertyInfo/*!*/ PythonType = typeof(IPythonObject).GetProperty("PythonType");
- public static readonly PropertyInfo/*!*/ Dict = typeof(IPythonObject).GetProperty("Dict");
- }
-
- public static class _PythonOps {
- public static readonly MethodInfo/*!*/ SlotTryGetBoundValue = typeof(PythonOps).GetMethod("SlotTryGetBoundValue");
- public static readonly MethodInfo/*!*/ GetTypeVersion = typeof(PythonOps).GetMethod("GetTypeVersion");
- public static readonly MethodInfo/*!*/ CheckTypeVersion = typeof(PythonOps).GetMethod("CheckTypeVersion");
- }
-
- public static class _OperationFailed {
- public static readonly FieldInfo/*!*/ Value = typeof(OperationFailed).GetField("Value");
- }
-
- public static class _PythonDictionary {
- public static readonly MethodInfo/*!*/ TryGetvalue = typeof(PythonDictionary).GetMethod("TryGetValue");
- }
-
- public static class _PythonGenerator {
- public static readonly ConstructorInfo Ctor = typeof(PythonGenerator).GetConstructor(new Type[] { typeof(PythonFunction) });
- }
-
-
- #endregion
-
- #region MemberResolver implementations and infrastructure
-
- /// <summary>
- /// Abstract class used for resolving members. This provides two methods of member look. The first is looking
- /// up a single member by name. The other is getting all of the members.
- ///
- /// There are various subclasses of this which have different methods of resolving the members. The primary
- /// function of the resolvers are to provide the name->value lookup. They also need to provide a simple name
- /// enumerator. The enumerator is kept simple because it's allowed to return duplicate names as well as return
- /// names of members that don't exist. The base MemberResolver will then verify their existance as well as
- /// filter duplicates.
- /// </summary>
- abstract class MemberResolver {
- /// <summary>
- /// Looks up an individual member and returns a MemberGroup with the given members.
- /// </summary>
- public abstract MemberGroup/*!*/ ResolveMember(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type, string/*!*/ name);
-
- /// <summary>
- /// Returns a list of members that exist on the type. The ResolvedMember structure indicates both
- /// the name and provides the MemberGroup.
- /// </summary>
- public IList<ResolvedMember/*!*/>/*!*/ ResolveMembers(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type) {
- Dictionary<string, ResolvedMember> members = new Dictionary<string, ResolvedMember>();
-
- foreach (string name in GetCandidateNames(binder, action, type)) {
- if (members.ContainsKey(name)) {
- continue;
- }
-
- MemberGroup member = ResolveMember(binder, action, type, name);
- if (member.Count > 0) {
- members[name] = new ResolvedMember(name, member);
- }
- }
-
- ResolvedMember[] res = new ResolvedMember[members.Count];
- members.Values.CopyTo(res, 0);
- return res;
- }
-
- /// <summary>
- /// Returns a list of possible members which could exist. ResolveMember needs to be called to verify their existance. Duplicate
- /// names can also be returned.
- /// </summary>
- protected abstract IEnumerable<string/*!*/>/*!*/ GetCandidateNames(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type);
- }
-
- /// <summary>
- /// One off resolver for various special methods which are known by name. A delegate is provided to provide the actual member which
- /// will be resolved.
- /// </summary>
- class OneOffResolver : MemberResolver {
- private string/*!*/ _name;
- private Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/>/*!*/ _resolver;
-
- public OneOffResolver(string/*!*/ name, Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/>/*!*/ resolver) {
- Assert.NotNull(name, resolver);
-
- _name = name;
- _resolver = resolver;
- }
-
- public override MemberGroup/*!*/ ResolveMember(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type, string/*!*/ name) {
- Assert.NotNull(binder, action, type, name);
-
- if (name == _name) {
- return _resolver(binder, type);
- }
-
- return MemberGroup.EmptyGroup;
- }
-
- protected override IEnumerable<string/*!*/>/*!*/ GetCandidateNames(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type) {
- yield return _name;
- }
- }
-
- /// <summary>
- /// Standard resolver for looking up .NET members. Uses reflection to get the members by name.
- /// </summary>
- class StandardResolver : MemberResolver {
- public override MemberGroup/*!*/ ResolveMember(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type, string/*!*/ name) {
- if (name == ".ctor" || name == ".cctor") return MemberGroup.EmptyGroup;
-
- // normal binding
- MemberGroup res;
-
- foreach (Type curType in binder.GetContributingTypes(type)) {
- res = FilterSpecialNames(binder.GetMember(curType, name), name, action);
- if (res.Count > 0) {
- return res;
- }
- }
-
- if (type.IsInterface) {
- foreach (Type t in type.GetInterfaces()) {
- res = FilterSpecialNames(binder.GetMember(t, name), name, action);
- if (res.Count > 0) {
- return res;
- }
- }
- }
-
- return MemberGroup.EmptyGroup;
- }
-
- protected override IEnumerable<string/*!*/>/*!*/ GetCandidateNames(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type) {
- foreach (Type curType in binder.GetContributingTypes(type)) {
- foreach (MemberInfo mi in curType.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)) {
- if (mi.MemberType == MemberTypes.Method) {
- MethodInfo meth = (MethodInfo)mi;
-
- if (meth.IsSpecialName) {
- if (meth.IsDefined(typeof(PropertyMethodAttribute), true)) {
- if (meth.Name.StartsWith("Get") || meth.Name.StartsWith("Set")) {
- yield return meth.Name.Substring(3);
- } else {
- Debug.Assert(meth.Name.StartsWith("Delete"));
- yield return meth.Name.Substring(6);
- }
- }
-
- continue;
- }
- }
-
- yield return mi.Name;
- }
- }
- }
- }
-
- /// <summary>
- /// Resolves methods mapped to __eq__ and __ne__ from:
- /// 1. IStructuralEquatable.Equals
- /// 2. IValueEquality.Equals (CLR2 only)
- /// </summary>
- class EqualityResolver : MemberResolver {
- public static readonly EqualityResolver Instance = new EqualityResolver();
-
- private EqualityResolver() { }
-
- public override MemberGroup/*!*/ ResolveMember(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type, string/*!*/ name) {
- Assert.NotNull(binder, action, type, name);
-
- bool equality;
- switch (name) {
- case "__eq__": equality = true; break;
- case "__ne__": equality = false; break;
- default:
- return MemberGroup.EmptyGroup;
- }
-
- if (typeof(IStructuralEquatable).IsAssignableFrom(type)) {
- return new MemberGroup(
- GetEqualityMethods(type, equality ? "StructuralEqualityMethod" : "StructuralInequalityMethod")
- );
- #if CLR2
- } else if (typeof(IValueEquality).IsAssignableFrom(type)) {
- return new MemberGroup(
- GetEqualityMethods(type, equality ? "ValueEqualsMethod" : "ValueNotEqualsMethod")
- );
- #endif
- }
-
- return MemberGroup.EmptyGroup;
- }
-
- protected override IEnumerable<string/*!*/>/*!*/ GetCandidateNames(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type) {
- yield return "__eq__";
- yield return "__ne__";
- }
- }
-
- /// <summary>
- /// Resolves methods mapped to __gt__, __lt__, __ge__, __le__, as well as providing an alternate resolution
- /// for __eq__ and __ne__, from the comparable type's CompareTo method.
- ///
- /// This should be run after the EqualityResolver.
- /// </summary>
- class ComparisonResolver : MemberResolver {
- private readonly bool _excludePrimitiveTypes;
- private readonly Type/*!*/ _comparable;
- private readonly Dictionary<string/*!*/, string/*!*/>/*!*/ _helperMap;
-
- public ComparisonResolver(Type/*!*/ comparable, string/*!*/ helperPrefix) {
- Assert.NotNull(comparable, helperPrefix);
-
- _excludePrimitiveTypes = comparable == typeof(IComparable);
- _comparable = comparable;
- _helperMap = new Dictionary<string, string>();
- _helperMap["__eq__"] = helperPrefix + "Equality";
- _helperMap["__ne__"] = helperPrefix + "Inequality";
- _helperMap["__gt__"] = helperPrefix + "GreaterThan";
- _helperMap["__lt__"] = helperPrefix + "LessThan";
- _helperMap["__ge__"] = helperPrefix + "GreaterEqual";
- _helperMap["__le__"] = helperPrefix + "LessEqual";
- }
-
- public override MemberGroup/*!*/ ResolveMember(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type, string/*!*/ name) {
- Assert.NotNull(binder, action, type, name);
-
- // Do not map IComparable if this is a primitive builtin type.
- if (_excludePrimitiveTypes) {
- if (type.IsPrimitive || type == typeof(BigInteger) ||
- type == typeof(string) || type == typeof(decimal)) {
- return MemberGroup.EmptyGroup;
- }
- }
-
- string helperName;
- if (_helperMap.TryGetValue(name, out helperName) &&
- _comparable.IsAssignableFrom(type)) {
- return new MemberGroup(GetEqualityMethods(type, helperName));
- }
-
- return MemberGroup.EmptyGroup;
- }
-
- protected override IEnumerable<string/*!*/>/*!*/ GetCandidateNames(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type) {
- return _helperMap.Keys;
- }
- }
-
- /// <summary>
- /// Resolves methods mapped to __*__ methods automatically from the .NET operator.
- /// </summary>
- class OperatorResolver : MemberResolver {
- public override MemberGroup/*!*/ ResolveMember(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type, string/*!*/ name) {
- if (type.IsSealed() && type.IsAbstract()) {
- // static types don't have PythonOperationKind
- return MemberGroup.EmptyGroup;
- }
-
- // try mapping __*__ methods to .NET method names
- PythonOperationKind opMap;
- EnsureOperatorTable();
-
- if (_pythonOperatorTable.TryGetValue(name, out opMap)) {
- if (IncludeOperatorMethod(type, opMap)) {
- OperatorMapping opInfo;
- if (IsReverseOperator(opMap)) {
- opInfo = OperatorMapping.GetOperatorMapping(opMap & ~PythonOperationKind.Reversed);
- } else {
- opInfo = OperatorMapping.GetOperatorMapping(opMap);
- }
-
- if (opInfo != null) {
- foreach (Type curType in binder.GetContributingTypes(type)) {
- #if !CLR2
- if (curType == typeof(double)) {
- if ((opInfo.Operator & PythonOperationKind.Comparison) != 0) {
- // we override these with our own comparisons in DoubleOps
- continue;
- }
- } else
- #endif
- if (curType == typeof(BigInteger)) {
- if (opInfo.Operator == PythonOperationKind.Mod ||
- opInfo.Operator == PythonOperationKind.RightShift ||
- opInfo.Operator == PythonOperationKind.LeftShift ||
- opInfo.Operator == PythonOperationKind.Compare ||
- opInfo.Operator == PythonOperationKind.Divide) {
- // we override these with our own modulus/power PythonOperationKind which are different from BigInteger.
- continue;
- }
- #if !CLR2
- } else if (curType == typeof(Complex) && opInfo.Operator == PythonOperationKind.Divide) {
- // we override this with our own division PythonOperationKind which is different from .NET Complex.
- continue;
- #endif
- }
-
- Debug.Assert(opInfo.Name != "Equals");
-
- MemberGroup res = binder.GetMember(curType, opInfo.Name);
- if (res.Count == 0 && opInfo.AlternateName != null) {
- res = binder.GetMember(curType, opInfo.AlternateName);
- if (opInfo.AlternateName == "Equals") {
- // "Equals" is available as an alternate method name. Because it's also on object and Python
- // doesn't define it on object we need to filter it out.
- res = FilterObjectEquality(res);
- }
-
- res = FilterAlternateMethods(opInfo, res);
- }
-
- if (res.Count > 0) {
- return FilterForwardReverseMethods(name, res, type, opMap);
- }
- }
- }
- }
- }
-
- if (name == "__call__") {
- MemberGroup res = binder.GetMember(type, "Call");
- if (res.Count > 0) {
- return res;
- }
- }
-
- return MemberGroup.EmptyGroup;
- }
-
- /// <summary>
- /// Filters alternative methods out that don't match the expected signature and therefore
- /// are just sharing a common method name.
- /// </summary>
- private static MemberGroup FilterAlternateMethods(OperatorMapping opInfo, MemberGroup res) {
- if (res.Count > 0 && opInfo.AlternateExpectedType != null) {
- List<MemberTracker> matchingMethods = new List<MemberTracker>();
- for (int i = 0; i < res.Count; i++) {
- MemberTracker mt = res[i];
- if (mt.MemberType == TrackerTypes.Method &&
- ((MethodTracker)mt).Method.ReturnType == opInfo.AlternateExpectedType) {
- matchingMethods.Add(mt);
- }
- }
-
- if (matchingMethods.Count == 0) {
- res = MemberGroup.EmptyGroup;
- } else {
- res = new MemberGroup(matchingMethods.ToArray());
- }
- }
- return res;
- }
-
- /// <summary>
- /// Removes Object.Equals methods as we never return these for PythonOperationKind.
- /// </summary>
- private static MemberGroup/*!*/ FilterObjectEquality(MemberGroup/*!*/ group) {
- List<MemberTracker> res = null;
-
- for (int i = 0; i < group.Count; i++) {
- MemberTracker mt = group[i];
- if (mt.MemberType == TrackerTypes.Method && (mt.DeclaringType == typeof(object) || mt.DeclaringType == typeof(double) || mt.DeclaringType == typeof(float)) && mt.Name == "Equals") {
- if (res == null) {
- res = new List<MemberTracker>();
- for (int j = 0; j < i; j++) {
- res.Add(group[j]);
- }
- }
- } else if (mt.MemberType == TrackerTypes.Method && mt.DeclaringType == typeof(ValueType) && mt.Name == "Equals") {
- // ValueType.Equals overrides object.Equals but we can't call it w/ a boxed value type therefore
- // we return the object version and the virtual call will dispatch to ValueType.Equals.
- if (res == null) {
- res = new List<MemberTracker>();
- for (int j = 0; j < i; j++) {
- res.Add(group[j]);
- }
- }
-
- res.Add(MemberTracker.FromMemberInfo(typeof(object).GetMethod("Equals", new Type[] { typeof(object) })));
- } else if (res != null) {
- res.Add(group[i]);
- }
- }
-
- if (res != null) {
- if (res.Count == 0) {
- return MemberGroup.EmptyGroup;
- }
-
- return new MemberGroup(res.ToArray());
- }
-
- return group;
- }
-
- protected override IEnumerable<string/*!*/>/*!*/ GetCandidateNames(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type) {
- EnsureOperatorTable();
-
- foreach (string si in _pythonOperatorTable.Keys) {
- yield return si;
- }
-
- yield return "__call__";
- }
- }
-
- /// <summary>
- /// Provides bindings to private members when that global option is enabled.
- /// </summary>
- class PrivateBindingResolver : MemberResolver {
- private const BindingFlags _privateFlags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
-
- public override MemberGroup/*!*/ ResolveMember(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type, string/*!*/ name) {
- if (binder.DomainManager.Configuration.PrivateBinding) {
- // in private binding mode Python exposes private members under a mangled name.
- string header = "_" + type.Name + "__";
- if (name.StartsWith(header)) {
- string memberName = name.Substring(header.Length);
-
- MemberGroup res = new MemberGroup(type.GetMember(memberName, _privateFlags));
- if (res.Count > 0) {
- return FilterFieldAndEvent(res);
- }
-
- res = new MemberGroup(type.GetMember(memberName, BindingFlags.FlattenHierarchy | _privateFlags));
- if (res.Count > 0) {
- return FilterFieldAndEvent(res);
- }
- }
- }
-
- return MemberGroup.EmptyGroup;
- }
-
- protected override IEnumerable<string/*!*/>/*!*/ GetCandidateNames(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type) {
- if (!binder.DomainManager.Configuration.PrivateBinding) {
- yield break;
- }
-
- foreach (MemberInfo mi in type.GetMembers(_privateFlags | BindingFlags.FlattenHierarchy)) {
- yield return String.Concat("_", mi.DeclaringType.Name, "__", mi.Name);
- }
- }
- }
-
- /// <summary>
- /// Provides resolutions for protected members that haven't yet been
- /// subclassed by NewTypeMaker.
- /// </summary>
- class ProtectedMemberResolver : MemberResolver {
- public override MemberGroup/*!*/ ResolveMember(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type, string/*!*/ name) {
- foreach (Type t in binder.GetContributingTypes(type)) {
- MemberGroup res = new MemberGroup(
- t.GetMember(name, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)
- .Where(ProtectedOnly)
- .ToArray());
-
- for (int i = 0; i < res.Count; i++) {
- MethodTracker meth = res[i] as MethodTracker;
- if (meth == null) {
- continue;
- }
-
- if (meth.Name == "Finalize" && meth.Method.GetBaseDefinition() == typeof(object).GetMethod("Finalize", BindingFlags.NonPublic | BindingFlags.Instance)) {
- MemberTracker[] retained = new MemberTracker[res.Count - 1];
- if (res.Count == 1) {
- res = MemberGroup.EmptyGroup;
- } else {
- for (int j = 0; j < i; j++) {
- retained[j] = res[j];
- }
- for (int j = i + 1; j < res.Count; j++) {
- retained[j - 1] = res[j];
- }
- res = new MemberGroup(retained);
- }
- break;
- }
- }
- return FilterSpecialNames(res, name, action);
- }
-
- return MemberGroup.EmptyGroup;
- }
-
- protected override IEnumerable<string/*!*/>/*!*/ GetCandidateNames(MemberBinder/*!*/ binder, MemberRequestKind/*!*/ action, Type/*!*/ type) {
- // these members are visible but only accept derived types.
- foreach (Type t in binder.GetContributingTypes(type)) {
- foreach (MemberInfo mi in t.GetMembers(BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic)) {
- if (ProtectedOnly(mi)) {
- yield return mi.Name;
- }
- }
- }
- }
- }
-
- /// <summary>
- /// Creates the resolver table which includes all the possible resolutions.
- /// </summary>
- /// <returns></returns>
- private static MemberResolver/*!*/[]/*!*/ MakeResolverTable() {
- return new MemberResolver[] {
- // values that live on object need to run before the StandardResolver
- // which will pick up values off of object.
- new OneOffResolver("__str__", StringResolver),
- new OneOffResolver("__new__", NewResolver),
- new OneOffResolver("__repr__", ReprResolver),
- new OneOffResolver("__hash__", HashResolver),
- new OneOffResolver("__iter__", IterResolver),
- #if FEATURE_SERIALIZATION
- new OneOffResolver("__reduce_ex__", SerializationResolver),
- #endif
- // The standard resolver looks for types using .NET reflection by name
- new StandardResolver(),
-
- // Runs after StandardResolver so custom __eq__ methods can be added
- // that support things like returning NotImplemented vs. IValueEquality
- // which only supports true/false. Runs before OperatorResolver so that
- // IStructuralEquatable and IValueEquality take precedence over Equals,
- // which can be provided for nice .NET interop.
-
- EqualityResolver.Instance,
- new ComparisonResolver(typeof(IStructuralComparable), "StructuralComparable"),
-
- new OneOffResolver("__all__", AllResolver),
- new OneOffResolver("__contains__", ContainsResolver),
- new OneOffResolver("__dir__", DirResolver),
- new OneOffResolver("__doc__", DocResolver),
- new OneOffResolver("__enter__", EnterResolver),
- new OneOffResolver("__exit__", ExitResolver),
- new OneOffResolver("__len__", LengthResolver),
- new OneOffResolver("__format__", FormatResolver),
- new OneOffResolver("next", NextResolver),
-
- new OneOffResolver("__complex__", ComplexResolver),
- new OneOffResolver("__float__", FloatResolver),
- new OneOffResolver("__int__", IntResolver),
- new OneOffResolver("__long__", BigIntegerResolver),
-
- // non standard PythonOperationKind which are Python specific
- new OneOffResolver("__truediv__", new OneOffOperatorBinder("TrueDivide", "__truediv__", PythonOperationKind.TrueDivide).Resolver),
- new OneOffResolver("__rtruediv__", new OneOffOperatorBinder("TrueDivide", "__rtruediv__", PythonOperationKind.ReverseTrueDivide).Resolver),
- new OneOffResolver("__itruediv__", new OneOffOperatorBinder("InPlaceTrueDivide", "__itruediv__", PythonOperationKind.InPlaceTrueDivide).Resolver),
- new OneOffResolver("__floordiv__", new OneOffOperatorBinder("FloorDivide", "__floordiv__", PythonOperationKind.FloorDivide).Resolver),
- new OneOffResolver("__rfloordiv__", new OneOffOperatorBinder("FloorDivide", "__rfloordiv__", PythonOperationKind.ReverseFloorDivide).Resolver),
- new OneOffResolver("__ifloordiv__", new OneOffOperatorBinder("InPlaceFloorDivide", "__ifloordiv__", PythonOperationKind.InPlaceFloorDivide).Resolver),
- new OneOffResolver("__pow__", new OneOffPowerBinder("__pow__", PythonOperationKind.Power).Resolver),
- new OneOffResolver("__rpow__", new OneOffPowerBinder("__rpow__", PythonOperationKind.ReversePower).Resolver),
- new OneOffResolver("__ipow__", new OneOffOperatorBinder("InPlacePower", "__ipow__", PythonOperationKind.InPlacePower).Resolver),
- new OneOffResolver("__abs__", new OneOffOperatorBinder("Abs", "__abs__", PythonOperationKind.AbsoluteValue).Resolver),
- new OneOffResolver("__divmod__", new OneOffOperatorBinder("DivMod", "__divmod__", PythonOperationKind.DivMod).Resolver),
- new OneOffResolver("__rdivmod__", new OneOffOperatorBinder("DivMod", "__rdivmod__", PythonOperationKind.DivMod).Resolver),
-
- // The operator resolver maps standard .NET operator methods into Python operator
- // methods
- new OperatorResolver(),
-
- // Runs after operator resolver to map default members to __getitem__/__setitem__
- new OneOffResolver("__getitem__", GetItemResolver),
- new OneOffResolver("__setitem__", SetItemResolver),
-
- // Runs after operator resolver to map __ne__ -> !__eq__
- new OneOffResolver("__ne__", FallbackInequalityResolver),
-
- // Runs after the operator resolver to map IComparable
- new ComparisonResolver(typeof(IComparable), "Comparable"),
-
- // Protected members are visible but only usable from derived types
- new ProtectedMemberResolver(),
- // Support binding to private members if the user has enabled that feature
- new PrivateBindingResolver(),
- };
- }
-
- #endregion
-
- #region One-off resolvers
-
- #region Resolving numerical conversions (__complex__, __float__, __int__, and __long__)
-
- /// <summary>
- /// Provides a resolution for __complex__
- /// </summary>
- private static Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/>/*!*/ ComplexResolver {
- get {
- if (_ComplexResolver != null) return _ComplexResolver;
- _ComplexResolver = MakeConversionResolver(new List<Type> {
- typeof(Complex), typeof(ExtensibleComplex), typeof(Extensible<Complex>),
- typeof(double), typeof(Extensible<double>)
- });
- return _ComplexResolver;
- }
- }
- private static Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/> _ComplexResolver;
-
- /// <summary>
- /// Provides a resolution for __float__
- /// </summary>
- private static Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/>/*!*/ FloatResolver {
- get {
- if (_FloatResolver != null) return _FloatResolver;
- _FloatResolver = MakeConversionResolver(new List<Type> {
- typeof(double), typeof(Extensible<double>)
- });
- return _FloatResolver;
- }
- }
- private static Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/> _FloatResolver;
-
- /// <summary>
- /// Provides a resolution for __int__
- /// </summary>
- private static Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/>/*!*/ IntResolver {
- get {
- if (_IntResolver != null) return _IntResolver;
- _IntResolver = MakeConversionResolver(new List<Type> {
- typeof(int), typeof(Extensible<int>),
- typeof(BigInteger), typeof(Extensible<BigInteger>)
- });
- return _IntResolver;
- }
- }
- private static Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/> _IntResolver;
-
- /// <summary>
- /// Provides a resolution for __long__
- /// </summary>
- private static Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/>/*!*/ BigIntegerResolver {
- get {
- if (_BigIntegerResolver != null) return _BigIntegerResolver;
- _BigIntegerResolver = MakeConversionResolver(new List<Type> {
- typeof(BigInteger), typeof(Extensible<BigInteger>),
- typeof(int), typeof(Extensible<int>)
- });
- return _BigIntegerResolver;
- }
- }
- private static Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/> _BigIntegerResolver;
-
- /// <summary>
- /// Provides a resolution for __getitem__
- /// </summary>
- private static Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/>/*!*/ GetItemResolver {
- get {
- if (_GetItemResolver == null) {
- _GetItemResolver = MakeIndexerResolver(false);
- }
- return _GetItemResolver;
- }
- }
- private static Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/> _GetItemResolver;
-
- /// <summary>
- /// Provides a resolution for __setitem__
- /// </summary>
- private static Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/>/*!*/ SetItemResolver {
- get {
- if (_SetItemResolver == null) {
- _SetItemResolver = MakeIndexerResolver(true);
- }
- return _SetItemResolver;
- }
- }
- private static Func<MemberBinder/*!*/, Type/*!*/, MemberGroup/*!*/> _SetItemResolver;
-
- #endregion
-
- /// <summary>
- /// Provides a resolution for __str__.
- /// </summary>
- private static MemberGroup/*!*/ StringResolver(MemberBinder/*!*/ binder, Type/*!*/ type) {
- if (type != typeof(double) && type != typeof(float)
- #if FEATURE_NUMERICS
- && type != typeof(Complex)
- #endif
- ) {
- MethodInfo tostr = type.GetMethod("ToString", ReflectionUtils.EmptyTypes);
- if (tostr != null && tostr.DeclaringType != typeof(object)) {
- return GetInstanceOpsMethod(type, "ToStringMethod");
- }
- }
-
- return MemberGroup.EmptyGroup;
- }
-
- /// <summary>
- /// Provides a resolution for __repr__
- /// </summary>
- private static MemberGroup/*!*/ ReprResolver(MemberBinder/*!*/ binder, Type/*!*/ type) {
- // __repr__ for normal .NET types is special, if we're a Python type then
- // we'll use one of the built-in reprs (from object or from the type)
- if (!PythonBinder.IsPythonType(type) &&
- (!type.IsSealed() || !type.IsAbstract())) { // static types don't get __repr__
- // check and see if __repr__ has been overridden by the base type.
- foreach (Type t in binder.GetContributingTypes(type)) {
- if (t == typeof(ObjectOps) && type != typeof(object)) {
- break;
- }
-
- if (t.GetMember("__repr__").Length > 0) {
- // type has a specific __repr__ overload, pick it up normally later
- return MemberGroup.EmptyGroup;
- }
- }
-
- // no override, pick up the default fancy .NET __repr__
- return binder.GetBaseInstanceMethod(type, "FancyRepr");
- }
-
- return MemberGroup.EmptyGroup;
- }
-
- #if FEATURE_SERIALIZATION
- private static MemberGroup/*!*/ SerializationResolver(MemberBinder/*!*/ binder, Type/*!*/ type) {
- if (type.IsSerializable && !PythonBinder.IsPythonType(type)) {
-
- string methodName = "__reduce_ex__";
-
- if (!TypeOverridesMethod(binder, type, methodName)) {
- return GetInstanceOpsMethod(type, "SerializeReduce");
- }
- }
-
- return MemberGroup.EmptyGroup;
- }
- #endif
-
- #if !SILVERLIGHT
- /// <summary>
- /// Helper to see if the type explicitly overrides the method. This ignores members
- /// defined on object.
- /// </summary>
- private static bool TypeOverridesMethod(MemberBinder/*!*/ binder, Type/*!*/ type, string/*!*/ methodName) {
- // check and see if the method has been overridden by the base type.
- foreach (Type t in binder.GetContributingTypes(type)) {
- if (!PythonBinder.IsPythonType(type) && t == typeof(ObjectOps) && type != typeof(object)) {
- break;
- }
-
- MemberInfo[] reduce = t.GetMember(methodName);
- if (reduce.Length > 0) {
- // type has a specific overload
- return true;
- }
- }
- return false;
- }
- #endif
-
- /// <summary>
- /// Provides a resolution for __hash__, first looking for IStructuralEquatable.GetHashCode,
- /// then IValueEquality.GetValueHashCode.
- /// </summary>
- private static MemberGroup/*!*/ HashResolver(MemberBinder/*!*/ binder, Type/*!*/ type) {
- #if FEATURE_VALUE_EQUALITY
- if (typeof(IStructuralEquatable).IsAssignableFrom(type) && !type.IsInterface) {
- #else
- if ((typeof(IStructuralEquatable).IsAssignableFrom(type) ||
- typeof(IValueEquality).IsAssignableFrom(type)) && !type.IsInterface) {
- #endif
- // check and see if __hash__ has been overridden by the base type.
- foreach (Type t in binder.GetContributingTypes(type)) {
- // if it's defined on object, it's not overridden
- if (t == typeof(ObjectOps) || t == typeof(object)) {
- break;
- }
-
- MemberInfo[] hash = t.GetMember("__hash__");
- if (hash.Length > 0) {
- return MemberGroup.EmptyGroup;
- }
- }
-
- #if FEATURE_VALUE_EQUALITY
- return GetInstanceOpsMethod(type, "StructuralHashMethod");
- #else
- if (typeof(IStructuralEquatable).IsAssignableFrom(type)) {
- return GetInstanceOpsMethod(type, "StructuralHashMethod");
- }
-
- if (typeof(IValueEquality).IsAssignableFrom(type)) {
- return new MemberGroup(typeof(IValueEquality).GetMethod("GetValueHashCode"));
- }
- #endif
- }
-
- // otherwise we'll pick up __hash__ from ObjectOps which will call .NET's .GetHashCode therefore
- // we don't explicitly search to see if the object overrides GetHashCode here.
- return MemberGroup.EmptyGroup;
- }
-
- /// <summary>
- /// Provides a resolution for __new__. For standard .NET types __new__ resolves to their
- /// constructor. For Python types they inherit __new__ from their base class.
- ///
- /// TODO: Can we just always fallback to object.__new__? If not why not?
- /// </summary>
- private static MemberGroup/*!*/ NewResolver(MemberBinder/*!*/ binder, Type/*!*/ type) {
- if (type.IsSealed && type.IsAbstract) {
- // static types don't have __new__
- return MemberGroup.EmptyGroup;
- }
-
- bool isPythonType = typeof(IPythonObject).IsAssignableFrom(type);
-
- // check and see if __new__ has been overridden by the base type.
- foreach (Type t in binder.GetContributingTypes(type)) {
- if (!isPythonType && t == typeof(ObjectOps) && type != typeof(object)) {
- break;
- }
-
- MemberInfo[] news = t.GetMember("__new__");
- if (news.Length > 0) {
- // type has a specific __new__ overload, return that for the constructor
- return GetExtensionMemberGroup(type, news);
- }
- }
-
- // TODO: CompilerHelpers.GetConstructors(type, binder.DomainManager.Configuration.PrivateBinding, true);
- var ctors = CompilerHelpers.FilterConstructorsToPublicAndProtected(
- type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
- ).ToArray();
-
- // type has no Python __new__, just return the .NET constructors if they have
- // a custom new
- if (!PythonTypeOps.IsDefaultNew(ctors)) {
- return new MemberGroup(ctors);
- }
-
- // if no ctor w/ parameters are defined, fall back to object.__new__ which
- // will ignore all the extra arguments allowing the user to just override
- // __init__.
- return MemberGroup.EmptyGroup;
- }
-
- internal static MemberGroup GetExtensionMemberGroup(Type type, MemberInfo[] news) {
- List<MemberTracker> mts = new List<MemberTracker>();
- foreach (MemberInfo mi in news) {
- if (mi.MemberType == MemberTypes.Method) {
- if (mi.DeclaringType.IsAssignableFrom(type)) {
- mts.Add(MethodTracker.FromMemberInfo(mi));
- } else {
- mts.Add(MethodTracker.FromMemberInfo(mi, type));
- }
- }
- }
-
- return new MemberGroup(mts.ToArray());
- }
-
- /// <summary>
- /// Provides a resolution for next
- /// </summary>
- private static MemberGroup/*!*/ NextResolver(MemberBinder/*!*/ binder, Type/*!*/ type) {
- if (typeof(IEnumerator).IsAssignableFrom(type)) {
- return GetInstanceOpsMethod(type, "NextMethod");
- }
-
- return MemberGroup.EmptyGroup;
- }
-
- /// <summary>
- /// Provides a resolution for __len__
- /// </summary>
- private static MemberGroup/*!*/ LengthResolver(MemberBinder/*!*/ binder, Type/*!*/ type) {
- if (!type.IsDefined(typeof(DontMapICollectionToLenAttribute), true)) {
- if (binder.GetInterfaces(type).Contains(typeof(ICollection))) {
- return GetInstanceOpsMethod(type, "LengthMethod");
- }
-
- foreach (Type t in binder.GetInterfaces(type)) {
- if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ICollection<>)) {
- MethodInfo genMeth = typeof(InstanceOps).GetMethod("GenericLengthMethod");
- return new MemberGroup(
- MethodTracker.FromMemberInfo(genMeth.MakeGenericMethod(t.GetGenericArguments()), type)
- );
- }
- }
- }
-
- return MemberGroup.EmptyGroup;
- }
-
- /// <summary>
- /// Provides a resolution for __iter__
- /// </summary>
- private static MemberGroup/*!*/ IterResolver(MemberBinder/*!*/ binder, Type/*!*/ type) {
- if (type == typeof(string)) {
- // __iter__ is only exposed in 3.0
- if (binder.Binder.Context.PythonOptions.Python30) {
- return GetInstanceOpsMethod(type, "IterMethodForString");
- }
- return MemberGroup.EmptyGroup;
- }
-
- if (typeof(Bytes).IsAssignableFrom(type)) {
- // __iter__ is only exposed in 3.0
- if (binder.Binder.Context.PythonOptions.Python30) {
- return GetInstanceOpsMethod(type, "IterMethodForBytes");
- }
- return MemberGroup.EmptyGroup;
- }
-
- foreach (Type t in binder.GetContributingTypes(type)) {
- MemberInfo[] news = t.GetMember("__iter__");
- if (news.Length > 0) {
- // type has a specific __iter__ overload, we'll pick it up later
- return MemberGroup.EmptyGroup;
- }
- }
-
- if (!type.IsDefined(typeof(DontMapIEnumerableToIterAttribute), true)) {
- // no special __iter__, use the default.
- if (typeof…
Large files files are truncated, but you can click here to view the full file