Skip to content

Commit de42b75

Browse files
committed
detect Py_TRACE_REFS at runtime and calculate object offsets accordingly
1 parent a2304dc commit de42b75

File tree

7 files changed

+108
-110
lines changed

7 files changed

+108
-110
lines changed

src/runtime/classderived.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,7 @@ internal static IntPtr ToPython(IPythonDerivedType obj)
101101
// collected while Python still has a reference to it.
102102
if (Runtime.Refcount(self.pyHandle) == 1)
103103
{
104-
105-
#if PYTHON_WITH_PYDEBUG
106-
Runtime._Py_NewReference(self.pyHandle);
107-
#endif
104+
Runtime._Py_NewReference(self.ObjectReference);
108105
GCHandle gc = GCHandle.Alloc(self, GCHandleType.Normal);
109106
Marshal.WriteIntPtr(self.pyHandle, ObjectOffset.magic(self.tpHandle), (IntPtr)gc);
110107
self.gcHandle.Free();

src/runtime/converter.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,16 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
511511

512512
internal delegate bool TryConvertFromPythonDelegate(IntPtr pyObj, out object result);
513513

514+
internal static int ToInt32(BorrowedReference value)
515+
{
516+
nint num = Runtime.PyLong_AsSignedSize_t(value);
517+
if (num == -1 && Exceptions.ErrorOccurred())
518+
{
519+
throw new PythonException();
520+
}
521+
return checked((int)num);
522+
}
523+
514524
/// <summary>
515525
/// Convert a Python value to an instance of a primitive managed type.
516526
/// </summary>

src/runtime/interop.cs

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ internal static partial class TypeOffset
7676
internal static class ManagedDataOffsets
7777
{
7878
public static int Magic { get; internal set; }
79-
public static readonly Dictionary<string, int> NameMapping = new Dictionary<string, int>();
8079

8180
static class DataOffsets
8281
{
@@ -95,17 +94,10 @@ static DataOffsets()
9594

9695
static ManagedDataOffsets()
9796
{
98-
NameMapping = TypeOffset.GetOffsets();
99-
10097
FieldInfo[] fields = typeof(DataOffsets).GetFields(BindingFlags.Static | BindingFlags.Public);
10198
size = fields.Length * IntPtr.Size;
10299
}
103100

104-
public static int GetSlotOffset(string name)
105-
{
106-
return NameMapping[name];
107-
}
108-
109101
private static int BaseOffset(IntPtr type)
110102
{
111103
Debug.Assert(type != IntPtr.Zero);
@@ -135,30 +127,15 @@ internal static class OriginalObjectOffsets
135127
{
136128
static OriginalObjectOffsets()
137129
{
138-
int size = IntPtr.Size;
139-
var n = 0; // Py_TRACE_REFS add two pointers to PyObject_HEAD
140-
#if PYTHON_WITH_PYDEBUG
141-
_ob_next = 0;
142-
_ob_prev = 1 * size;
143-
n = 2;
144-
#endif
145-
ob_refcnt = (n + 0) * size;
146-
ob_type = (n + 1) * size;
130+
ob_refcnt = Native.ABI.ObjectHeadOffset;
131+
ob_type = ob_refcnt + IntPtr.Size;
132+
size = ob_type + IntPtr.Size;
147133
}
148134

149135
public static int Size { get { return size; } }
150136

151-
private static readonly int size =
152-
#if PYTHON_WITH_PYDEBUG
153-
4 * IntPtr.Size;
154-
#else
155-
2 * IntPtr.Size;
156-
#endif
157-
158-
#if PYTHON_WITH_PYDEBUG
159-
public static int _ob_next;
160-
public static int _ob_prev;
161-
#endif
137+
private static readonly int size;
138+
162139
public static int ob_refcnt;
163140
public static int ob_type;
164141
}
@@ -168,10 +145,6 @@ internal class ObjectOffset
168145
{
169146
static ObjectOffset()
170147
{
171-
#if PYTHON_WITH_PYDEBUG
172-
_ob_next = OriginalObjectOffsets._ob_next;
173-
_ob_prev = OriginalObjectOffsets._ob_prev;
174-
#endif
175148
ob_refcnt = OriginalObjectOffsets.ob_refcnt;
176149
ob_type = OriginalObjectOffsets.ob_type;
177150

@@ -198,10 +171,6 @@ public static int Size(IntPtr pyType)
198171
return size;
199172
}
200173

201-
#if PYTHON_WITH_PYDEBUG
202-
public static int _ob_next;
203-
public static int _ob_prev;
204-
#endif
205174
public static int ob_refcnt;
206175
public static int ob_type;
207176
private static readonly int size;
@@ -256,17 +225,12 @@ static BytesOffset()
256225
int size = IntPtr.Size;
257226
for (int i = 0; i < fi.Length; i++)
258227
{
259-
fi[i].SetValue(null, i * size);
228+
fi[i].SetValue(null, i * size + Native.ABI.ObjectHeadOffset);
260229
}
261230
}
262231

263232
/* The *real* layout of a type object when allocated on the heap */
264233
//typedef struct _heaptypeobject {
265-
#if PYTHON_WITH_PYDEBUG
266-
/* _PyObject_HEAD_EXTRA defines pointers to support a doubly-linked list of all live heap objects. */
267-
public static int _ob_next = 0;
268-
public static int _ob_prev = 0;
269-
#endif
270234
// PyObject_VAR_HEAD {
271235
// PyObject_HEAD {
272236
public static int ob_refcnt = 0;

src/runtime/native/ABI.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ namespace Python.Runtime.Native
88

99
static class ABI
1010
{
11+
public static int ObjectHeadOffset { get; } = GetRefCountOffset();
12+
1113
internal static void Initialize(Version version, BorrowedReference pyType)
1214
{
1315
string offsetsClassSuffix = string.Format(CultureInfo.InvariantCulture,
@@ -17,20 +19,36 @@ internal static void Initialize(Version version, BorrowedReference pyType)
1719

1820
const string nativeTypeOffsetClassName = "Python.Runtime.NativeTypeOffset";
1921
string className = "Python.Runtime.TypeOffset" + offsetsClassSuffix;
22+
Type nativeOffsetsClass = thisAssembly.GetType(nativeTypeOffsetClassName, throwOnError: false);
2023
Type typeOffsetsClass =
2124
// Try platform native offsets first. It is only present when generated by setup.py
22-
thisAssembly.GetType(nativeTypeOffsetClassName, throwOnError: false)
23-
?? thisAssembly.GetType(className, throwOnError: false);
25+
nativeOffsetsClass ?? thisAssembly.GetType(className, throwOnError: false);
2426
if (typeOffsetsClass is null)
2527
{
2628
var types = thisAssembly.GetTypes().Select(type => type.Name).Where(name => name.StartsWith("TypeOffset"));
2729
string message = $"Searching for {className}, found {string.Join(",", types)}.";
2830
throw new NotSupportedException($"Python ABI v{version} is not supported: {message}");
2931
}
32+
3033
var typeOffsets = (ITypeOffsets)Activator.CreateInstance(typeOffsetsClass);
31-
TypeOffset.Use(typeOffsets);
34+
TypeOffset.Use(typeOffsets, nativeOffsetsClass == null ? ObjectHeadOffset : 0);
3235

3336
ManagedDataOffsets.Magic = Marshal.ReadInt32(pyType.DangerousGetAddress(), TypeOffset.tp_basicsize);
3437
}
38+
39+
static unsafe int GetRefCountOffset()
40+
{
41+
IntPtr tempObject = Runtime.PyList_New(0);
42+
IntPtr* tempPtr = (IntPtr*)tempObject;
43+
int offset = 0;
44+
while(tempPtr[offset] != (IntPtr)1)
45+
{
46+
offset++;
47+
if (offset > 100)
48+
throw new InvalidProgramException("PyObject_HEAD could not be found withing reasonable distance from the start of PyObject");
49+
}
50+
Runtime.XDecref(tempObject);
51+
return offset * IntPtr.Size;
52+
}
3553
}
3654
}

src/runtime/native/TypeOffset.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ static partial class TypeOffset
7474
internal static int tp_str { get; private set; }
7575
internal static int tp_traverse { get; private set; }
7676

77-
internal static void Use(ITypeOffsets offsets)
77+
internal static void Use(ITypeOffsets offsets, int extraHeadOffset)
7878
{
7979
if (offsets is null) throw new ArgumentNullException(nameof(offsets));
8080

@@ -86,9 +86,12 @@ internal static void Use(ITypeOffsets offsets)
8686

8787
var sourceProperty = typeof(ITypeOffsets).GetProperty(offsetProperty.Name);
8888
int value = (int)sourceProperty.GetValue(offsets, null);
89+
value += extraHeadOffset;
8990
offsetProperty.SetValue(obj: null, value: value, index: null);
9091
}
9192

93+
NameMapping = GetOffsets();
94+
9295
ValidateUnusedTypeOffsetProperties(offsetProperties);
9396
ValidateRequiredOffsetsPresent(offsetProperties);
9497
}
@@ -167,5 +170,12 @@ static void ValidateRequiredOffsetsPresent(PropertyInfo[] offsetProperties)
167170
Debug.Assert(missing.Count == 0,
168171
"Missing slots: " + string.Join(", ", missing));
169172
}
173+
174+
static Dictionary<string, int> NameMapping;
175+
176+
public static int GetSlotOffset(string name)
177+
{
178+
return NameMapping[name];
179+
}
170180
}
171181
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy