X Tutup
using System; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Serialization; namespace Python.Runtime { /// /// Managed class that provides the implementation for reflected types. /// Managed classes and value types are represented in Python by actual /// Python type objects. Each of those type objects is associated with /// an instance of ClassObject, which provides its implementation. /// [Serializable] internal class ClassObject : ClassBase { internal readonly int NumCtors = 0; internal ClassObject(Type tp) : base(tp) { var _ctors = type.Value.GetConstructors(); NumCtors = _ctors.Length; } /// /// Helper to get docstring from reflected constructor info. /// internal NewReference GetDocString() { if (!type.Valid) { return Exceptions.RaiseTypeError(type.DeletedMessage); } MethodBase[] methods = type.Value.GetConstructors(); var str = ""; foreach (MethodBase t in methods) { if (str.Length > 0) { str += Environment.NewLine; } str += t.ToString(); } return Runtime.PyString_FromString(str); } private static string ConvertFlags(Enum value) { Type primitiveType = value.GetType().GetEnumUnderlyingType(); string format = "X" + (Marshal.SizeOf(primitiveType) * 2).ToString(CultureInfo.InvariantCulture); var primitive = (IFormattable)Convert.ChangeType(value, primitiveType); return "0x" + primitive.ToString(format, null); } private static string ConvertValue(Enum value) { Type primitiveType = value.GetType().GetEnumUnderlyingType(); return Convert.ChangeType(value, primitiveType).ToString()!; } /// /// given an enum, write a __repr__ string formatted in the same /// way as a python repr string. Something like: /// '<Color.GREEN: 2>'; /// with a binary value for [Flags] enums /// /// Instace of the enum object /// private static string GetEnumReprString(Enum inst) { var obType = inst.GetType(); string strValue2 = obType.IsFlagsEnum() ? ConvertFlags(inst) : ConvertValue(inst); var repr = $"<{obType.Name}.{inst}: {strValue2}>"; return repr; } /// /// ClassObject __repr__ implementation. /// public new static NewReference tp_repr(BorrowedReference ob) { if (GetManagedObject(ob) is not CLRObject co) { return Exceptions.RaiseTypeError("invalid object"); } if (co.inst.GetType().IsEnum) { return Runtime.PyString_FromString(GetEnumReprString((Enum)co.inst)); } return ClassBase.tp_repr(ob); } /// /// Implements __new__ for reflected classes and value types. /// static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) { // Sanity check: this ensures a graceful error if someone does // something intentially wrong like use the managed metatype for // a class that is not really derived from a managed class. if (GetManagedObject(tp) is not ClassObject self) { return Exceptions.RaiseTypeError("invalid object"); } if (!self.type.Valid) { return Exceptions.RaiseTypeError(self.type.DeletedMessage); } Type type = self.type.Value; // Primitive types do not have constructors, but they look like // they do from Python. If the ClassObject represents one of the // convertible primitive types, just convert the arg directly. if (type.IsPrimitive) { return NewPrimitive(tp, args, type); } if (type.IsAbstract) { Exceptions.SetError(Exceptions.TypeError, "cannot instantiate abstract class"); return default; } if (type.IsEnum) { return NewEnum(type, args, tp); } if (type == typeof(string)) { return NewString(args, tp); } if (IsGenericNullable(type)) { // Nullable has special handling in .NET runtime. // Invoking its constructor via reflection on an uninitialized instance // does not actually set the object fields. return NewNullable(type, args, kw, tp); } object obj = FormatterServices.GetUninitializedObject(type); return self.NewObjectToPython(obj, tp); } /// /// Construct a new .NET String object from Python args /// /// This manual implementation of all individual relevant constructors /// is required because System.String can't be allocated uninitialized. /// /// Additionally, it implements `String(pythonStr)` /// private static NewReference NewString(BorrowedReference args, BorrowedReference tp) { var argCount = Runtime.PyTuple_Size(args); string? result = null; if (argCount == 1) { BorrowedReference ob = Runtime.PyTuple_GetItem(args, 0); if (Runtime.PyString_Check(ob)) { if (Runtime.GetManagedString(ob) is string val) result = val; } else if (Converter.ToManagedValue(ob, typeof(char[]), out object? arr, false)) { result = new String((char[])arr!); } } else if (argCount == 2) { BorrowedReference p1 = Runtime.PyTuple_GetItem(args, 0); BorrowedReference p2 = Runtime.PyTuple_GetItem(args, 1); if ( Converter.ToManagedValue(p1, typeof(char), out object? chr, false) && Converter.ToManagedValue(p2, typeof(int), out object? count, false) ) { result = new String((char)chr!, (int)count!); } } else if (argCount == 3) { BorrowedReference p1 = Runtime.PyTuple_GetItem(args, 0); BorrowedReference p2 = Runtime.PyTuple_GetItem(args, 1); BorrowedReference p3 = Runtime.PyTuple_GetItem(args, 2); if ( Converter.ToManagedValue(p1, typeof(char[]), out object? arr, false) && Converter.ToManagedValue(p2, typeof(int), out object? offset, false) && Converter.ToManagedValue(p3, typeof(int), out object? length, false) ) { result = new String((char[])arr!, (int)offset!, (int)length!); } } if (result != null) return CLRObject.GetReference(result!, tp); Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments"); return default; } /// /// Create a new Python object for a primitive type /// /// The primitive types are Boolean, Byte, SByte, Int16, UInt16, /// Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, /// and Single. /// /// All numeric types and Boolean can be handled by a simple /// conversion, (U)IntPtr has to be handled separately as we /// do not want to convert them automically to/from integers. /// /// .NET type to construct /// Corresponding Python type /// Constructor arguments private static NewReference NewPrimitive(BorrowedReference tp, BorrowedReference args, Type type) { // TODO: Handle IntPtr if (Runtime.PyTuple_Size(args) != 1) { Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments"); return default; } BorrowedReference op = Runtime.PyTuple_GetItem(args, 0); object? result = null; if (type == typeof(IntPtr)) { if (ManagedType.GetManagedObject(op) is CLRObject clrObject) { switch (clrObject.inst) { case nint val: result = new IntPtr(val); break; case Int64 val: result = new IntPtr(val); break; case Int32 val: result = new IntPtr(val); break; } } else if (Runtime.PyInt_Check(op)) { long? num = Runtime.PyLong_AsLongLong(op); if (num is long n && n >= Runtime.IntPtrMinValue && n <= Runtime.IntPtrMaxValue) { result = new IntPtr(n); } else { Exceptions.SetError(Exceptions.OverflowError, "value not in range for IntPtr"); return default; } } } if (type == typeof(UIntPtr)) { if (ManagedType.GetManagedObject(op) is CLRObject clrObject) { switch (clrObject.inst) { case nuint val: result = new UIntPtr(val); break; case UInt64 val: result = new UIntPtr(val); break; case UInt32 val: result = new UIntPtr(val); break; } } else if (Runtime.PyInt_Check(op)) { ulong? num = Runtime.PyLong_AsUnsignedLongLong(op); if (num is ulong n && n <= Runtime.UIntPtrMaxValue) { result = new UIntPtr(n); } else { Exceptions.SetError(Exceptions.OverflowError, "value not in range for UIntPtr"); return default; } } } if (result == null && !Converter.ToManaged(op, type, out result, true)) { return default; } return CLRObject.GetReference(result!, tp); } protected virtual void SetTypeNewSlot(BorrowedReference pyType, SlotsHolder slotsHolder) { TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.tp_new, new Interop.BBB_N(tp_new_impl), slotsHolder); } public override bool HasCustomNew() { if (base.HasCustomNew()) return true; Type clrType = type.Value; return clrType.IsPrimitive || clrType.IsEnum || clrType == typeof(string) || IsGenericNullable(clrType); } static bool IsGenericNullable(Type type) => type.IsValueType && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); public override void InitializeSlots(BorrowedReference pyType, SlotsHolder slotsHolder) { base.InitializeSlots(pyType, slotsHolder); this.SetTypeNewSlot(pyType, slotsHolder); } protected virtual NewReference NewObjectToPython(object obj, BorrowedReference tp) => CLRObject.GetReference(obj, tp); private static NewReference NewEnum(Type type, BorrowedReference args, BorrowedReference tp) { nint argCount = Runtime.PyTuple_Size(args); bool allowUnchecked = false; if (argCount == 2) { var allow = Runtime.PyTuple_GetItem(args, 1); if (!Converter.ToManaged(allow, typeof(bool), out var allowObj, true) || allowObj is null) { Exceptions.RaiseTypeError("second argument to enum constructor must be a boolean"); return default; } allowUnchecked |= (bool)allowObj; } if (argCount < 1 || argCount > 2) { Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments"); return default; } var op = Runtime.PyTuple_GetItem(args, 0); if (!Converter.ToManaged(op, type.GetEnumUnderlyingType(), out object? result, true)) { return default; } if (!allowUnchecked && !Enum.IsDefined(type, result) && !type.IsFlagsEnum()) { Exceptions.SetError(Exceptions.ValueError, "Invalid enumeration value. Pass True as the second argument if unchecked conversion is desired"); return default; } object enumValue = Enum.ToObject(type, result); return CLRObject.GetReference(enumValue, tp); } private static NewReference NewNullable(Type type, BorrowedReference args, BorrowedReference kw, BorrowedReference tp) { Debug.Assert(IsGenericNullable(type)); if (kw != null) { return Exceptions.RaiseTypeError("System.Nullable constructor does not support keyword arguments"); } nint argsCount = Runtime.PyTuple_Size(args); if (argsCount != 1) { return Exceptions.RaiseTypeError("System.Nullable constructor expects 1 argument, got " + (int)argsCount); } var value = Runtime.PyTuple_GetItem(args, 0); var elementType = type.GetGenericArguments()[0]; return Converter.ToManaged(value, elementType, out var result, setError: true) ? CLRObject.GetReference(result!, tp) : default; } /// /// Implementation of [] semantics for reflected types. This exists /// both to implement the Array[int] syntax for creating arrays and /// to support generic name overload resolution using []. /// public override NewReference type_subscript(BorrowedReference idx) { if (!type.Valid) { return Exceptions.RaiseTypeError(type.DeletedMessage); } // If this type is the Array type, the [] means we need to // construct and return an array type of the given element type. if (type.Value == typeof(Array)) { if (Runtime.PyTuple_Check(idx)) { return Exceptions.RaiseTypeError("type expected"); } Type? t; if (GetManagedObject(idx) is ClassBase c) { t = c.type.Value; } else { t = Converter.GetTypeByAlias(idx); } if (t == null) { return Exceptions.RaiseTypeError("type expected"); } Type a = t.MakeArrayType(); BorrowedReference o = ClassManager.GetClass(a); return new NewReference(o); } // If there are generics in our namespace with the same base name // as the current type, then [] means the caller wants to // bind the generic type matching the given type parameters. Type[]? types = Runtime.PythonArgsToTypeArray(idx); if (types == null) { return Exceptions.RaiseTypeError("type(s) expected"); } Type gtype = AssemblyManager.LookupTypes($"{type.Value.FullName}`{types.Length}").FirstOrDefault(); if (gtype != null) { var g = (GenericType)ClassManager.GetClassImpl(gtype); return g.type_subscript(idx); } return Exceptions.RaiseTypeError("unsubscriptable object"); } } }
X Tutup