From 2048616678e171f121935ccd48e7c71c377032be Mon Sep 17 00:00:00 2001 From: jmlidbetter <53430310+jmlidbetter@users.noreply.github.com> Date: Tue, 24 Sep 2019 13:46:04 +0100 Subject: [PATCH 1/5] Detect python interpreter arch --- src/runtime/runtime.cs | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 75f11492f..fd89798e6 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -155,6 +155,11 @@ public class Runtime /// public static string MachineName { get; private set; } + /// + /// Gets the architecture the python interpreter is using as reported by python's struct.calcsize("P") + /// + public static MachineType PythonArchitecture { get; private set; } + internal static bool IsPython2 = pyversionnumber < 30; internal static bool IsPython3 = pyversionnumber >= 30; @@ -192,6 +197,9 @@ internal static void Initialize(bool initSigs = false) IntPtr op; IntPtr dict; + + InitializePythonArch(); + if (IsPython3) { op = PyImport_ImportModule("builtins"); @@ -371,6 +379,43 @@ private static void InitializePlatformData() Machine = MType; } + /// + /// Initializes the architecture used within the python interpreter + /// + /// For various reasons the python interpreter often has a different + /// architecture to that of the machine. For example on a 64 bit Windows + /// platform the CPython interpreter is compiled with 32 bit longs. + /// This method will allow pythonnet to determine at runtime how big + /// python's longs are. + /// + private static void InitializePythonArch() + { + IntPtr structModule = PyImport_ImportModule("struct"); + IntPtr calcsizeMethod = PyObject_GetAttrString(structModule, "calcsize"); + IntPtr methodArgs = PyTuple_New(1); + IntPtr pString = PyString_FromString("P"); + + if(PyTuple_SetItem(methodArgs, 0, pString) != 0) + { + PythonArchitecture = MachineType.Other; + return; + } + + var result = PyLong_AsLong(PyObject_Call(calcsizeMethod, methodArgs, IntPtr.Zero)); + + switch(result){ + case 4: + PythonArchitecture = MachineType.i386; + break; + case 8: + PythonArchitecture = MachineType.x86_64; + break; + default: + PythonArchitecture = MachineType.Other; + break; + } + } + internal static void Shutdown() { AssemblyManager.Shutdown(); From d8040fa7a9782620e6b252c9650419c56722eea1 Mon Sep 17 00:00:00 2001 From: jmlidbetter <53430310+jmlidbetter@users.noreply.github.com> Date: Tue, 24 Sep 2019 14:34:39 +0100 Subject: [PATCH 2/5] Updates PyLong_FromUnsignedLong to be interpreter arch aware --- src/runtime/runtime.cs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index fd89798e6..536f0e052 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -1081,8 +1081,24 @@ internal static bool PyLong_Check(IntPtr ob) [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromLong(long value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyLong_FromUnsignedLong(uint value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_FromUnsignedLong")] + internal static extern IntPtr PyLong_FromUnsignedLong32(uint value); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_FromUnsignedLong")] + internal static extern IntPtr PyLong_FromUnsignedLong64(ulong value); + + internal static IntPtr PyLong_FromUnsignedLong(object value) + { + if(PythonArchitecture == MachineType.i386) + return PyLong_FromUnsignedLong32(Convert.ToUInt32(value)); + else if(PythonArchitecture == MachineType.x86_64) + return PyLong_FromUnsignedLong64(Convert.ToUInt64(value)); + else + // Couldn't determine interpreter arch, try 64 bit + return PyLong_FromUnsignedLong64(Convert.ToUInt64(value)); + } [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromDouble(double value); From c337688033f16dad6e12cafd7fa09e3a4759a395 Mon Sep 17 00:00:00 2001 From: jmlidbetter <53430310+jmlidbetter@users.noreply.github.com> Date: Tue, 24 Sep 2019 15:23:39 +0100 Subject: [PATCH 3/5] Makes PyLong_AsUnsignedLong interpreter arch aware --- src/runtime/converter.cs | 15 ++++++++++++++- src/runtime/runtime.cs | 21 +++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 5d8769a73..e7e047419 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -728,7 +728,20 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo } goto type_error; } - uint ui = (uint)Runtime.PyLong_AsUnsignedLong(op); + + uint ui; + try + { + ui = Convert.ToUInt32(Runtime.PyLong_AsUnsignedLong(op)); + } catch (OverflowException) + { + // Probably wasn't an overflow in python but was in C# (e.g. if cpython + // longs are 64 bit then 0xFFFFFFFF + 1 will not overflow in + // PyLong_AsUnsignedLong) + Runtime.XDecref(op); + goto overflow; + } + if (Exceptions.ErrorOccurred()) { diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 536f0e052..b45aff894 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -1115,8 +1115,25 @@ internal static IntPtr PyLong_FromUnsignedLong(object value) [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyLong_AsLong(IntPtr value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint PyLong_AsUnsignedLong(IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_AsUnsignedLong")] + internal static extern uint PyLong_AsUnsignedLong32(IntPtr value); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_AsUnsignedLong")] + internal static extern ulong PyLong_AsUnsignedLong64(IntPtr value); + + internal static object PyLong_AsUnsignedLong(IntPtr value) + { + if (PythonArchitecture == MachineType.i386) + return PyLong_AsUnsignedLong32(value); + else if (PythonArchitecture == MachineType.x86_64) + return PyLong_AsUnsignedLong64(value); + else + return PyLong_AsUnsignedLong64(value); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern long PyLong_AsLongLong(IntPtr value); From fb1e1adbd4427a58f792136326009e2705323fa2 Mon Sep 17 00:00:00 2001 From: jmlidbetter <53430310+jmlidbetter@users.noreply.github.com> Date: Wed, 25 Sep 2019 14:21:06 +0100 Subject: [PATCH 4/5] Updates CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cb0ea96c..b5b11cd77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Fixed runtime that fails loading when using pythonnet in an environment together with Nuitka - Fixes bug where delegates get casts (dotnetcore) +- Determine size of interpreter longs at runtime ## [2.4.0][] From ac9294d6c30bf5c74947c61fcaadebe50fc7acb0 Mon Sep 17 00:00:00 2001 From: jmlidbetter <53430310+jmlidbetter@users.noreply.github.com> Date: Tue, 1 Oct 2019 13:52:06 +0100 Subject: [PATCH 5/5] Gets size of C long from Is32Bit and IsWindows --- src/runtime/runtime.cs | 58 +++--------------------------------------- 1 file changed, 3 insertions(+), 55 deletions(-) diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index b45aff894..4985a57f5 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -155,11 +155,6 @@ public class Runtime /// public static string MachineName { get; private set; } - /// - /// Gets the architecture the python interpreter is using as reported by python's struct.calcsize("P") - /// - public static MachineType PythonArchitecture { get; private set; } - internal static bool IsPython2 = pyversionnumber < 30; internal static bool IsPython3 = pyversionnumber >= 30; @@ -197,9 +192,6 @@ internal static void Initialize(bool initSigs = false) IntPtr op; IntPtr dict; - - InitializePythonArch(); - if (IsPython3) { op = PyImport_ImportModule("builtins"); @@ -379,43 +371,6 @@ private static void InitializePlatformData() Machine = MType; } - /// - /// Initializes the architecture used within the python interpreter - /// - /// For various reasons the python interpreter often has a different - /// architecture to that of the machine. For example on a 64 bit Windows - /// platform the CPython interpreter is compiled with 32 bit longs. - /// This method will allow pythonnet to determine at runtime how big - /// python's longs are. - /// - private static void InitializePythonArch() - { - IntPtr structModule = PyImport_ImportModule("struct"); - IntPtr calcsizeMethod = PyObject_GetAttrString(structModule, "calcsize"); - IntPtr methodArgs = PyTuple_New(1); - IntPtr pString = PyString_FromString("P"); - - if(PyTuple_SetItem(methodArgs, 0, pString) != 0) - { - PythonArchitecture = MachineType.Other; - return; - } - - var result = PyLong_AsLong(PyObject_Call(calcsizeMethod, methodArgs, IntPtr.Zero)); - - switch(result){ - case 4: - PythonArchitecture = MachineType.i386; - break; - case 8: - PythonArchitecture = MachineType.x86_64; - break; - default: - PythonArchitecture = MachineType.Other; - break; - } - } - internal static void Shutdown() { AssemblyManager.Shutdown(); @@ -1091,12 +1046,9 @@ internal static bool PyLong_Check(IntPtr ob) internal static IntPtr PyLong_FromUnsignedLong(object value) { - if(PythonArchitecture == MachineType.i386) + if(Is32Bit || IsWindows) return PyLong_FromUnsignedLong32(Convert.ToUInt32(value)); - else if(PythonArchitecture == MachineType.x86_64) - return PyLong_FromUnsignedLong64(Convert.ToUInt64(value)); else - // Couldn't determine interpreter arch, try 64 bit return PyLong_FromUnsignedLong64(Convert.ToUInt64(value)); } @@ -1125,16 +1077,12 @@ internal static IntPtr PyLong_FromUnsignedLong(object value) internal static object PyLong_AsUnsignedLong(IntPtr value) { - if (PythonArchitecture == MachineType.i386) + if (Is32Bit || IsWindows) return PyLong_AsUnsignedLong32(value); - else if (PythonArchitecture == MachineType.x86_64) - return PyLong_AsUnsignedLong64(value); - else + else return PyLong_AsUnsignedLong64(value); } - - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern long PyLong_AsLongLong(IntPtr value);