diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 27151b8cbe..041b677d39 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -2536,7 +2536,7 @@ write_module_class(ostream &out, Object *obj) { } } - if (NeedsARichCompareFunction(obj->_itype)) { + if (NeedsARichCompareFunction(obj->_itype) || slots.count("tp_compare")) { out << "//////////////////\n"; out << "// A rich comparison function\n"; out << "// " << ClassName << "\n"; @@ -2547,7 +2547,6 @@ write_module_class(ostream &out, Object *obj) { out << " return nullptr;\n"; out << " }\n\n"; - out << " switch (op) {\n"; for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) { std::set remaps; Function *func = (*fi); @@ -2564,21 +2563,28 @@ write_module_class(ostream &out, Object *obj) { } } const string &fname = func->_ifunc.get_name(); + const char *op_type; if (fname == "operator <") { - out << " case Py_LT:\n"; + op_type = "Py_LT"; } else if (fname == "operator <=") { - out << " case Py_LE:\n"; + op_type = "Py_LE"; } else if (fname == "operator ==") { - out << " case Py_EQ:\n"; + op_type = "Py_EQ"; } else if (fname == "operator !=") { - out << " case Py_NE:\n"; + op_type = "Py_NE"; } else if (fname == "operator >") { - out << " case Py_GT:\n"; + op_type = "Py_GT"; } else if (fname == "operator >=") { - out << " case Py_GE:\n"; + op_type = "Py_GE"; } else { continue; } + if (!has_local_richcompare) { + out << " switch (op) {\n"; + has_local_richcompare = true; + } + out << " case " << op_type << ":\n"; + out << " {\n"; string expected_params; @@ -2587,14 +2593,15 @@ write_module_class(ostream &out, Object *obj) { out << " break;\n"; out << " }\n"; - has_local_richcompare = true; } - out << " }\n\n"; - - out << " if (_PyErr_OCCURRED()) {\n"; - out << " PyErr_Clear();\n"; - out << " }\n\n"; + if (has_local_richcompare) { + // End of switch block + out << " }\n\n"; + out << " if (_PyErr_OCCURRED()) {\n"; + out << " PyErr_Clear();\n"; + out << " }\n\n"; + } if (slots.count("tp_compare")) { // A lot of Panda code depends on comparisons being done via the @@ -2624,6 +2631,7 @@ write_module_class(ostream &out, Object *obj) { out << " case Py_GE:\n"; out << " return PyBool_FromLong(cmpval >= 0);\n"; out << " }\n"; + has_local_richcompare = true; } out << " Py_INCREF(Py_NotImplemented);\n"; @@ -2850,7 +2858,7 @@ write_module_class(ostream &out, Object *obj) { out << "#else\n"; if (has_hash_compare) { write_function_slot(out, 4, slots, "tp_compare", - "&DTOOL_PyObject_ComparePointers"); + "&DtoolInstance_ComparePointers"); } else { out << " nullptr, // tp_compare\n"; } @@ -2880,7 +2888,7 @@ write_module_class(ostream &out, Object *obj) { // hashfunc tp_hash; if (has_hash_compare) { - write_function_slot(out, 4, slots, "tp_hash", "&DTOOL_PyObject_HashPointer"); + write_function_slot(out, 4, slots, "tp_hash", "&DtoolInstance_HashPointer"); } else { out << " nullptr, // tp_hash\n"; } @@ -2951,7 +2959,7 @@ write_module_class(ostream &out, Object *obj) { } else if (has_hash_compare) { // All hashable types need to be comparable. out << "#if PY_MAJOR_VERSION >= 3\n"; - out << " &DTOOL_PyObject_RichCompare,\n"; + out << " &DtoolInstance_RichComparePointers,\n"; out << "#else\n"; out << " nullptr, // tp_richcompare\n"; out << "#endif\n"; diff --git a/dtool/src/interrogatedb/dtool_super_base.cxx b/dtool/src/interrogatedb/dtool_super_base.cxx index d5197b3c76..9170af9d4f 100644 --- a/dtool/src/interrogatedb/dtool_super_base.cxx +++ b/dtool/src/interrogatedb/dtool_super_base.cxx @@ -79,13 +79,13 @@ EXPORT_THIS Dtool_PyTypedObject Dtool_DTOOL_SUPER_BASE = { #if PY_MAJOR_VERSION >= 3 nullptr, // tp_compare #else - &DTOOL_PyObject_ComparePointers, + &DtoolInstance_ComparePointers, #endif nullptr, // tp_repr nullptr, // tp_as_number nullptr, // tp_as_sequence nullptr, // tp_as_mapping - &DTOOL_PyObject_HashPointer, + &DtoolInstance_HashPointer, nullptr, // tp_call nullptr, // tp_str PyObject_GenericGetAttr, @@ -96,7 +96,7 @@ EXPORT_THIS Dtool_PyTypedObject Dtool_DTOOL_SUPER_BASE = { nullptr, // tp_traverse nullptr, // tp_clear #if PY_MAJOR_VERSION >= 3 - &DTOOL_PyObject_RichCompare, + &DtoolInstance_RichComparePointers, #else nullptr, // tp_richcompare #endif diff --git a/dtool/src/interrogatedb/py_compat.cxx b/dtool/src/interrogatedb/py_compat.cxx index f0dd42cf73..0c1383f983 100644 --- a/dtool/src/interrogatedb/py_compat.cxx +++ b/dtool/src/interrogatedb/py_compat.cxx @@ -16,8 +16,6 @@ #ifdef HAVE_PYTHON -PyTupleObject Dtool_EmptyTuple = {PyVarObject_HEAD_INIT(nullptr, 0)}; - #if PY_MAJOR_VERSION < 3 /** * Given a long or int, returns a size_t, or raises an OverflowError if it is diff --git a/dtool/src/interrogatedb/py_compat.h b/dtool/src/interrogatedb/py_compat.h index 46537114aa..f87c73cf3d 100644 --- a/dtool/src/interrogatedb/py_compat.h +++ b/dtool/src/interrogatedb/py_compat.h @@ -138,11 +138,29 @@ typedef long Py_hash_t; /* Python 3.6 */ -// Used to implement _PyObject_CallNoArg -extern EXPCL_INTERROGATEDB PyTupleObject Dtool_EmptyTuple; - #ifndef _PyObject_CallNoArg -# define _PyObject_CallNoArg(func) PyObject_Call((func), (PyObject *)&Dtool_EmptyTuple, nullptr) +INLINE PyObject *_PyObject_CallNoArg(PyObject *func) { + static PyTupleObject empty_tuple = {PyVarObject_HEAD_INIT(nullptr, 0)}; +#ifdef Py_TRACE_REFS + _Py_AddToAllObjects((PyObject *)&empty_tuple, 0); +#endif + return PyObject_Call(func, (PyObject *)&empty_tuple, nullptr); +} +# define _PyObject_CallNoArg _PyObject_CallNoArg +#endif + +#ifndef _PyObject_FastCall +INLINE PyObject *_PyObject_FastCall(PyObject *func, PyObject **args, Py_ssize_t nargs) { + PyObject *tuple = PyTuple_New(nargs); + for (Py_ssize_t i = 0; i < nargs; ++i) { + PyTuple_SET_ITEM(tuple, i, args[i]); + Py_INCREF(args[i]); + } + PyObject *result = PyObject_Call(func, tuple, nullptr); + Py_DECREF(tuple); + return result; +} +# define _PyObject_FastCall _PyObject_FastCall #endif // Python versions before 3.6 didn't require longlong support to be enabled. @@ -161,6 +179,21 @@ extern EXPCL_INTERROGATEDB PyTupleObject Dtool_EmptyTuple; # define PyDict_GET_SIZE(mp) (((PyDictObject *)mp)->ma_used) #endif +#ifndef Py_RETURN_RICHCOMPARE +# define Py_RETURN_RICHCOMPARE(val1, val2, op) \ + do { \ + switch (op) { \ + NODEFAULT \ + case Py_EQ: if ((val1) == (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ + case Py_NE: if ((val1) != (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ + case Py_LT: if ((val1) < (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ + case Py_GT: if ((val1) > (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ + case Py_LE: if ((val1) <= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ + case Py_GE: if ((val1) >= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ + } \ + } while (0) +#endif + /* Other Python implementations */ // _PyErr_OCCURRED is an undocumented macro version of PyErr_Occurred. diff --git a/dtool/src/interrogatedb/py_panda.I b/dtool/src/interrogatedb/py_panda.I index 7ddf8bec9b..8f889768ea 100644 --- a/dtool/src/interrogatedb/py_panda.I +++ b/dtool/src/interrogatedb/py_panda.I @@ -62,6 +62,37 @@ DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &target_c return false; } +/** + * Function to create a hash from a wrapped Python object. + */ +INLINE Py_hash_t DtoolInstance_HashPointer(PyObject *self) { + if (self != nullptr && DtoolInstance_Check(self)) { + return (Py_hash_t)(intptr_t)DtoolInstance_VOID_PTR(self); + } + return -1; +} + +/** + * Python 2-style comparison function that compares objects by pointer. + */ +INLINE int DtoolInstance_ComparePointers(PyObject *v1, PyObject *v2) { + void *v1_this = DtoolInstance_Check(v1) ? DtoolInstance_VOID_PTR(v1) : nullptr; + void *v2_this = DtoolInstance_Check(v2) ? DtoolInstance_VOID_PTR(v2) : nullptr; + if (v1_this != nullptr && v2_this != nullptr) { + return (v1_this > v2_this) - (v1_this < v2_this); + } else { + return (v1 > v2) - (v1 < v2); + } +} + +/** + * Rich comparison function that compares objects by pointer. + */ +INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, int op) { + int cmpval = DtoolInstance_ComparePointers(v1, v2); + Py_RETURN_RICHCOMPARE(cmpval, 0, op); +} + /** * These functions wrap a pointer for a class that defines get_type_handle(). */ diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index 236c9a5197..becbedfa78 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -143,13 +143,6 @@ DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, return nullptr; } -void *DTOOL_Call_GetPointerThis(PyObject *self) { - if (self != nullptr && DtoolInstance_Check(self)) { - return DtoolInstance_VOID_PTR(self); - } - return nullptr; -} - /** * This is similar to a PyErr_Occurred() check, except that it also checks * Notify to see if an assertion has occurred. If that is the case, then it @@ -588,10 +581,6 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) { return Dtool_Raise_TypeError("PyType_Ready(Dtool_StaticProperty_Type)"); } -#ifdef Py_TRACE_REFS - _Py_AddToAllObjects((PyObject *)&Dtool_EmptyTuple, 0); -#endif - // Initialize the base class of everything. Dtool_PyModuleClassInit_DTOOL_SUPER_BASE(nullptr); } @@ -734,127 +723,6 @@ PyObject *Dtool_AddToDictionary(PyObject *self1, PyObject *args) { return Py_None; } -Py_hash_t DTOOL_PyObject_HashPointer(PyObject *self) { - if (self != nullptr && DtoolInstance_Check(self)) { - return (Py_hash_t)(intptr_t)DtoolInstance_VOID_PTR(self); - } - return -1; -} - -/* Compare v to w. Return - -1 if v < w or exception (PyErr_Occurred() true in latter case). - 0 if v == w. - 1 if v > w. - XXX The docs (C API manual) say the return value is undefined in case - XXX of error. -*/ - -int DTOOL_PyObject_ComparePointers(PyObject *v1, PyObject *v2) { - // try this compare - void *v1_this = DTOOL_Call_GetPointerThis(v1); - void *v2_this = DTOOL_Call_GetPointerThis(v2); - if (v1_this != nullptr && v2_this != nullptr) { // both are our types... - if (v1_this < v2_this) { - return -1; - } - if (v1_this > v2_this) { - return 1; - } - return 0; - } - - // ok self compare... - if (v1 < v2) { - return -1; - } - if (v1 > v2) { - return 1; - } - return 0; -} - -int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2) { - // First try compareTo function.. - PyObject * func = PyObject_GetAttrString(v1, "compare_to"); - if (func == nullptr) { - PyErr_Clear(); - } else { -#if PY_VERSION_HEX >= 0x03060000 - PyObject *res = _PyObject_FastCall(func, &v2, 1); -#else - PyObject *res = nullptr; - PyObject *args = PyTuple_Pack(1, v2); - if (args != nullptr) { - res = PyObject_Call(func, args, nullptr); - Py_DECREF(args); - } -#endif - Py_DECREF(func); - PyErr_Clear(); // just in case the function threw an error - // only use if the function returns an INT... hmm - if (res != nullptr) { - if (PyLong_Check(res)) { - long answer = PyLong_AsLong(res); - Py_DECREF(res); - - // Python really wants us to return strictly -1, 0, or 1. - if (answer < 0) { - return -1; - } else if (answer > 0) { - return 1; - } else { - return 0; - } - } -#if PY_MAJOR_VERSION < 3 - else if (PyInt_Check(res)) { - long answer = PyInt_AsLong(res); - Py_DECREF(res); - - // Python really wants us to return strictly -1, 0, or 1. - if (answer < 0) { - return -1; - } else if (answer > 0) { - return 1; - } else { - return 0; - } - } -#endif - Py_DECREF(res); - } - } - - return DTOOL_PyObject_ComparePointers(v1, v2); -} - -PyObject *DTOOL_PyObject_RichCompare(PyObject *v1, PyObject *v2, int op) { - int cmpval = DTOOL_PyObject_Compare(v1, v2); - bool result; - switch (op) { - NODEFAULT - case Py_LT: - result = (cmpval < 0); - break; - case Py_LE: - result = (cmpval <= 0); - break; - case Py_EQ: - result = (cmpval == 0); - break; - case Py_NE: - result = (cmpval != 0); - break; - case Py_GT: - result = (cmpval > 0); - break; - case Py_GE: - result = (cmpval >= 0); - break; - } - return PyBool_FromLong(result); -} - /** * This is a support function for a synthesized __copy__() method from a C++ * make_copy() method. @@ -875,15 +743,7 @@ PyObject *copy_from_make_copy(PyObject *self, PyObject *noargs) { */ PyObject *copy_from_copy_constructor(PyObject *self, PyObject *noargs) { PyObject *callable = (PyObject *)Py_TYPE(self); - -#if PY_VERSION_HEX >= 0x03060000 - PyObject *result = _PyObject_FastCall(callable, &self, 1); -#else - PyObject *args = PyTuple_Pack(1, self); - PyObject *result = PyObject_Call(callable, args, nullptr); - Py_DECREF(args); -#endif - return result; + return _PyObject_FastCall(callable, &self, 1); } /** diff --git a/dtool/src/interrogatedb/py_panda.h b/dtool/src/interrogatedb/py_panda.h index ed6d063e03..6feb2d6f70 100644 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -195,8 +195,6 @@ EXPCL_INTERROGATEDB void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dt EXPCL_INTERROGATEDB void *DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, int param, const std::string &function_name, bool const_ok, bool report_errors); -EXPCL_INTERROGATEDB void *DTOOL_Call_GetPointerThis(PyObject *self); - EXPCL_INTERROGATEDB bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer); EXPCL_INTERROGATEDB bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef, @@ -205,6 +203,10 @@ EXPCL_INTERROGATEDB bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, template INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into); template INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &classdef); +INLINE Py_hash_t DtoolInstance_HashPointer(PyObject *self); +INLINE int DtoolInstance_ComparePointers(PyObject *v1, PyObject *v2); +INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, int op); + // Functions related to error reporting. EXPCL_INTERROGATEDB bool _Dtool_CheckErrorOccurred(); @@ -331,21 +333,8 @@ EXPCL_INTERROGATEDB PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject // some point.. EXPCL_INTERROGATEDB PyObject *Dtool_AddToDictionary(PyObject *self1, PyObject *args); - -EXPCL_INTERROGATEDB Py_hash_t DTOOL_PyObject_HashPointer(PyObject *obj); - -/* Compare v to w. Return - -1 if v < w or exception (PyErr_Occurred() true in latter case). - 0 if v == w. - 1 if v > w. - XXX The docs (C API manual) say the return value is undefined in case - XXX of error. -*/ - -EXPCL_INTERROGATEDB int DTOOL_PyObject_ComparePointers(PyObject *v1, PyObject *v2); -EXPCL_INTERROGATEDB int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2); - -EXPCL_INTERROGATEDB PyObject *DTOOL_PyObject_RichCompare(PyObject *v1, PyObject *v2, int op); +#define DTOOL_PyObject_HashPointer DtoolInstance_HashPointer +#define DTOOL_PyObject_ComparePointers DtoolInstance_ComparePointers EXPCL_INTERROGATEDB PyObject * copy_from_make_copy(PyObject *self, PyObject *noargs); diff --git a/dtool/src/pystub/pystub.cxx b/dtool/src/pystub/pystub.cxx index 91cad58115..cf617cd6f7 100644 --- a/dtool/src/pystub/pystub.cxx +++ b/dtool/src/pystub/pystub.cxx @@ -192,6 +192,7 @@ extern "C" { EXPCL_PYSTUB int _PyArg_Parse_SizeT(...); EXPCL_PYSTUB int _PyErr_BadInternalCall(...); EXPCL_PYSTUB int _PyLong_AsByteArray(...); + EXPCL_PYSTUB int _PyLong_Sign(...); EXPCL_PYSTUB int _PyObject_CallFunction_SizeT(...); EXPCL_PYSTUB int _PyObject_CallMethod_SizeT(...); EXPCL_PYSTUB int _PyObject_DebugFree(...); @@ -421,6 +422,7 @@ int _PyArg_ParseTupleAndKeywords_SizeT(...) { return 0; }; int _PyArg_Parse_SizeT(...) { return 0; }; int _PyErr_BadInternalCall(...) { return 0; }; int _PyLong_AsByteArray(...) { return 0; }; +int _PyLong_Sign(...) { return 0; }; int _PyObject_CallFunction_SizeT(...) { return 0; }; int _PyObject_CallMethod_SizeT(...) { return 0; }; int _PyObject_DebugFree(...) { return 0; };