diff --git a/src/core/IronPython.Modules/_ctypes/INativeType.cs b/src/core/IronPython.Modules/_ctypes/INativeType.cs index a3a99f166..18d4834cd 100644 --- a/src/core/IronPython.Modules/_ctypes/INativeType.cs +++ b/src/core/IronPython.Modules/_ctypes/INativeType.cs @@ -50,7 +50,7 @@ int Alignment { /// Serializes the provided value into the specified address at the given /// offset. /// - object SetValue(MemoryHolder/*!*/ address, int offset, object value); + object? SetValue(MemoryHolder/*!*/ address, int offset, object value); /// /// Gets the .NET type which is used when calling or returning the value @@ -68,12 +68,12 @@ int Alignment { /// Emits marshalling of an object from Python to native code. This produces the /// native type from the Python type. /// - MarshalCleanup EmitMarshalling(ILGenerator/*!*/ method, LocalOrArg/*!*/ argIndex, List/*!*/ constantPool, int constantPoolArgument); + MarshalCleanup? EmitMarshalling(ILGenerator/*!*/ method, LocalOrArg/*!*/ argIndex, List/*!*/ constantPool, int constantPoolArgument); /// /// Emits marshalling from native code to Python code This produces the python type /// from the native type. This is used for return values and parameters - /// to Python callable objects that are passed back out to native code. + /// to Python callable objects that are passed back out of native code. /// void EmitReverseMarshalling(ILGenerator/*!*/ method, LocalOrArg/*!*/ value, List/*!*/ constantPool, int constantPoolArgument); diff --git a/src/core/IronPython.Modules/_ctypes/StructType.cs b/src/core/IronPython.Modules/_ctypes/StructType.cs index e4ffdafee..b37878ec2 100644 --- a/src/core/IronPython.Modules/_ctypes/StructType.cs +++ b/src/core/IronPython.Modules/_ctypes/StructType.cs @@ -2,15 +2,16 @@ // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. +#nullable enable + #if FEATURE_CTYPES using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Reflection.Emit; -using System.Runtime.InteropServices; using System.Text; using Microsoft.Scripting; @@ -19,6 +20,7 @@ using IronPython.Runtime.Operations; using IronPython.Runtime.Types; + namespace IronPython.Modules { /// /// Provides support for interop with native code from Python code. @@ -30,15 +32,16 @@ public static partial class CTypes { /// [PythonType, PythonHidden] public class StructType : PythonType, INativeType { - internal Field[] _fields; + [DisallowNull] + internal Field[]? _fields; // not null after type construction completes private int? _size, _alignment, _pack; - private static readonly Field[] _emptyFields = System.Array.Empty(); // fields were never initialized before a type was created + private static readonly Field[] _emptyFields = []; // fields were never initialized before a type was created - public StructType(CodeContext/*!*/ context, string name, PythonTuple bases, PythonDictionary members) + public StructType(CodeContext/*!*/ context, [NotNone] string name, [NotNone] PythonTuple bases, [NotNone] PythonDictionary members) : base(context, name, bases, members) { foreach (PythonType pt in ResolutionOrder) { - StructType st = pt as StructType; + StructType? st = pt as StructType; if (st != this) { st?.EnsureFinal(); } @@ -71,11 +74,11 @@ private StructType(Type underlyingSystemType) : base(underlyingSystemType) { } - public static ArrayType/*!*/ operator *(StructType type, int count) { + public static ArrayType/*!*/ operator *([NotNone] StructType type, int count) { return MakeArrayType(type, count); } - public static ArrayType/*!*/ operator *(int count, StructType type) { + public static ArrayType/*!*/ operator *(int count, [NotNone] StructType type) { return MakeArrayType(type, count); } @@ -93,13 +96,13 @@ public _Structure from_address(CodeContext/*!*/ context, IntPtr ptr) { return res; } - public _Structure from_buffer(CodeContext/*!*/ context, object/*?*/ data, int offset = 0) { + public _Structure from_buffer(CodeContext/*!*/ context, object? data, int offset = 0) { _Structure res = (_Structure)CreateInstance(context); res.InitializeFromBuffer(data, offset, ((INativeType)this).Size); return res; } - public _Structure from_buffer_copy(CodeContext/*!*/ context, object/*?*/ data, int offset = 0) { + public _Structure from_buffer_copy(CodeContext/*!*/ context, object? data, int offset = 0) { _Structure res = (_Structure)CreateInstance(context); res.InitializeFromBufferCopy(data, offset, ((INativeType)this).Size); return res; @@ -110,19 +113,19 @@ public _Structure from_buffer_copy(CodeContext/*!*/ context, object/*?*/ data, i /// /// Structures just return themselves. /// - public object from_param(object obj) { + public object from_param(object? obj) { if (!Builtin.isinstance(obj, this)) { - throw PythonOps.TypeError("expected {0} instance got {1}", Name, PythonOps.GetPythonTypeName(obj)); + throw PythonOps.TypeError("expected {0} instance, got {1}", Name, PythonOps.GetPythonTypeName(obj)); } - return obj; + return obj!; } - public object in_dll(object library, string name) { + public object in_dll(object? library, [NotNone] string name) { throw new NotImplementedException("in dll"); } - public new virtual void __setattr__(CodeContext/*!*/ context, string name, object value) { + public new virtual void __setattr__(CodeContext/*!*/ context, [NotNone] string name, object? value) { if (name == "_fields_") { lock (this) { if (_fields != null) { @@ -160,7 +163,7 @@ object INativeType.GetValue(MemoryHolder/*!*/ owner, object readingFrom, int off return res; } - object INativeType.SetValue(MemoryHolder/*!*/ address, int offset, object value) { + object? INativeType.SetValue(MemoryHolder/*!*/ address, int offset, object value) { try { return SetValueInternal(address, offset, value); } catch (ArgumentTypeException e) { @@ -174,9 +177,9 @@ object INativeType.SetValue(MemoryHolder/*!*/ address, int offset, object value) } } - internal object SetValueInternal(MemoryHolder address, int offset, object value) { - IList init = value as IList; - if (init != null) { + internal object? SetValueInternal(MemoryHolder address, int offset, object value) { + if (value is IList init) { + EnsureFinal(); if (init.Count > _fields.Length) { throw PythonOps.TypeError("too many initializers"); } @@ -184,14 +187,11 @@ internal object SetValueInternal(MemoryHolder address, int offset, object value) for (int i = 0; i < init.Count; i++) { _fields[i].SetValue(address, offset, init[i]); } + } else if (value is CData data) { + data.MemHolder.CopyTo(address, offset, data.Size); + return data.MemHolder.EnsureObjects(); } else { - CData data = value as CData; - if (data != null) { - data.MemHolder.CopyTo(address, offset, data.Size); - return data.MemHolder.EnsureObjects(); - } else { - throw new NotImplementedException("set value"); - } + throw new NotImplementedException("set value"); } return null; } @@ -202,7 +202,7 @@ internal object SetValueInternal(MemoryHolder address, int offset, object value) return GetMarshalTypeFromSize(_size.Value); } - MarshalCleanup INativeType.EmitMarshalling(ILGenerator/*!*/ method, LocalOrArg argIndex, List/*!*/ constantPool, int constantPoolArgument) { + MarshalCleanup? INativeType.EmitMarshalling(ILGenerator/*!*/ method, LocalOrArg argIndex, List/*!*/ constantPool, int constantPoolArgument) { Type argumentType = argIndex.Type; argIndex.Emit(method); if (argumentType.IsValueType) { @@ -212,8 +212,8 @@ MarshalCleanup INativeType.EmitMarshalling(ILGenerator/*!*/ method, LocalOrArg a method.Emit(OpCodes.Ldarg, constantPoolArgument); method.Emit(OpCodes.Ldc_I4, constantPool.Count - 1); method.Emit(OpCodes.Ldelem_Ref); - method.Emit(OpCodes.Call, typeof(ModuleOps).GetMethod(nameof(ModuleOps.CheckCDataType))); - method.Emit(OpCodes.Call, typeof(CData).GetProperty(nameof(CData.UnsafeAddress)).GetGetMethod()); + method.Emit(OpCodes.Call, typeof(ModuleOps).GetMethod(nameof(ModuleOps.CheckCDataType))!); + method.Emit(OpCodes.Call, typeof(CData).GetProperty(nameof(CData.UnsafeAddress))!.GetGetMethod()!); method.Emit(OpCodes.Ldobj, ((INativeType)this).GetNativeType()); return null; } @@ -251,24 +251,24 @@ internal static PythonType MakeSystemType(Type underlyingSystemType) { return PythonType.SetPythonType(underlyingSystemType, new StructType(underlyingSystemType)); } - private void SetFields(object fields) { + [MemberNotNull(nameof(_fields), nameof(_size), nameof(_alignment))] + private void SetFields(object? fields) { lock (this) { - IList list = GetFieldsList(fields); + IList fieldDefList = GetFieldsList(fields); int? bitCount = null; int? curBitCount = null; - INativeType lastType = null; + INativeType? lastType = null; List allFields = GetBaseSizeAlignmentAndFields(out int size, out int alignment); - IList anonFields = GetAnonymousFields(this); + IList? anonFields = GetAnonymousFields(this); - for (int fieldIndex = 0; fieldIndex < list.Count; fieldIndex++) { - object o = list[fieldIndex]; - GetFieldInfo(this, o, out string fieldName, out INativeType cdata, out bitCount); + foreach (object fieldDef in fieldDefList) { + GetFieldInfo(this, fieldDef, out string fieldName, out INativeType cdata, out bitCount); int prevSize = UpdateSizeAndAlignment(cdata, bitCount, lastType, ref size, ref alignment, ref curBitCount); - Field newField = new Field(fieldName, cdata, prevSize, allFields.Count, bitCount, curBitCount - bitCount); + var newField = new Field(fieldName, cdata, prevSize, allFields.Count, bitCount, curBitCount - bitCount); allFields.Add(newField); AddSlot(fieldName, newField); @@ -282,16 +282,18 @@ private void SetFields(object fields) { CheckAnonymousFields(allFields, anonFields); if (bitCount != null) { - size += lastType.Size; + // incomplete last bitfield + // bitCount not null implies at least one bitfield, so at least one iteration of the loop above + size += lastType!.Size; } - _fields = allFields.ToArray(); + _fields = [..allFields]; _size = PythonStruct.Align(size, alignment); _alignment = alignment; } } - internal static void CheckAnonymousFields(List allFields, IList anonFields) { + internal static void CheckAnonymousFields(List allFields, IList? anonFields) { if (anonFields != null) { foreach (string s in anonFields) { bool found = false; @@ -309,9 +311,9 @@ internal static void CheckAnonymousFields(List allFields, IList a } } - internal static IList GetAnonymousFields(PythonType type) { + internal static IList? GetAnonymousFields(PythonType type) { object anonymous; - IList anonFields = null; + IList? anonFields = null; if (type.TryGetBoundAttr(type.Context.SharedContext, type, "_anonymous_", out anonymous)) { anonFields = anonymous as IList; if (anonFields == null) { @@ -323,16 +325,18 @@ internal static IList GetAnonymousFields(PythonType type) { internal static void AddAnonymousFields(PythonType type, List allFields, INativeType cdata, Field newField) { Field[] childFields; - if (cdata is StructType) { - childFields = ((StructType)cdata)._fields; - } else if (cdata is UnionType) { - childFields = ((UnionType)cdata)._fields; + if (cdata is StructType st) { + st.EnsureFinal(); + childFields = st._fields; + } else if (cdata is UnionType un) { + un.EnsureFinal(); + childFields = un._fields; } else { throw PythonOps.TypeError("anonymous field must be struct or union"); } foreach (Field existingField in childFields) { - Field anonField = new Field( + var anonField = new Field( existingField.FieldName, existingField.NativeType, checked(existingField.offset + newField.offset), @@ -347,12 +351,12 @@ internal static void AddAnonymousFields(PythonType type, List allFields, private List GetBaseSizeAlignmentAndFields(out int size, out int alignment) { size = 0; alignment = 1; - List allFields = new List(); - INativeType lastType = null; + List allFields = []; + INativeType? lastType = null; int? totalBitCount = null; foreach (PythonType pt in BaseTypes) { - StructType st = pt as StructType; - if (st != null) { + if (pt is StructType st) { + st.EnsureFinal(); foreach (Field f in st._fields) { allFields.Add(f); UpdateSizeAndAlignment(f.NativeType, f.BitCount, lastType, ref size, ref alignment, ref totalBitCount); @@ -368,7 +372,8 @@ private List GetBaseSizeAlignmentAndFields(out int size, out int alignmen return allFields; } - private int UpdateSizeAndAlignment(INativeType cdata, int? bitCount, INativeType lastType, ref int size, ref int alignment, ref int? totalBitCount) { + private int UpdateSizeAndAlignment(INativeType cdata, int? bitCount, INativeType? lastType, ref int size, ref int alignment, ref int? totalBitCount) { + Debug.Assert(totalBitCount == null || lastType != null); // lastType is null only on the first iteration, when totalBitCount is null as well int prevSize = size; if (bitCount != null) { if (lastType != null && lastType.Size != cdata.Size) { @@ -382,7 +387,7 @@ private int UpdateSizeAndAlignment(INativeType cdata, int? bitCount, INativeType if ((bitCount + totalBitCount + 7) / 8 <= cdata.Size) { totalBitCount = bitCount + totalBitCount; } else { - size += lastType.Size; + size += lastType!.Size; prevSize = size; totalBitCount = bitCount; } @@ -391,7 +396,7 @@ private int UpdateSizeAndAlignment(INativeType cdata, int? bitCount, INativeType } } else { if (totalBitCount != null) { - size += lastType.Size; + size += lastType!.Size; prevSize = size; totalBitCount = null; } @@ -411,6 +416,7 @@ private int UpdateSizeAndAlignment(INativeType cdata, int? bitCount, INativeType return prevSize; } + [MemberNotNull(nameof(_fields), nameof(_size), nameof(_alignment))] internal void EnsureFinal() { if (_fields == null) { SetFields(PythonTuple.EMPTY); @@ -419,6 +425,8 @@ internal void EnsureFinal() { // track that we were initialized w/o fields. _fields = _emptyFields; } + } else if (_size == null || _alignment == null) { + throw new InvalidOperationException("fields initialized w/o size or alignment"); } } @@ -427,19 +435,22 @@ internal void EnsureFinal() { /// from all of our base classes. If later new _fields_ are added we'll be /// initialized and these values will be replaced. /// + [MemberNotNull(nameof(_size), nameof(_alignment))] private void EnsureSizeAndAlignment() { Debug.Assert(_size.HasValue == _alignment.HasValue); - // these are always iniitalized together + // these are always initialized together if (_size == null) { lock (this) { if (_size == null) { - int size, alignment; - GetBaseSizeAlignmentAndFields(out size, out alignment); + GetBaseSizeAlignmentAndFields(out int size, out int alignment); _size = size; _alignment = alignment; } } } + if (_alignment == null) { + throw new InvalidOperationException("size and alignment should always be initialized together"); + } } } } diff --git a/src/core/IronPython.Modules/_ctypes/UnionType.cs b/src/core/IronPython.Modules/_ctypes/UnionType.cs index ce2e0c0b6..afbc95d64 100644 --- a/src/core/IronPython.Modules/_ctypes/UnionType.cs +++ b/src/core/IronPython.Modules/_ctypes/UnionType.cs @@ -2,19 +2,20 @@ // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. +#nullable enable + #if FEATURE_CTYPES using System; -using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection.Emit; -using Microsoft.Scripting; - using IronPython.Runtime; using IronPython.Runtime.Operations; using IronPython.Runtime.Types; + namespace IronPython.Modules { /// /// Provides support for interop with native code from Python code. @@ -25,19 +26,18 @@ public static partial class CTypes { /// [PythonType, PythonHidden] public class UnionType : PythonType, INativeType { - internal Field[] _fields; + internal Field[]? _fields; private int _size, _alignment; - public UnionType(CodeContext/*!*/ context, string name, PythonTuple bases, PythonDictionary members) + public UnionType(CodeContext/*!*/ context, [NotNone] string name, [NotNone] PythonTuple bases, [NotNone] PythonDictionary members) : base(context, name, bases, members) { - object fields; - if (members.TryGetValue("_fields_", out fields)) { + if (members.TryGetValue("_fields_", out object fields)) { SetFields(fields); } } - public new void __setattr__(CodeContext/*!*/ context, string name, object value) { + public new void __setattr__(CodeContext/*!*/ context, [NotNone] string name, object? value) { if (name == "_fields_") { lock (this) { if (_fields != null) { @@ -58,7 +58,7 @@ private UnionType(Type underlyingSystemType) /// /// Converts an object into a function call parameter. /// - public object from_param(object obj) { + public object? from_param(object? obj) { return null; } @@ -66,21 +66,21 @@ internal static PythonType MakeSystemType(Type underlyingSystemType) { return PythonType.SetPythonType(underlyingSystemType, new UnionType(underlyingSystemType)); } - public static ArrayType/*!*/ operator *(UnionType type, int count) { + public static ArrayType/*!*/ operator *([NotNone] UnionType type, int count) { return MakeArrayType(type, count); } - public static ArrayType/*!*/ operator *(int count, UnionType type) { + public static ArrayType/*!*/ operator *(int count, [NotNone] UnionType type) { return MakeArrayType(type, count); } - public _Union from_buffer(CodeContext/*!*/ context, object/*?*/ data, int offset = 0) { + public _Union from_buffer(CodeContext/*!*/ context, object? data, int offset = 0) { _Union res = (_Union)CreateInstance(context); res.InitializeFromBuffer(data, offset, ((INativeType)this).Size); return res; } - public _Union from_buffer_copy(CodeContext/*!*/ context, object/*?*/ data, int offset = 0) { + public _Union from_buffer_copy(CodeContext/*!*/ context, object? data, int offset = 0) { _Union res = (_Union)CreateInstance(context); res.InitializeFromBufferCopy(data, offset, ((INativeType)this).Size); return res; @@ -106,9 +106,9 @@ object INativeType.GetValue(MemoryHolder owner, object readingFrom, int offset, return res; } - object INativeType.SetValue(MemoryHolder address, int offset, object value) { - IList init = value as IList; - if (init != null) { + object? INativeType.SetValue(MemoryHolder address, int offset, object value) { + if (value is IList init) { + EnsureFinal(); if (init.Count > _fields.Length) { throw PythonOps.TypeError("too many initializers"); } @@ -116,14 +116,11 @@ object INativeType.SetValue(MemoryHolder address, int offset, object value) { for (int i = 0; i < init.Count; i++) { _fields[i].SetValue(address, offset, init[i]); } + } else if (value is CData data) { + data.MemHolder.CopyTo(address, offset, data.Size); + return data.MemHolder.EnsureObjects(); } else { - CData data = value as CData; - if (data != null) { - data.MemHolder.CopyTo(address, offset, data.Size); - return data.MemHolder.EnsureObjects(); - } else { - throw new NotImplementedException("Union set value"); - } + throw new NotImplementedException("Union set value"); } return null; } @@ -132,7 +129,7 @@ Type INativeType.GetNativeType() { return GetMarshalTypeFromSize(_size); } - MarshalCleanup INativeType.EmitMarshalling(ILGenerator/*!*/ method, LocalOrArg argIndex, List/*!*/ constantPool, int constantPoolArgument) { + MarshalCleanup? INativeType.EmitMarshalling(ILGenerator/*!*/ method, LocalOrArg argIndex, List/*!*/ constantPool, int constantPoolArgument) { Type argumentType = argIndex.Type; argIndex.Emit(method); if (argumentType.IsValueType) { @@ -142,8 +139,8 @@ MarshalCleanup INativeType.EmitMarshalling(ILGenerator/*!*/ method, LocalOrArg a method.Emit(OpCodes.Ldarg, constantPoolArgument); method.Emit(OpCodes.Ldc_I4, constantPool.Count - 1); method.Emit(OpCodes.Ldelem_Ref); - method.Emit(OpCodes.Call, typeof(ModuleOps).GetMethod(nameof(ModuleOps.CheckCDataType))); - method.Emit(OpCodes.Call, typeof(CData).GetProperty(nameof(CData.UnsafeAddress)).GetGetMethod()); + method.Emit(OpCodes.Call, typeof(ModuleOps).GetMethod(nameof(ModuleOps.CheckCDataType))!); + method.Emit(OpCodes.Call, typeof(CData).GetProperty(nameof(CData.UnsafeAddress))!.GetGetMethod()!); method.Emit(OpCodes.Ldobj, ((INativeType)this).GetNativeType()); return null; } @@ -165,19 +162,19 @@ string INativeType.TypeFormat { #endregion - private void SetFields(object fields) { + [MemberNotNull(nameof(_fields))] + private void SetFields(object? fields) { lock (this) { - IList list = GetFieldsList(fields); - IList anonFields = StructType.GetAnonymousFields(this); + IList fieldDefList = GetFieldsList(fields); + IList? anonFields = StructType.GetAnonymousFields(this); int size = 0, alignment = 1; List allFields = new List();//GetBaseSizeAlignmentAndFields(out size, out alignment); int? bitCount; - for (int fieldIndex = 0; fieldIndex < list.Count; fieldIndex++) { - object o = list[fieldIndex]; + foreach (object fieldDef in fieldDefList) { string fieldName; INativeType cdata; - GetFieldInfo(this, o, out fieldName, out cdata, out bitCount); + GetFieldInfo(this, fieldDef, out fieldName, out cdata, out bitCount); alignment = Math.Max(alignment, cdata.Alignment); size = Math.Max(size, cdata.Size); @@ -192,12 +189,13 @@ private void SetFields(object fields) { StructType.CheckAnonymousFields(allFields, anonFields); - _fields = allFields.ToArray(); + _fields = [..allFields]; _size = PythonStruct.Align(size, alignment); _alignment = alignment; } } + [MemberNotNull(nameof(_fields))] internal void EnsureFinal() { if (_fields == null) { SetFields(PythonTuple.EMPTY); diff --git a/src/core/IronPython.Modules/_ctypes/_ctypes.cs b/src/core/IronPython.Modules/_ctypes/_ctypes.cs index acdd300c6..44a554de9 100644 --- a/src/core/IronPython.Modules/_ctypes/_ctypes.cs +++ b/src/core/IronPython.Modules/_ctypes/_ctypes.cs @@ -633,8 +633,8 @@ private static int CheckBits(INativeType cdata, PythonTuple pt) { /// Shared helper to get the _fields_ list for struct/union and validate it. /// private static IList/*!*/ GetFieldsList(object fields) { - if (!(fields is IList list)) { - throw PythonOps.TypeError("class must be a sequence of pairs"); + if (fields is not IList list) { + throw PythonOps.TypeError("'_fields_' must be a sequence of (name, C type) pairs"); } return list; }