X Tutup
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; namespace Python.Runtime { internal static class OperatorMethod { /// /// Maps the compiled method name in .NET CIL (e.g. op_Addition) to /// the equivalent Python operator (e.g. __add__) as well as the offset /// that identifies that operator's slot (e.g. nb_add) in heap space. /// public static Dictionary OpMethodMap { get; private set; } public static Dictionary ComparisonOpMap { get; private set; } public readonly struct SlotDefinition { public SlotDefinition(string methodName, int typeOffset) { MethodName = methodName; TypeOffset = typeOffset; } public string MethodName { get; } public int TypeOffset { get; } } private static PyObject? _opType; static OperatorMethod() { // .NET operator method names are documented at: // https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/operator-overloads // Python operator methods and slots are documented at: // https://docs.python.org/3/c-api/typeobj.html // TODO: Rich compare, inplace operator support OpMethodMap = new Dictionary { ["op_Addition"] = new SlotDefinition("__add__", TypeOffset.nb_add), ["op_Subtraction"] = new SlotDefinition("__sub__", TypeOffset.nb_subtract), ["op_Multiply"] = new SlotDefinition("__mul__", TypeOffset.nb_multiply), ["op_Division"] = new SlotDefinition("__truediv__", TypeOffset.nb_true_divide), ["op_Modulus"] = new SlotDefinition("__mod__", TypeOffset.nb_remainder), ["op_BitwiseAnd"] = new SlotDefinition("__and__", TypeOffset.nb_and), ["op_BitwiseOr"] = new SlotDefinition("__or__", TypeOffset.nb_or), ["op_ExclusiveOr"] = new SlotDefinition("__xor__", TypeOffset.nb_xor), ["op_LeftShift"] = new SlotDefinition("__lshift__", TypeOffset.nb_lshift), ["op_RightShift"] = new SlotDefinition("__rshift__", TypeOffset.nb_rshift), ["op_OnesComplement"] = new SlotDefinition("__invert__", TypeOffset.nb_invert), ["op_UnaryNegation"] = new SlotDefinition("__neg__", TypeOffset.nb_negative), ["op_UnaryPlus"] = new SlotDefinition("__pos__", TypeOffset.nb_positive), ["__int__"] = new SlotDefinition("__int__", TypeOffset.nb_int), ["__float__"] = new SlotDefinition("__float__", TypeOffset.nb_float), ["__index__"] = new SlotDefinition("__index__", TypeOffset.nb_index), }; ComparisonOpMap = new Dictionary { ["op_Equality"] = "__eq__", ["op_Inequality"] = "__ne__", ["op_LessThanOrEqual"] = "__le__", ["op_GreaterThanOrEqual"] = "__ge__", ["op_LessThan"] = "__lt__", ["op_GreaterThan"] = "__gt__", }; } public static void Initialize() { _opType = GetOperatorType(); } public static void Shutdown() { if (_opType != null) { _opType.Dispose(); _opType = null; } } public static bool IsOperatorMethod(MethodBase method) { if (!method.IsSpecialName && !method.IsOpsHelper()) { return false; } return OpMethodMap.ContainsKey(method.Name) || ComparisonOpMap.ContainsKey(method.Name); } public static bool IsComparisonOp(MethodBase method) { return ComparisonOpMap.ContainsKey(method.Name); } /// /// For the operator methods of a CLR type, set the special slots of the /// corresponding Python type's operator methods. /// public static void FixupSlots(BorrowedReference pyType, Type clrType) { Debug.Assert(_opType != null); var operatorCandidates = GetOperatorCandidates(clrType); foreach (var method in operatorCandidates) { // We only want to override slots for operators excluding // comparison operators, which are handled by ClassBase.tp_richcompare. if (!OpMethodMap.ContainsKey(method.Name)) { continue; } int offset = OpMethodMap[method.Name].TypeOffset; // Copy the default implementation of e.g. the nb_add slot, // which simply calls __add__ on the type. IntPtr func = Util.ReadIntPtr(_opType!, offset); // Write the slot definition of the target Python type, so // that we can later modify __add___ and it will be called // when used with a Python operator. // https://tenthousandmeters.com/blog/python-behind-the-scenes-6-how-python-object-system-works/ Util.WriteIntPtr(pyType, offset, func); } } static IEnumerable GetOperatorCandidates(Type clrType) { const BindingFlags flags = BindingFlags.Public | BindingFlags.Static; if (clrType.IsEnum) { return typeof(EnumOps<>).MakeGenericType(clrType).GetMethods(flags) .Concat(typeof(FlagEnumOps<>).MakeGenericType(clrType).GetMethods(flags)); } return clrType.GetMethods(flags); } public static string GetPyMethodName(string clrName) { if (OpMethodMap.ContainsKey(clrName)) { return OpMethodMap[clrName].MethodName; } else { return ComparisonOpMap[clrName]; } } private static string GenerateDummyCode() { var sb = new StringBuilder(); sb.AppendLine("class OperatorMethod(object):"); foreach (var item in OpMethodMap.Values) { string def = string.Format(" def {0}(self, other): pass", item.MethodName); sb.AppendLine(def); } return sb.ToString(); } private static PyObject GetOperatorType() { using var locals = new PyDict(); // A hack way for getting typeobject.c::slotdefs string code = GenerateDummyCode(); // The resulting OperatorMethod class is stored in a PyDict. PythonEngine.Exec(code, null, locals); // Return the class itself, which is a type. return locals.GetItem("OperatorMethod"); } public static string ReversePyMethodName(string pyName) { return pyName.Insert(2, "r"); } /// /// Check if the method should have a reversed operation. /// /// The operator method. /// public static bool HaveReverse(MethodBase method) { var pi = method.GetParameters(); return OpMethodMap.ContainsKey(method.Name) && pi.Length == 2; } public static void FilterMethods(MethodBase[] methods, out MethodBase[] forwardMethods, out MethodBase[] reverseMethods) { var forwardMethodsList = new List(); var reverseMethodsList = new List(); foreach (var method in methods) { forwardMethodsList.Add(method); if (HaveReverse(method)) { reverseMethodsList.Add(method); } } forwardMethods = forwardMethodsList.ToArray(); reverseMethods = reverseMethodsList.ToArray(); } } }
X Tutup