From c257ce624e19da30ef8bcf4403a18861c24c7073 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 18 Jul 2014 13:54:38 +0000 Subject: [PATCH] Interrogate overhaul that dramatically increases performance for the simpler methods --- direct/src/showbase/extend_frozen.c | 5 +- dtool/src/dtoolbase/typeHandle.cxx | 4 + dtool/src/dtoolbase/typeHandle.h | 12 +- dtool/src/interrogate/functionRemap.cxx | 104 +- dtool/src/interrogate/functionRemap.h | 4 + dtool/src/interrogate/interfaceMaker.cxx | 11 +- .../src/interrogate/interfaceMakerPython.cxx | 9 +- .../interfaceMakerPythonNative.cxx | 1257 ++++++++++------- .../interrogate/interfaceMakerPythonNative.h | 41 +- dtool/src/interrogate/typeManager.cxx | 5 +- dtool/src/interrogatedb/dtool_super_base.cxx | 2 +- dtool/src/interrogatedb/py_panda.cxx | 26 +- dtool/src/interrogatedb/py_panda.h | 21 +- dtool/src/parser-inc/Python.h | 4 +- dtool/src/pystub/pystub.cxx | 18 + panda/src/pgraph/nodePath_ext.cxx | 2 +- panda/src/putil/typedWritable_ext.cxx | 6 +- 17 files changed, 912 insertions(+), 619 deletions(-) diff --git a/direct/src/showbase/extend_frozen.c b/direct/src/showbase/extend_frozen.c index 15d259783e..1a562f8a63 100644 --- a/direct/src/showbase/extend_frozen.c +++ b/direct/src/showbase/extend_frozen.c @@ -1,3 +1,4 @@ +#define PY_SSIZE_T_CLEAN #include #ifdef _WIN32 @@ -105,7 +106,7 @@ py_extend_frozen_modules(PyObject *self, PyObject *args) { PyObject *tuple; const char *name; const char *code; - int size; + Py_ssize_t size; tuple = PySequence_GetItem(list, i); if (!PyArg_ParseTuple(tuple, "ss#", &name, &code, &size)) { @@ -178,7 +179,7 @@ py_get_frozen_module_code(PyObject *self, PyObject *args) { if (strcmp(PyImport_FrozenModules[i].name, name) == 0) { int is_package = (PyImport_FrozenModules[i].size < 0); return Py_BuildValue("(s#i)", PyImport_FrozenModules[i].code, - abs(PyImport_FrozenModules[i].size), + (Py_ssize_t) abs(PyImport_FrozenModules[i].size), is_package); } ++i; diff --git a/dtool/src/dtoolbase/typeHandle.cxx b/dtool/src/dtoolbase/typeHandle.cxx index c25ee886c7..773cca79ec 100644 --- a/dtool/src/dtoolbase/typeHandle.cxx +++ b/dtool/src/dtoolbase/typeHandle.cxx @@ -16,6 +16,10 @@ #include "typeRegistryNode.h" #include "atomicAdjust.h" +#ifdef HAVE_PYTHON +#include "Python.h" +#endif + // This is initialized to zero by static initialization. TypeHandle TypeHandle::_none; diff --git a/dtool/src/dtoolbase/typeHandle.h b/dtool/src/dtoolbase/typeHandle.h index beecc306ba..5d0bfa34fb 100644 --- a/dtool/src/dtoolbase/typeHandle.h +++ b/dtool/src/dtoolbase/typeHandle.h @@ -66,7 +66,10 @@ class TypedObject; #ifdef HAVE_PYTHON -#include "Python.h" +#ifndef PyObject_HEAD +struct _object; +typedef _object PyObject; +#endif #endif //////////////////////////////////////////////////////////////////// @@ -127,15 +130,15 @@ PUBLISHED: INLINE TypeHandle get_parent_towards(TypeHandle ancestor, TypedObject *object = (TypedObject *)NULL) const; - - INLINE int get_best_parent_from_Set(const std::set< int > &legal_vals) const; + + INLINE int get_best_parent_from_Set(const std::set< int > &legal_vals) const; #ifdef DO_MEMORY_USAGE int get_memory_usage(MemoryClass memory_class) const; void inc_memory_usage(MemoryClass memory_class, int size); void dec_memory_usage(MemoryClass memory_class, int size); #else - INLINE int get_memory_usage(MemoryClass) const { return 0; } + CONSTEXPR int get_memory_usage(MemoryClass) const { return 0; } INLINE void inc_memory_usage(MemoryClass, int) { } INLINE void dec_memory_usage(MemoryClass, int) { } #endif // DO_MEMORY_USAGE @@ -170,4 +173,3 @@ EXPCL_DTOOL ostream &operator << (ostream &out, TypeHandle::MemoryClass mem_clas #include "typeHandle.I" #endif - diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index ec583b082d..b3eefd602f 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -303,26 +303,6 @@ make_wrapper_entry(FunctionIndex function_index) { assert(!iwrapper._parameters.empty()); iwrapper._parameters.front()._parameter_flags |= InterrogateFunctionWrapper::PF_is_this; - - if (_parameters.size() >= 2 && _parameters[1]._name == "self" && - TypeManager::is_pointer_to_PyObject(_parameters[1]._remap->get_orig_type())) { - // Here's a special case. If the first parameter of a nonstatic - // method is a PyObject * called "self", then we will - // automatically fill it in from the this pointer, and remove it - // from the generated parameter list. - _parameters.erase(_parameters.begin() + 1); - _flags |= F_explicit_self; - } - - } else if (_type == T_constructor) { - // We also allow "self" to be passed in to a constructor, even - // though the constructor doesn't normally accept a this pointer. - // But this makes sense to Python programmers. - if (_parameters.size() >= 1 && _parameters[0]._name == "self" && - TypeManager::is_pointer_to_PyObject(_parameters[0]._remap->get_orig_type())) { - _parameters.erase(_parameters.begin() + 0); - _flags |= F_explicit_self; - } } if (!_void_return) { @@ -650,12 +630,32 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } // Check for a special meaning by name and signature. - if (_type == T_normal) { - int first_param = 0; - if (_has_this) { - first_param = 1; - } + int first_param = 0; + if (_has_this) { + first_param = 1; + } + if (_has_this || _type == T_constructor) { + if (_parameters.size() > first_param && _parameters[first_param]._name == "self" && + TypeManager::is_pointer_to_PyObject(_parameters[first_param]._remap->get_orig_type())) { + // Here's a special case. If the first parameter of a nonstatic + // method is a PyObject * called "self", then we will + // automatically fill it in from the this pointer, and remove it + // from the generated parameter list. + _parameters.erase(_parameters.begin() + first_param); + _flags |= F_explicit_self; + } + } + + if (_parameters.size() == first_param) { + _flags |= F_no_args; + } else if (_parameters.size() == first_param + 1) { + _flags |= F_single_arg; + } else { + _flags |= F_varargs; + } + + if (_type == T_normal) { if (fname == "operator []" || fname == "__getitem__") { _flags |= F_getitem; if (_has_this && _parameters.size() == 2) { @@ -670,7 +670,7 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak if (_has_this && _parameters.size() > 2) { if (TypeManager::is_integer(_parameters[1]._remap->get_new_type())) { // Its first parameter is an int parameter, presumably an index. - _flags |= F_setitem_int; + _flags |= (F_setitem_int | F_varargs); } } @@ -681,55 +681,69 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak _flags |= F_size; } - } else if (fname == "make_copy" ) { + } else if (fname == "make_copy") { if (_has_this && _parameters.size() == 1 && TypeManager::is_pointer(_return_type->get_new_type())) { // It receives no parameters, and returns a pointer. _flags |= F_make_copy; } - } else if (fname == "__iter__" ) { + } else if (fname == "__iter__") { if (_has_this && _parameters.size() == 1 && TypeManager::is_pointer(_return_type->get_new_type())) { // It receives no parameters, and returns a pointer. _flags |= F_iter; } - } else if (fname == "__getbuffer__" ) { - if (_has_this && _parameters.size() == 4 && + } else if (fname == "__getbuffer__") { + if (_has_this && _parameters.size() == 3 && TypeManager::is_integer(_return_type->get_new_type()) && - TypeManager::is_pointer_to_PyObject(_parameters[1]._remap->get_orig_type()) && - TypeManager::is_pointer_to_Py_buffer(_parameters[2]._remap->get_orig_type()) && - TypeManager::is_integer(_parameters[3]._remap->get_orig_type())) { + TypeManager::is_pointer_to_Py_buffer(_parameters[1]._remap->get_orig_type()) && + TypeManager::is_integer(_parameters[2]._remap->get_orig_type())) { _flags |= F_getbuffer; } - } else if (fname == "__releasebuffer__" ) { - if (_has_this && _parameters.size() == 3 && - TypeManager::is_pointer_to_PyObject(_parameters[1]._remap->get_orig_type()) && - TypeManager::is_pointer_to_Py_buffer(_parameters[2]._remap->get_orig_type())) { + } else if (fname == "__releasebuffer__") { + if (_has_this && _parameters.size() == 2 && + TypeManager::is_pointer_to_Py_buffer(_parameters[1]._remap->get_orig_type())) { _flags |= F_releasebuffer; } - } else if (fname == "compare_to" ) { + } else if (fname == "compare_to") { if (_has_this && _parameters.size() == 2 && TypeManager::is_integer(_return_type->get_new_type())) { // It receives one parameter, and returns an integer. _flags |= F_compare_to; } + + } else if (fname == "operator ()" || fname == "__call__") { + // Call operators always take keyword arguments. + _flags |= (F_varargs | F_keyword_args); + + } else if (fname == "__setattr__" || fname == "__getattr__") { + // Just to prevent these from getting keyword arguments. + + } else { + if (_flags & F_varargs) { + // Every other method can take keyword arguments, if they + // take more than one argument. + _flags |= F_keyword_args; + } } } else if (_type == T_constructor) { - if (!_has_this && _parameters.size() == 1) { - if (TypeManager::unwrap(_parameters[0]._remap->get_orig_type()) == - TypeManager::unwrap(_return_type->get_orig_type())) { - // If this is the only parameter, and it's the same as the - // "this" type, this is a copy constructor. - _flags |= F_copy_constructor; - } + if (!_has_this && _parameters.size() == 1 && + TypeManager::unwrap(_parameters[0]._remap->get_orig_type()) == + TypeManager::unwrap(_return_type->get_orig_type())) { + // If this is the only parameter, and it's the same as the + // "this" type, this is a copy constructor. + _flags |= F_copy_constructor; + } + // Constructors always take varargs and keyword args. + _flags |= (F_varargs | F_keyword_args); } return true; diff --git a/dtool/src/interrogate/functionRemap.h b/dtool/src/interrogate/functionRemap.h index db23ba0139..f1812009d7 100644 --- a/dtool/src/interrogate/functionRemap.h +++ b/dtool/src/interrogate/functionRemap.h @@ -91,6 +91,10 @@ public: F_getbuffer = 0x0200, F_releasebuffer = 0x0400, F_compare_to = 0x0800, + F_no_args = 0x1000, + F_single_arg = 0x2000, + F_varargs = 0x4000, + F_keyword_args = 0x8000, }; typedef vector Parameters; diff --git a/dtool/src/interrogate/interfaceMaker.cxx b/dtool/src/interrogate/interfaceMaker.cxx index 717a0993d8..e5ca8ce25e 100644 --- a/dtool/src/interrogate/interfaceMaker.cxx +++ b/dtool/src/interrogate/interfaceMaker.cxx @@ -663,7 +663,7 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) { make_function_remap(itype, ifunc, cppfunc, num_default_parameters); if (remap != (FunctionRemap *)NULL) { func->_remaps.push_back(remap); - + // If *any* of the variants of this function has a "this" // pointer, the entire set of functions is deemed to have a // "this" pointer. @@ -672,7 +672,7 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) { } func->_flags |= remap->_flags; - + // Make a wrapper for the function. FunctionWrapperIndex wrapper_index = remap->make_wrapper_entry(func_index); @@ -685,6 +685,13 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) { } } + // If there's a remap taking no args and a remap + // taking only a single arg, assume we need varargs. + if ((func->_flags & FunctionRemap::F_no_args) && + (func->_flags & FunctionRemap::F_single_arg)) { + func->_flags |= FunctionRemap::F_varargs; + } + return func; } diff --git a/dtool/src/interrogate/interfaceMakerPython.cxx b/dtool/src/interrogate/interfaceMakerPython.cxx index b1b5596fcd..213966c900 100644 --- a/dtool/src/interrogate/interfaceMakerPython.cxx +++ b/dtool/src/interrogate/interfaceMakerPython.cxx @@ -36,17 +36,12 @@ InterfaceMakerPython(InterrogateModuleDef *def) : void InterfaceMakerPython:: write_includes(ostream &out) { InterfaceMaker::write_includes(out); - out << "#undef HAVE_LONG_LONG\n" - << "#undef _POSIX_C_SOURCE\n\n" + out << "#undef _POSIX_C_SOURCE\n\n" << "#if PYTHON_FRAMEWORK\n" << " #include \"Python/Python.h\"\n" << "#else\n" << " #include \"Python.h\"\n" - << "#endif\n" - << "#ifdef HAVE_LONG_LONG\n" - << "#undef HAVE_LONG_LONG\n" - << "#endif \n"; - + << "#endif\n"; } //////////////////////////////////////////////////////////////////// diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 4fb38a721c..bde6164881 100755 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -899,16 +899,15 @@ write_class_details(ostream &out, Object *obj) { } if (obj->_constructors.size() == 0) { - std::string fname = "int Dtool_Init_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds)"; - out << fname << " {\n"; - out << " PyErr_SetString(PyExc_TypeError, \"cannot init constant class (" << cClassName << ")\");\n"; - out << " return -1;\n" ; - out << "}\n\n"; + out << "int Dtool_Init_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds) {\n" + << " PyErr_SetString(PyExc_TypeError, \"cannot init constant class (" << cClassName << ")\");\n" + << " return -1;\n" + << "}\n\n"; - out - << "int Dtool_InitNoCoerce_" << ClassName << "(PyObject *self, PyObject *args, PyObject *kwds) {\n" - << " return Dtool_Init_" << ClassName << "(self, args, kwds);\n" - << "}\n\n"; + out << "int Dtool_InitNoCoerce_" << ClassName << "(PyObject *self, PyObject *args) {\n" + << " PyErr_SetString(PyExc_TypeError, \"cannot init constant class (" << cClassName << ")\");\n" + << " return -1;\n" + << "}\n\n"; } else { bool coercion_attempted = false; @@ -916,7 +915,7 @@ write_class_details(ostream &out, Object *obj) { Function *func = (*fi); std::string fname = "int Dtool_Init_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds)"; - write_function_for_name(out, obj, func, fname, "", ClassName, true, coercion_attempted); + write_function_for_name(out, obj, func, fname, "", true, coercion_attempted, false, true, true, true); } if (coercion_attempted) { // If a coercion attempt was written into the above constructor, @@ -924,18 +923,17 @@ write_class_details(ostream &out, Object *obj) { // coercion. We'll need this for nested coercion calls. for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) { Function *func = (*fi); - std::string fname = "int Dtool_InitNoCoerce_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds)"; + std::string fname = "int Dtool_InitNoCoerce_" + ClassName + "(PyObject *self, PyObject *args)"; - write_function_for_name(out, obj, func, fname, "", ClassName, false, coercion_attempted); + write_function_for_name(out, obj, func, fname, "", false, coercion_attempted, false, true, false, true); } } else { // Otherwise, since the above constructor didn't involve any // coercion anyway, we can use the same function for both // purposes. Construct a trivial wrapper. - out - << "int Dtool_InitNoCoerce_" << ClassName << "(PyObject *self, PyObject *args, PyObject *kwds) {\n" - << " return Dtool_Init_" << ClassName << "(self, args, kwds);\n" - << "}\n\n"; + out << "int Dtool_InitNoCoerce_" << ClassName << "(PyObject *self, PyObject *args) {\n" + << " return Dtool_Init_" << ClassName << "(self, args, NULL);\n" + << "}\n\n"; } } @@ -1144,11 +1142,23 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) { if (!func->_itype.is_global() && is_function_legal(func)) { string name1 = methodNameFromCppName(func, "", false); string name2 = methodNameFromCppName(func, "", true); + + const char *flags; + if (func->_flags & FunctionRemap::F_keyword_args) { + flags = "METH_VARARGS | METH_KEYWORDS"; + } else if (func->_flags & FunctionRemap::F_varargs) { + flags = "METH_VARARGS"; + } else if (func->_flags & FunctionRemap::F_single_arg) { + flags = "METH_O"; + } else { + flags = "METH_NOARGS"; + } + out << " { \"" << name1 << "\", (PyCFunction) &" - << func->_name << ", METH_VARARGS | METH_KEYWORDS, (char *)" << func->_name << "_comment},\n"; + << func->_name << ", " << flags << ", (const char *)" << func->_name << "_comment},\n"; if (name1 != name2) { out << " { \"" << name2 << "\", (PyCFunction) &" - << func->_name << ", METH_VARARGS | METH_KEYWORDS, (char *)" << func->_name << "_comment},\n"; + << func->_name << ", " << flags << ", (const char *)" << func->_name << "_comment},\n"; } } } @@ -1274,12 +1284,24 @@ write_module_class(ostream &out, Object *obj) { string name1 = methodNameFromCppName(func, export_class_name, false); string name2 = methodNameFromCppName(func, export_class_name, true); + + const char *flags; + if (func->_flags & FunctionRemap::F_keyword_args) { + flags = "METH_VARARGS | METH_KEYWORDS"; + } else if (func->_flags & FunctionRemap::F_varargs) { + flags = "METH_VARARGS"; + } else if (func->_flags & FunctionRemap::F_single_arg) { + flags = "METH_O"; + } else { + flags = "METH_NOARGS"; + } + out << " { \"" << name1 << "\", (PyCFunction) &" - << func->_name << ", METH_VARARGS | METH_KEYWORDS, (char *) " << func->_name << "_comment},\n"; + << func->_name << ", " << flags << ", (char *) " << func->_name << "_comment},\n"; ++x; if (name1 != name2) { out << " { \"" << name2 << "\", (PyCFunction) &" - << func->_name << ", METH_VARARGS | METH_KEYWORDS, (char *) " << func->_name << "_comment},\n"; + << func->_name << ", " << flags << ", (char *) " << func->_name << "_comment},\n"; ++x; } @@ -1358,20 +1380,42 @@ write_module_class(ostream &out, Object *obj) { { std::map::iterator rfi; // slotted_functions; for (rfi = slotted_functions.begin(); rfi != slotted_functions.end(); rfi++) { + Function *func = rfi->first; + bool func_varargs = true; + string call_func; + + if (func->_flags & FunctionRemap::F_keyword_args) { + call_func = func->_name + "(self, args, NULL)"; + + } else if (func->_flags & FunctionRemap::F_varargs) { + call_func = func->_name + "(self, args)"; + + } else if (func->_flags & FunctionRemap::F_single_arg) { + func_varargs = false; + call_func = func->_name + "(self, arg)"; + + } else { + func_varargs = false; + call_func = func->_name + "(self)"; + } + switch (rfi->second._wrapper_type) { case WT_no_params: // PyObject *func(PyObject *self) { - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self) {\n"; - out << " PyObject *args = Py_BuildValue(\"()\");\n"; - out << " PyObject *result = " << func->_name << "(self, args, NULL);\n"; - out << " Py_DECREF(args);\n"; - out << " return result;\n"; + if (func_varargs) { + out << " PyObject *args = PyTuple_New(0);\n"; + out << " PyObject *result = " << call_func << ";\n"; + out << " Py_DECREF(args);\n"; + out << " return result;\n"; + } else { + out << " return " << call_func << ";\n"; + } out << "}\n\n"; } break; @@ -1380,16 +1424,19 @@ write_module_class(ostream &out, Object *obj) { case WT_numeric_operator: // PyObject *func(PyObject *self, PyObject *one) { - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *one) {\n"; - out << " PyObject *args = Py_BuildValue(\"(O)\", one);\n"; - out << " PyObject *result = " << func->_name << "(self, args, NULL);\n"; - out << " Py_DECREF(args);\n"; - out << " return result;\n"; + out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *arg) {\n"; + if (func_varargs) { + out << " PyObject *args = PyTuple_Pack(1, arg);\n"; + out << " PyObject *result = " << call_func << ";\n"; + out << " Py_DECREF(args);\n"; + out << " return result;\n"; + } else { + out << " return " << call_func << ";\n"; + } out << "}\n\n"; } break; @@ -1397,20 +1444,23 @@ write_module_class(ostream &out, Object *obj) { case WT_setattr: // int func(PyObject *self, PyObject *one, PyObject *two = NULL) { - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static int " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *one, PyObject *two) {\n"; - out << " PyObject *args;\n"; - out << " if (two == NULL) {\n"; - out << " args = Py_BuildValue(\"(O)\", one);\n"; - out << " } else {\n"; - out << " args = Py_BuildValue(\"(OO)\", one, two);\n"; - out << " }\n"; - out << " PyObject *py_result = " << func->_name << "(self, args, NULL);\n"; - out << " Py_DECREF(args);\n"; + out << "static int " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *arg, PyObject *arg2) {\n"; + if (func_varargs) { + out << " PyObject *args;\n"; + out << " if (arg2 == NULL) {\n"; + out << " args = PyTuple_Pack(1, arg);\n"; + out << " } else {\n"; + out << " args = PyTuple_Pack(2, arg, arg2);\n"; + out << " }\n"; + out << " PyObject *py_result = " << call_func << ";\n"; + out << " Py_DECREF(args);\n"; + } else { + out << " PyObject *py_result = " << call_func << ";\n"; + } out << " if (py_result == NULL) return -1;\n"; out << "#if PY_MAJOR_VERSION >= 3\n"; out << " int result = PyLong_AsLong(py_result);\n"; @@ -1429,18 +1479,21 @@ write_module_class(ostream &out, Object *obj) { // With special handling to pass up to // PyObject_GenericGetAttr() if it returns NULL. { - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *one) {\n"; - out << " PyObject *args = Py_BuildValue(\"(O)\", one);\n"; - out << " PyObject *result = " << func->_name << "(self, args, NULL);\n"; - out << " Py_DECREF(args);\n"; + out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *arg) {\n"; + if (func_varargs) { + out << " PyObject *args = PyTuple_Pack(1, arg);\n"; + out << " PyObject *result = " << call_func << ";\n"; + out << " Py_DECREF(args);\n"; + } else { + out << " PyObject *result = " << call_func << ";\n"; + } out << " if (result == NULL) {\n"; - out << " PyErr_Clear();\n"; - out << " return PyObject_GenericGetAttr(self, one);\n"; + out << " PyErr_Clear();\n"; + out << " return PyObject_GenericGetAttr(self, arg);\n"; out << " }\n"; out << " return result;\n"; out << "}\n\n"; @@ -1450,16 +1503,42 @@ write_module_class(ostream &out, Object *obj) { case WT_sequence_getitem: // PyObject *func(PyObject *self, Py_ssize_t index) { - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, Py_ssize_t index) {\n"; - out << " PyObject *args = Py_BuildValue(\"(i)\", index);\n"; - out << " PyObject *result = " << func->_name << "(self, args, NULL);\n"; - out << " Py_DECREF(args);\n"; - out << " return result;\n"; + out << " " << cClassName << " *local_this = NULL;\n"; + out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; + out << " if (local_this == NULL) {\n"; + out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " return NULL;\n"; + out << " }\n\n"; + + // This is a getitem or setitem of a sequence type. This means we + // *need* to raise IndexError if we're out of bounds. We have to + // assume the bounds are 0 .. this->size() (this is the same + // assumption that Python makes). + out << " if (index < 0 || index >= local_this->size()) {\n"; + out << " PyErr_SetString(PyExc_IndexError, \"" << ClassName << " index out of range\");\n"; + out << " return NULL;\n"; + out << " }\n"; + + // Gather the remaps with the F_getitem_int flag. + std::set remaps; + Function::Remaps::const_iterator ri; + for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + if (is_remap_legal(remap) && (remap->_flags & FunctionRemap::F_getitem_int)) { + remaps.insert(remap); + } + } + + string expected_params; + bool coercion_attempted = false; + write_function_forset(out, obj, func, remaps, expected_params, 2, false, false, coercion_attempted, false, false, false, false, "index"); + + out << " return NULL;\n"; out << "}\n\n"; } break; @@ -1467,20 +1546,47 @@ write_module_class(ostream &out, Object *obj) { case WT_sequence_setitem: // int_t func(PyObject *self, Py_ssize_t index, PyObject *value) { - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static int " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, Py_ssize_t index, PyObject *value) {\n"; - out << " PyObject *args = Py_BuildValue(\"(iO)\", index, value);\n"; - out << " PyObject *result = " << func->_name << "(self, args, NULL);\n"; - out << " Py_DECREF(args);\n"; - out << " if (result == NULL) {\n"; + out << "static int " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, Py_ssize_t index, PyObject *arg) {\n"; + out << " " << cClassName << " *local_this = NULL;\n"; + out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; + out << " if (local_this == NULL) {\n"; + out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " return -1;\n"; + out << " }\n\n"; + + out << " if (index < 0 || index >= local_this->size()) {\n"; + out << " PyErr_SetString(PyExc_IndexError, \"" << ClassName << " index out of range\");\n"; out << " return -1;\n"; out << " }\n"; - out << " Py_DECREF(result);\n"; - out << " return 0;\n"; + + // Gather the remaps with the F_getitem_int flag. + std::set remaps; + Function::Remaps::const_iterator ri; + for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + if (is_remap_legal(remap) && (remap->_flags & FunctionRemap::F_setitem_int)) { + remaps.insert(remap); + } + } + + // Note: we disallow parameter coercion for setitem. It's not clear if anybody + // uses it in this case, and since some people may need to call this function + // very often, it's probably best to disable it for performance. + string expected_params; + bool coercion_attempted = false; + write_function_forset(out, obj, func, remaps, expected_params, 2, false, false, coercion_attempted, true, false, false, true, "index"); + + out << " if (!PyErr_Occurred()) {\n"; + out << " PyErr_SetString(PyExc_TypeError,\n"; + out << " \"Arguments must match:\\n\"\n"; + output_quoted(out, 6, expected_params); + out << ");\n"; + out << " }\n"; + out << " return -1;\n"; out << "}\n\n"; } break; @@ -1488,25 +1594,20 @@ write_module_class(ostream &out, Object *obj) { case WT_sequence_size: // Py_ssize_t func(PyObject *self) { - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; out << "static Py_ssize_t " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self) {\n"; - out << " PyObject *args = Py_BuildValue(\"()\");\n"; - out << " PyObject *result = " << func->_name << "(self, args, NULL);\n"; - out << " Py_DECREF(args);\n"; - out << " if (result == NULL) {\n"; + out << " " << cClassName << " *local_this = NULL;\n"; + out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; + out << " if (local_this == NULL) {\n"; + out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; out << " return -1;\n"; - out << " }\n"; - out << "#if PY_MAJOR_VERSION >= 3\n"; - out << " Py_ssize_t num = PyLong_AsSsize_t(result);\n"; - out << "#else\n"; - out << " Py_ssize_t num = PyInt_AsSsize_t(result);\n"; - out << "#endif\n"; - out << " Py_DECREF(result);\n"; - out << " return num;\n"; + out << " }\n\n"; + + // This is a cheap cheat around all of the overhead of calling the wrapper function. + out << " return (Py_ssize_t) local_this->" << func->_ifunc.get_name() << "();\n"; out << "}\n\n"; } break; @@ -1514,15 +1615,18 @@ write_module_class(ostream &out, Object *obj) { case WT_mapping_setitem: // int func(PyObject *self, PyObject *one, PyObject *two) { - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static int " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *one, PyObject *two) {\n"; - out << " PyObject *args = Py_BuildValue(\"(OO)\", one, two);\n"; - out << " PyObject *result = " << func->_name << "(self, args, NULL);\n"; - out << " Py_DECREF(args);\n"; + out << "static int " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *arg, PyObject *arg2) {\n"; + if (func_varargs) { + out << " PyObject *args = PyTuple_Pack(2, arg, arg2);\n"; + out << " PyObject *result = " << call_func << ";\n"; + out << " Py_DECREF(args);\n"; + } else { + out << " PyObject *result = " << call_func << ";\n"; + } out << " if (result == NULL) {\n"; out << " return -1;\n"; out << " }\n"; @@ -1535,15 +1639,18 @@ write_module_class(ostream &out, Object *obj) { case WT_inquiry: // int func(PyObject *self) { - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; out << "static int " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self) {\n"; - out << " PyObject *args = Py_BuildValue(\"()\");\n"; - out << " PyObject *result = " << func->_name << "(self, args, NULL);\n"; - out << " Py_DECREF(args);\n"; + if (func_varargs) { + out << " PyObject *args = PyTuple_New(0);\n"; + out << " PyObject *result = " << call_func << ";\n"; + out << " Py_DECREF(args);\n"; + } else { + out << " PyObject *result = " << call_func << ";\n"; + } out << " if (result == NULL) {\n"; out << " return -1;\n"; out << " }\n"; @@ -1567,7 +1674,6 @@ write_module_class(ostream &out, Object *obj) { { has_local_getbuffer = true; - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; @@ -1640,7 +1746,6 @@ write_module_class(ostream &out, Object *obj) { // void __releasebuffer__(PyObject *self, Py_buffer *buffer) // Same story as __getbuffer__ above. { - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; @@ -1717,15 +1822,18 @@ write_module_class(ostream &out, Object *obj) { // PyObject *func(PyObject *self) // However, returns NULL instead of None { - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self) {\n"; - out << " PyObject *args = Py_BuildValue(\"()\");\n"; - out << " PyObject *result = " << func->_name << "(self, args, NULL);\n"; - out << " Py_DECREF(args);\n"; + if (func_varargs) { + out << " PyObject *args = PyTuple_New(0);\n"; + out << " PyObject *result = " << call_func << ";\n"; + out << " Py_DECREF(args);\n"; + } else { + out << " PyObject *result = " << call_func << ";\n"; + } out << " if (result == Py_None) {\n"; out << " Py_DECREF(Py_None);\n"; out << " return NULL;\n"; @@ -1740,21 +1848,24 @@ write_module_class(ostream &out, Object *obj) { case WT_ternary_operator: // PyObject *func(PyObject *self, PyObject *one, PyObject *two) { - Function *func = rfi->first; out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *one, PyObject *two) {\n"; - out << " PyObject *args;\n"; - out << " if (two != Py_None) {\n"; - out << " args = Py_BuildValue(\"(OO)\", one, two);\n"; - out << " } else {\n"; - out << " args = Py_BuildValue(\"(O)\", one);\n"; - out << " }\n\n"; - out << " PyObject *result = " << func->_name << "(self, args, NULL);\n"; - out << " Py_DECREF(args);\n"; - out << " return result;\n"; + out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *arg, PyObject *arg2) {\n"; + if (func_varargs) { + out << " PyObject *args;\n"; + out << " if (arg2 != Py_None) {\n"; + out << " args = PyTuple_Pack(2, arg, arg2);\n"; + out << " } else {\n"; + out << " args = PyTuple_Pack(1, arg);\n"; + out << " }\n\n"; + out << " PyObject *result = " << call_func << ";\n"; + out << " Py_DECREF(args);\n"; + out << " return result;\n"; + } else { + out << " return " << call_func << ";\n"; + } out << "}\n\n"; } break; @@ -1863,24 +1974,17 @@ write_module_class(ostream &out, Object *obj) { } if (NeedsARichCompareFunction(obj->_itype)) { - // NB. It's a bit inefficient to first pack the 'other' arg into a - // tuple only to be parsed by PyArg_Parse again later, but there's - // no obvious other way to do it. out << "//////////////////\n"; out << "// A rich comparison function\n"; out << "// " << ClassName << "\n"; out << "//////////////////\n"; - out << "static PyObject *Dtool_RichCompare_" << ClassName << "(PyObject *self, PyObject *other, int op) {\n"; - out << " PyObject *kwds = NULL;\n"; + out << "static PyObject *Dtool_RichCompare_" << ClassName << "(PyObject *self, PyObject *arg, int op) {\n"; out << " " << cClassName << " *local_this = NULL;\n"; out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; out << " if (local_this == NULL) {\n"; out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; out << " return NULL;\n"; - out << " }\n"; - out << " PyObject *args = PyTuple_Pack(1, other);\n"; - string args_cleanup = "Py_DECREF(args);"; - out << "\n"; + out << " }\n\n"; out << " switch (op) {\n"; Function *compare_to_func = NULL; @@ -1894,23 +1998,23 @@ write_module_class(ostream &out, Object *obj) { Function::Remaps::const_iterator ri; for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { FunctionRemap *remap = (*ri); - if (is_remap_legal(*remap) && remap->_has_this && remap->_parameters.size() == 2) { + if (is_remap_legal(remap) && remap->_has_this && (remap->_flags & FunctionRemap::F_single_arg)) { remaps.insert(remap); } } const string &fname = func->_ifunc.get_name(); if (fname == "operator <") { - out << " case Py_LT:\n"; + out << " case Py_LT: {\n"; } else if (fname == "operator <=") { - out << " case Py_LE:\n"; + out << " case Py_LE: {\n"; } else if (fname == "operator ==") { - out << " case Py_EQ:\n"; + out << " case Py_EQ: {\n"; } else if (fname == "operator !=") { - out << " case Py_NE:\n"; + out << " case Py_NE: {\n"; } else if (fname == "operator >") { - out << " case Py_GT:\n"; + out << " case Py_GT: {\n"; } else if (fname == "operator >=") { - out << " case Py_GE:\n"; + out << " case Py_GE: {\n"; } else if (fname == "compare_to") { compare_to_func = func; continue; @@ -1918,21 +2022,20 @@ write_module_class(ostream &out, Object *obj) { continue; } - ostringstream forward_decl; string expected_params; bool coercion_attempted = false; - write_function_forset(out, obj, func, remaps, expected_params, 2, forward_decl, false, true, coercion_attempted, args_cleanup); + write_function_forset(out, obj, func, remaps, expected_params, 4, false, true, coercion_attempted, true, false, false, false); - out << " if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_TypeError)) {\n"; - out << " PyErr_Clear();\n"; + out << " if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_TypeError)) {\n"; + out << " PyErr_Clear();\n"; + out << " }\n"; + out << " break;\n"; out << " }\n"; - out << " break;\n"; has_local_richcompare = true; } out << " }\n\n"; - out << " " << args_cleanup << "\n"; out << " if (PyErr_Occurred()) {\n"; out << " return (PyObject *)NULL;\n"; out << " }\n\n"; @@ -1940,7 +2043,7 @@ write_module_class(ostream &out, Object *obj) { if (compare_to_func != NULL) { out << "#if PY_MAJOR_VERSION >= 3\n"; out << " // All is not lost; we still have the compare_to function to fall back onto.\n"; - out << " PyObject *result = " << compare_to_func->_name << "(self, args, kwds);\n"; + out << " PyObject *result = " << compare_to_func->_name << "(self, arg);\n"; out << " if (result != NULL) {\n"; out << " if (PyLong_Check(result)) {;\n"; out << " long cmpval = PyLong_AsLong(result);\n"; @@ -2011,23 +2114,21 @@ write_module_class(ostream &out, Object *obj) { // add bases/// if (bases.size() > 0) { out << " // Dependent objects\n"; - std::string format1 = ""; - std::string format2 = ""; - for (std::vector::iterator bi = bases.begin(); bi != bases.end(); bi++) { - format1 += "O"; - format2 += ", &Dtool_" + *bi + ".As_PyTypeObject()"; + string baseargs; + for (vector::iterator bi = bases.begin(); bi != bases.end(); ++bi) { + baseargs += ", &Dtool_" + *bi + ".As_PyTypeObject()"; out << " Dtool_" << make_safe_name(*bi) << "._Dtool_ClassInit(NULL);\n"; } - out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_bases = Py_BuildValue(\"(" << format1 << ")\"" << format2 << ");\n"; + out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_bases = PyTuple_Pack(" << bases.size() << baseargs << ");\n"; } // get dictionary out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_dict = PyDict_New();\n"; - out << " PyDict_SetItemString(Dtool_" <::const_iterator rfi; + map::const_iterator rfi; int prev_min_version = 0; for (rfi = slotted_functions.begin(); rfi != slotted_functions.end(); rfi++) { @@ -2279,10 +2380,42 @@ write_prototype_for_name(ostream &out, InterfaceMaker::Function *func, const std //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker::Function *func, const std::string &PreProcess) { - std::string fname = "static PyObject *" + func->_name + "(PyObject *self, PyObject *args, PyObject *kwds)"; + std::string fname; + + bool single_arg = false; + bool have_varargs = false; + bool have_kwargs = false; + + if ((func->_flags & FunctionRemap::F_keyword_args) != 0) { + // Function takes both an args tuple and a kwargs dictionary. + have_kwargs = true; + have_varargs = true; + } else if ((func->_flags & FunctionRemap::F_varargs) != 0) { + // Function takes only an args tuple. + have_varargs = true; + } else if ((func->_flags & FunctionRemap::F_single_arg) != 0) { + // Function takes a single PyObject* argument. + single_arg = true; + } + + if (func->_ifunc.is_unary_op()) { + assert(!single_arg); + assert(!have_varargs); + assert(!have_kwargs); + } + + if (have_kwargs) { + fname = "static PyObject *" + func->_name + "(PyObject *self, PyObject *args, PyObject *kwds)"; + } else if (have_varargs) { + fname = "static PyObject *" + func->_name + "(PyObject *self, PyObject *args)"; + } else if (single_arg) { + fname = "static PyObject *" + func->_name + "(PyObject *self, PyObject *arg)"; + } else { + fname = "static PyObject *" + func->_name + "(PyObject *self)"; + } bool coercion_attempted = false; - write_function_for_name(out, obj, func, fname, PreProcess, "", true, coercion_attempted); + write_function_for_name(out, obj, func, fname, PreProcess, true, coercion_attempted, single_arg, have_varargs, have_kwargs, false); } //////////////////////////////////////////////////////////////////// @@ -2292,10 +2425,9 @@ write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMaker::Function *func, - const std::string &function_name, - const std::string &PreProcess, - const std::string &ClassName, bool coercion_allowed, bool &coercion_attempted) { - ostringstream forward_decl; + const std::string &function_name, const std::string &PreProcess, + bool coercion_allowed, bool &coercion_attempted, bool single_arg, + bool have_varargs, bool have_kwargs, bool return_int) { ostringstream out; std::map > MapSets; @@ -2306,10 +2438,11 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak out1 << "/******************************************************************\n" << " * Python type method wrapper for\n"; for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { FunctionRemap *remap = (*ri); - if (is_remap_legal(*remap)) { + if (is_remap_legal(remap)) { int parameter_size = remap->_parameters.size(); - if (remap->_has_this && remap->_type != FunctionRemap::T_constructor) + if (remap->_has_this && remap->_type != FunctionRemap::T_constructor) { parameter_size --; + } MapSets[parameter_size].insert(remap); out1 << " * "; @@ -2341,35 +2474,37 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak FunctionComment += "\n"; } - bool constructor = false; - if (MapSets.size() > 1) { string expected_params; - indent(out, 2) << "int parameter_count = 1;\n"; - indent(out, 2) << "if (PyTuple_Check(args)) {\n"; - indent(out, 2) << " parameter_count = PyTuple_Size(args);\n"; - indent(out, 2) << " if (kwds != NULL && PyDict_Check(kwds)) {\n"; - indent(out, 2) << " parameter_count += PyDict_Size(kwds);\n"; - indent(out, 2) << " }\n"; - indent(out, 2) << "}\n"; + if (have_varargs) { + indent(out, 2) << "int parameter_count = PyTuple_Size(args);\n"; + + if (have_kwargs) { + indent(out, 2) << "if (kwds != NULL && PyDict_Check(kwds)) {\n"; + indent(out, 2) << " parameter_count += PyDict_Size(kwds);\n"; + indent(out, 2) << "}\n"; + } + + } else if (single_arg) { + indent(out, 2) << "const int parameter_count = 1;\n"; + + } else { + indent(out, 2) << "const int parameter_count = 0;\n"; + } indent(out, 2) << "switch (parameter_count) {\n"; for (mii = MapSets.begin(); mii != MapSets.end(); mii ++) { indent(out, 2) << "case " << mii->first << ": {\n"; - write_function_forset(out, obj, func, mii->second, expected_params, 4, forward_decl, is_inplace, coercion_allowed, coercion_attempted, ""); - if ((*mii->second.begin())->_type == FunctionRemap::T_constructor) { - constructor = true; - } + write_function_forset(out, obj, func, mii->second, expected_params, 4, is_inplace, coercion_allowed, coercion_attempted, single_arg, have_varargs, have_kwargs, return_int); - indent(out, 4) << "}\n"; indent(out, 4) << "break;\n"; + indent(out, 2) << "}\n"; } indent(out, 2) << "default:\n"; - indent(out, 4) << "{\n"; - indent(out, 6) + indent(out, 4) << "PyErr_Format(PyExc_TypeError, \"" << methodNameFromCppName(func, "", false) << "() takes "; @@ -2396,13 +2531,12 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak out << " arguments (%d given)\", parameter_count + " << add_self << ");\n"; - if (constructor) - indent(out, 6) << "return -1;\n"; - else - indent(out, 6) << "return (PyObject *) NULL;\n"; + if (return_int) { + indent(out, 4) << "return -1;\n"; + } else { + indent(out, 4) << "return (PyObject *) NULL;\n"; + } - indent(out, 4) << "}\n"; - indent(out, 4) << "break;\n"; indent(out, 2) << "}\n"; out << " if (!PyErr_Occurred()) { // Let error pass on\n"; @@ -2412,7 +2546,7 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak out << ");\n"; out << " }\n"; - if (constructor) { + if (return_int) { indent(out, 2) << "return -1;\n"; } else { indent(out, 2) << "return (PyObject *) NULL;\n"; @@ -2427,10 +2561,7 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak } else { string expected_params = ""; for (mii = MapSets.begin(); mii != MapSets.end(); mii++) { - write_function_forset(out, obj, func, mii->second, expected_params, 2, forward_decl, is_inplace, coercion_allowed, coercion_attempted, ""); - if ((*mii->second.begin())->_type == FunctionRemap::T_constructor) { - constructor = true; - } + write_function_forset(out, obj, func, mii->second, expected_params, 2, is_inplace, coercion_allowed, coercion_attempted, single_arg, have_varargs, have_kwargs, return_int); } out << " if (!PyErr_Occurred()) {\n"; @@ -2439,7 +2570,8 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak output_quoted(out, 6, expected_params); out << ");\n"; out << " }\n"; - if (constructor) { + + if (return_int) { indent(out, 2) << "return -1;\n"; } else { indent(out, 2) << "return (PyObject *) NULL;\n"; @@ -2458,7 +2590,7 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak FunctionComment = FunctionComment1 + "\n" + FunctionComment; } - if (!constructor) { + if (!return_int) { // Write out the function doc string. We only do this if it is // not a constructor, since we don't have a place to put the // constructor doc string. @@ -2474,7 +2606,6 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak out << "\n"; - out1 << forward_decl.str(); out1 << out.str(); } @@ -2527,20 +2658,41 @@ int GetParnetDepth(CPPType *type) { //////////////////////////////////////////////////////// // The Core sort function for remap calling orders.. ////////////////////////////////////////////////////////// -int RemapCompareLess(FunctionRemap *in1, FunctionRemap *in2) { +bool RemapCompareLess(FunctionRemap *in1, FunctionRemap *in2) { + assert(in1 != NULL); + assert(in2 != NULL); + if (in1->_parameters.size() != in2->_parameters.size()) { - return (in1->_parameters.size() > in2->_parameters.size()); + return (in1->_parameters.size() > in2->_parameters.size()); } int pcount = in1->_parameters.size(); - for (int x = 0; x< pcount; x++) { + for (int x = 0; x < pcount; x++) { CPPType *orig_type1 = in1->_parameters[x]._remap->get_orig_type(); CPPType *orig_type2 = in2->_parameters[x]._remap->get_orig_type(); + // Hack to make int sort before float and double. We do + // this by letting everything compare less than float types. + // But if there's a double variant, prefer it over the float variant. + if (TypeManager::is_float(orig_type1) && + !TypeManager::is_double(orig_type1)) { + return false; + } + if (TypeManager::is_float(orig_type2) && + !TypeManager::is_double(orig_type2)) { + return true; + } + if (TypeManager::is_float(orig_type1)) { + return false; + } + if (TypeManager::is_float(orig_type2)) { + return true; + } + int pd1 = GetParnetDepth(orig_type1); int pd2 = GetParnetDepth(orig_type2); if (pd1 != pd2) { - return pd1 > pd2; + return (pd1 > pd2); } } @@ -2549,20 +2701,6 @@ int RemapCompareLess(FunctionRemap *in1, FunctionRemap *in2) { return false; } -////////////////////////////////////////////////////////// -// Convience for the sort behavior.. -/////////////////////////////////////////////////////////// -std::vector -SortFunctionSet(std::set &remaps) { - std::vector out; - for (std::set::iterator ii = remaps.begin(); ii!= remaps.end(); ii++) { - out.push_back(*ii); - } - - std::sort(out.begin(), out.end(), RemapCompareLess); - return out; -} - /////////////////////////////////////////////////////////// // Function : write_function_forset // @@ -2573,10 +2711,11 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker::Function *func, std::set &remapsin, string &expected_params, int indent_level, - ostream &forward_decl, bool is_inplace, - bool coercion_allowed, - bool &coercion_attempted, - const string &args_cleanup) { + bool is_inplace, bool coercion_allowed, + bool &coercion_attempted, bool single_arg, + bool have_varargs, bool have_kwargs, + bool return_int, + const string &first_pexpr) { // Do we accept any parameters that are class objects? If so, we // might need to check for parameter coercion. bool coercion_possible = false; @@ -2584,27 +2723,25 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, std::set::const_iterator sii; for (sii = remapsin.begin(); sii != remapsin.end() && !coercion_possible; ++sii) { FunctionRemap *remap = (*sii); - if (is_remap_legal(*remap)) { - int pn = 0; - if (remap->_has_this) { - // Skip the "this" parameter. It's never coercible. - ++pn; - } - while (pn < (int)remap->_parameters.size()) { - CPPType *type = remap->_parameters[pn]._remap->get_new_type(); + int pn = 0; + if (remap->_has_this) { + // Skip the "this" parameter. It's never coercible. + ++pn; + } + while (pn < (int)remap->_parameters.size()) { + CPPType *type = remap->_parameters[pn]._remap->get_new_type(); - if (TypeManager::is_char_pointer(type)) { - } else if (TypeManager::is_wchar_pointer(type)) { - } else if (TypeManager::is_pointer_to_PyObject(type)) { - } else if (TypeManager::is_pointer_to_Py_buffer(type)) { - } else if (TypeManager::is_pointer(type)) { - // This is a pointer to an object, so we - // might be able to coerce a parameter to it. - coercion_possible = true; - break; - } - ++pn; + if (TypeManager::is_char_pointer(type)) { + } else if (TypeManager::is_wchar_pointer(type)) { + } else if (TypeManager::is_pointer_to_PyObject(type)) { + } else if (TypeManager::is_pointer_to_Py_buffer(type)) { + } else if (TypeManager::is_pointer(type)) { + // This is a pointer to an object, so we + // might be able to coerce a parameter to it. + coercion_possible = true; + break; } + ++pn; } } } @@ -2624,36 +2761,37 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, indent(out, indent_level) << "while (true) {\n"; indent_level += 2; + } else { + out << "\n"; } if (remapsin.size() > 1) { // There are multiple different overloads for this number of // parameters. Sort them all into order from most-specific to // least-specific, then try them one at a time. - std::vector remaps = SortFunctionSet(remapsin); + std::vector remaps (remapsin.begin(), remapsin.end()); + std::sort(remaps.begin(), remaps.end(), RemapCompareLess); std::vector::iterator sii; for (sii = remaps.begin(); sii != remaps.end(); sii ++) { FunctionRemap *remap = (*sii); - if (is_remap_legal(*remap)) { - if (remap->_has_this && !remap->_const_method) { - // If it's a non-const method, we only allow a - // non-const this. - indent(out, indent_level) - << "if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; - } else { - indent(out, indent_level) - << "{\n"; - } - - indent(out, indent_level) << "// -2 "; - remap->write_orig_prototype(out, 0); out << "\n"; - - write_function_instance(out, obj, func, remap, expected_params, indent_level + 2, false, forward_decl, func->_name, is_inplace, coercion_possible, coercion_attempted, args_cleanup); - - indent(out, indent_level + 2) << "PyErr_Clear();\n"; - indent(out, indent_level) << "}\n\n"; + if (remap->_has_this && !remap->_const_method) { + // If it's a non-const method, we only allow a + // non-const this. + indent(out, indent_level) + << "if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; + } else { + indent(out, indent_level) + << "{\n"; } + + indent(out, indent_level) << "// -2 "; + remap->write_orig_prototype(out, 0); out << "\n"; + + write_function_instance(out, obj, func, remap, expected_params, indent_level + 2, is_inplace, coercion_possible, coercion_attempted, single_arg, have_varargs, have_kwargs, return_int, first_pexpr); + + indent(out, indent_level + 2) << "PyErr_Clear();\n"; + indent(out, indent_level) << "}\n\n"; } } else { // There is only one possible overload with this number of @@ -2661,46 +2799,45 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, std::set::iterator sii; for (sii = remapsin.begin(); sii != remapsin.end(); sii ++) { FunctionRemap *remap = (*sii); - if (is_remap_legal(*remap)) { - if (remap->_has_this && !remap->_const_method) { - // If it's a non-const method, we only allow a - // non-const this. + if (remap->_has_this && !remap->_const_method) { + // If it's a non-const method, we only allow a + // non-const this. + indent(out, indent_level) + << "if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; + indent_level += 2; + } + + indent(out, indent_level) + << "// 1-" ; + remap->write_orig_prototype(out, 0); + out << "\n" ; + write_function_instance(out, obj, func, remap, expected_params, indent_level, is_inplace, coercion_possible, coercion_attempted, single_arg, have_varargs, have_kwargs, return_int, first_pexpr); + + if (remap->_has_this && !remap->_const_method) { + indent(out, indent_level - 2) + << "} else {\n"; + indent(out, indent_level) + << "PyErr_SetString(PyExc_TypeError,\n"; + string class_name = remap->_cpptype->get_simple_name(); + indent(out, indent_level) + << " \"Cannot call " + << classNameFromCppName(class_name, false) + << "." << methodNameFromCppName(func, class_name, false) + << "() on a const object.\");\n"; + + if (return_int) { indent(out, indent_level) - << "if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; + << "return -1;\n"; } else { indent(out, indent_level) - << "{\n"; - } - - indent(out, indent_level + 2) - << "// 1-" ; - remap->write_orig_prototype(out, 0); - out << "\n" ; - write_function_instance(out, obj, func, remap, expected_params, indent_level + 2, true, forward_decl, func->_name, is_inplace, coercion_possible, coercion_attempted, args_cleanup); - - if (remap->_has_this && !remap->_const_method) { - indent(out, indent_level) - << "} else {\n"; - indent(out, indent_level + 2) - << "PyErr_SetString(PyExc_TypeError,\n"; - string class_name = remap->_cpptype->get_simple_name(); - indent(out, indent_level + 2) - << " \"Cannot call " - << classNameFromCppName(class_name, false) - << "." << methodNameFromCppName(func, class_name, false) - << "() on a const object.\");\n"; - if (!args_cleanup.empty()) { - indent(out, indent_level + 2) - << args_cleanup << "\n"; - } - indent(out, indent_level + 2) << "return (PyObject *) NULL;\n"; - indent(out, indent_level) - << "}\n\n"; - } else { - indent(out, indent_level) - << "}\n\n"; } + + indent_level -= 2; + indent(out, indent_level) + << "}\n\n"; + } else { + out << "\n"; } } } @@ -2753,13 +2890,13 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: write_function_instance(ostream &out, InterfaceMaker::Object *obj, - InterfaceMaker::Function *func1, + InterfaceMaker::Function *func, FunctionRemap *remap, string &expected_params, - int indent_level, bool errors_fatal, - ostream &ForwardDeclrs, - const std::string &functionnamestr, bool is_inplace, + int indent_level, bool is_inplace, bool coercion_possible, bool &coercion_attempted, - const string &args_cleanup) { + bool single_arg, bool have_varargs, + bool have_kwargs, bool return_int, + const string &first_pexpr) { string format_specifiers; std::string keyword_list; string parameter_list; @@ -2768,13 +2905,9 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, string extra_convert; string extra_param_check; string extra_cleanup; - string pname_for_pyobject; + string direct_assign; - bool is_constructor = false; - - if (remap->_type == FunctionRemap::T_constructor) { - is_constructor = true; - } + bool is_constructor = (remap->_type == FunctionRemap::T_constructor); if (is_constructor && (remap->_flags & FunctionRemap::F_explicit_self) != 0) { // If we'll be passing "self" to the constructor, we need to @@ -2799,15 +2932,26 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, // simultaneously building the ParseTuple() function call and also // the parameter expression list for call_function(). - expected_params += methodNameFromCppName(func1, "", false); + expected_params += methodNameFromCppName(func, "", false); expected_params += "("; + int num_params = 0; + bool only_pyobjects = true; + bool check_exceptions = false; + int pn; - for (pn = 0; pn < (int)remap->_parameters.size(); pn++) { + for (pn = 0; pn < (int)remap->_parameters.size(); ++pn) { if (pn > 0) { expected_params += ", "; } + if (((remap->_has_this && pn == 1) || + (!remap->_has_this && pn == 0)) && !first_pexpr.empty()) { + // The first param was already converted. + pexprs.push_back(first_pexpr); + continue; + } + CPPType *orig_type = remap->_parameters[pn]._remap->get_orig_type(); CPPType *type = remap->_parameters[pn]._remap->get_new_type(); string param_name = remap->get_parameter_name(pn); @@ -2826,6 +2970,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, indent(out, indent_level) << "char *" << param_name << ";\n"; format_specifiers += "s"; parameter_list += ", &" + param_name; + expected_params += "str"; } else if (TypeManager::is_wchar_pointer(orig_type)) { out << "#if PY_MAJOR_VERSION >= 3\n"; @@ -2837,13 +2982,14 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, format_specifiers += "U"; parameter_list += ", &" + param_name; - extra_convert += " int " + param_name + "_len = PyUnicode_GetSize((PyObject *)" + param_name + ");" + extra_convert += " Py_ssize_t " + param_name + "_len = PyUnicode_GetSize((PyObject *)" + param_name + ");" " wchar_t *" + param_name + "_str = new wchar_t[" + param_name + "_len + 1];" " PyUnicode_AsWideChar(" + param_name + ", " + param_name + "_str, " + param_name + "_len);" " " + param_name + "_str[" + param_name + "_len] = 0;"; pexpr_string = param_name + "_str"; extra_cleanup += " delete[] " + param_name + "_str;"; + expected_params += "unicode"; } else if (TypeManager::is_wstring(orig_type)) { out << "#if PY_MAJOR_VERSION >= 3\n"; @@ -2854,7 +3000,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, format_specifiers += "U"; parameter_list += ", &" + param_name; - extra_convert += " int " + param_name + "_len = PyUnicode_GetSize((PyObject *)" + param_name + ");" + extra_convert += " Py_ssize_t " + param_name + "_len = PyUnicode_GetSize((PyObject *)" + param_name + ");" " wchar_t *" + param_name + "_str = new wchar_t[" + param_name + "_len];" " PyUnicode_AsWideChar(" + param_name + ", " + param_name + "_str, " + param_name + "_len);"; @@ -2863,6 +3009,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, param_name + "_len)"; extra_cleanup += " delete[] " + param_name + "_str;"; + expected_params += "unicode"; } else if (TypeManager::is_const_ptr_to_basic_string_wchar(orig_type)) { out << "#if PY_MAJOR_VERSION >= 3\n"; @@ -2873,7 +3020,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, format_specifiers += "U"; parameter_list += ", &" + param_name; - extra_convert += " int " + param_name + "_len = PyUnicode_GetSize((PyObject *)" + param_name + ");" + extra_convert += " Py_ssize_t " + param_name + "_len = PyUnicode_GetSize((PyObject *)" + param_name + ");" " wchar_t *" + param_name + "_str = new wchar_t[" + param_name + "_len];" " PyUnicode_AsWideChar(" + param_name + ", " + param_name + "_str, " + param_name + "_len);"; @@ -2882,106 +3029,182 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, param_name + "_len)"; extra_cleanup += " delete[] " + param_name + "_str;"; - - } else if (TypeManager::is_const_ptr_to_basic_string_char(orig_type)) { - indent(out, indent_level) << "char *" << param_name << "_str;\n"; - indent(out, indent_level) << "int " << param_name << "_len;\n"; - format_specifiers += "s#"; - parameter_list += ", &" + param_name - + "_str, &" + param_name + "_len"; - pexpr_string = "&basic_string(" + - param_name + "_str, " + - param_name + "_len)"; + expected_params += "unicode"; } else { indent(out, indent_level) << "char *" << param_name << "_str;\n"; - indent(out, indent_level) << "int " << param_name << "_len;\n"; - format_specifiers += "s#"; - parameter_list += ", &" + param_name - + "_str, &" + param_name + "_len"; - pexpr_string = "basic_string(" + - param_name + "_str, " + - param_name + "_len)"; + indent(out, indent_level) << "Py_ssize_t " << param_name << "_len;\n"; + if (single_arg) { + out << "#if PY_MAJOR_VERSION >= 3\n"; + indent(out, indent_level) + << param_name << "_str = PyUnicode_AsUTF8AndSize(arg, &" + << param_name << "_len);\n"; + out << "#else\n"; + indent(out, indent_level) << "if (PyString_AsStringAndSize(arg, &" + << param_name << "_str, &" << param_name << "_len) == -1) {\n"; + indent(out, indent_level + 2) << param_name << "_str = NULL;\n"; + indent(out, indent_level) << "}\n"; + out << "#endif\n"; + + extra_param_check = " && " + param_name + "_str != NULL"; + } else { + format_specifiers += "s#"; + parameter_list += ", &" + param_name + + "_str, &" + param_name + "_len"; + } + + if (TypeManager::is_const_ptr_to_basic_string_char(orig_type)) { + pexpr_string = "&basic_string(" + + param_name + "_str, " + + param_name + "_len)"; + } else { + pexpr_string = "basic_string(" + + param_name + "_str, " + + param_name + "_len)"; + } + expected_params += "str"; } - expected_params += "string"; + ++num_params; + only_pyobjects = false; } else if (TypeManager::is_bool(type)) { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - format_specifiers += "O"; - parameter_list += ", &" + param_name; + if (single_arg) { + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } pexpr_string = "(PyObject_IsTrue(" + param_name + ") != 0)"; expected_params += "bool"; - pname_for_pyobject += param_name; + ++num_params; } else if (TypeManager::is_unsigned_longlong(type)) { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - format_specifiers += "O"; - parameter_list += ", &" + param_name; + if (single_arg) { + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; - extra_param_check += " || (" + param_name + "_long == NULL)"; + extra_param_check += " && " + param_name + "_long != NULL"; pexpr_string = "PyLong_AsUnsignedLongLong(" + param_name + "_long)"; extra_cleanup += "Py_XDECREF(" + param_name + "_long);"; expected_params += "unsigned long long"; - pname_for_pyobject += param_name; + ++num_params; } else if (TypeManager::is_longlong(type)) { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - format_specifiers += "O"; - parameter_list += ", &" + param_name; + if (single_arg) { + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; - extra_param_check += " || (" + param_name + "_long == NULL)"; + extra_param_check += " && " + param_name + "_long != NULL"; pexpr_string = "PyLong_AsLongLong(" + param_name + "_long)"; extra_cleanup += "Py_XDECREF(" + param_name + "_long);"; expected_params += "long long"; - pname_for_pyobject += param_name; + ++num_params; } else if (TypeManager::is_unsigned_integer(type)) { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - format_specifiers += "O"; + if (single_arg) { + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } parameter_list += ", &" + param_name; - extra_convert += "PyObject *" + param_name + "_uint = PyNumber_Long(" + param_name + ");"; - extra_param_check += " || (" + param_name + "_uint == NULL)"; - pexpr_string = "PyLong_AsUnsignedLong(" + param_name + "_uint)"; - extra_cleanup += "Py_XDECREF(" + param_name + "_uint);"; + extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; + extra_param_check += " && " + param_name + "_long != NULL"; + pexpr_string = "PyLong_AsUnsignedLong(" + param_name + "_long)"; + extra_cleanup += "Py_XDECREF(" + param_name + "_long);"; expected_params += "unsigned int"; - pname_for_pyobject += param_name; + ++num_params; } else if (TypeManager::is_integer(type)) { - indent(out, indent_level) << "int " << param_name << ";\n"; - format_specifiers += "i"; - parameter_list += ", &" + param_name; + if (single_arg) { + pexpr_string = "(" + type->get_local_name(&parser) + ")PyInt_AS_LONG(arg)"; + extra_param_check += " && PyInt_Check(arg)"; + } else { + indent(out, indent_level) << "int " << param_name << ";\n"; + format_specifiers += "i"; + parameter_list += ", &" + param_name; + } expected_params += "int"; + only_pyobjects = false; + ++num_params; + + } else if (TypeManager::is_double(type)) { + if (single_arg) { + pexpr_string = "PyFloat_AsDouble(arg)"; + extra_param_check += " && PyNumber_Check(arg)"; + } else { + indent(out, indent_level) << "double " << param_name << ";\n"; + format_specifiers += "d"; + parameter_list += ", &" + param_name; + } + expected_params += "double"; + only_pyobjects = false; + ++num_params; } else if (TypeManager::is_float(type)) { - indent(out, indent_level) << "double " << param_name << ";\n"; - format_specifiers += "d"; - parameter_list += ", &" + param_name; + if (single_arg) { + pexpr_string = "(float) PyFloat_AsDouble(arg)"; + extra_param_check += " && PyNumber_Check(arg)"; + } else { + indent(out, indent_level) << "float " << param_name << ";\n"; + format_specifiers += "f"; + parameter_list += ", &" + param_name; + } expected_params += "float"; + only_pyobjects = false; + ++num_params; } else if (TypeManager::is_char_pointer(type)) { indent(out, indent_level) << "char *" << param_name << ";\n"; format_specifiers += "s"; parameter_list += ", &" + param_name; expected_params += "string"; + only_pyobjects = false; + ++num_params; } else if (TypeManager::is_pointer_to_PyObject(type)) { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - format_specifiers += "O"; - parameter_list += ", &" + param_name; + if (single_arg) { + // This is a single-arg function, so there's no need + // to convert anything. + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } pexpr_string = param_name; - pname_for_pyobject += param_name; expected_params += "any"; + ++num_params; + + // It's reasonable to assume that a function taking a PyObject + // might also throw a TypeError if the type is incorrect. + check_exceptions = true; } else if (TypeManager::is_pointer_to_Py_buffer(type)) { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - format_specifiers += "O"; - parameter_list += ", &" + param_name; + if (single_arg) { + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } extra_convert += "PyObject *" + param_name + "_buffer = PyMemoryView_FromObject(" + param_name + ");"; - extra_param_check += " || (" + param_name + "_buffer == NULL)"; + extra_param_check += " && " + param_name + "_buffer != NULL"; pexpr_string = "PyMemoryView_GET_BUFFER(" + param_name + "_buffer)"; extra_cleanup += "Py_XDECREF(" + param_name + "_buffer);"; expected_params += "memoryview"; - pname_for_pyobject += param_name; + ++num_params; + check_exceptions = true; } else if (TypeManager::is_pointer(type)) { CPPType *obj_type = TypeManager::unwrap(TypeManager::resolve_type(type)); @@ -2989,17 +3212,21 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, if (const_ok) { expected_params += "const "; - } else { - expected_params += "non-const "; + //} else { + // expected_params += "non-const "; } expected_params += classNameFromCppName(obj_type->get_simple_name(), false); if (!remap->_has_this || pn != 0) { - indent(out, indent_level) - << "PyObject *" << param_name << ";\n"; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - pname_for_pyobject += param_name; + if (single_arg) { + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } + + ++num_params; TypeIndex p_type_index = builder.get_type(obj_type, false); InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); @@ -3019,7 +3246,6 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, // need to a forward scope for this class.. if (!isExportThisRun(p_itype._cpptype)) { _external_imports.insert(make_safe_name(p_itype.get_scoped_name())); - //ForwardDeclrs << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" << make_safe_name(p_itype.get_scoped_name()) << ";\n"; } string class_name; @@ -3033,7 +3259,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, str << "DTOOL_Call_GetPointerThisClass(" << param_name << ", &Dtool_" << make_safe_name(p_itype.get_scoped_name()) << ", " << pn << ", \"" - << method_prefix << methodNameFromCppName(func1, class_name, false) + << method_prefix << methodNameFromCppName(func, class_name, false) << "\", " << const_ok; if (coercion_possible && !is_copy_constructor) { // We never attempt to coerce a copy constructor parameter. @@ -3046,17 +3272,21 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, str << ");\n"; extra_convert += str.str(); - extra_param_check += " || (" + param_name + "_this == NULL)"; + extra_param_check += " && " + param_name + "_this != NULL"; pexpr_string = param_name + "_this"; } } else { // Ignore a parameter. - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - format_specifiers += "O"; - parameter_list += ", &" + param_name; + if (single_arg) { + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } expected_params += "any"; - pname_for_pyobject += param_name; + ++num_params; } if (remap->_parameters[pn]._has_name) { @@ -3075,83 +3305,75 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, } expected_params += ")\n"; - // If we got what claimed to be a unary operator, don't check for - // parameters, since we won't be getting any anyway. - if (!func1->_ifunc.is_unary_op()) { + // If this is the only overload, don't bother checking for type errors. + // Any type error that is raised will simply pass through to below. + if (func->_remaps.size() == 1) { + check_exceptions = false; + } + + // Track how many curly braces we've opened. + short open_scopes = 0; + + if (!format_specifiers.empty()) { std::string format_specifiers1 = format_specifiers + ":" + - methodNameFromCppName(func1, "", false); - indent(out, indent_level) - << "static char *keyword_list[] = {" << keyword_list << "NULL};\n"; + methodNameFromCppName(func, "", false); - if (remap->_parameters.size() == 1 || - (remap->_has_this && remap->_parameters.size() == 2)) { + if (have_kwargs) { indent(out, indent_level) - << "// Special case to make operators work\n"; - indent(out, indent_level) - << "if (PyTuple_Check(args) || (kwds != NULL && PyDict_Check(kwds))) {\n"; - indent(out, indent_level) - << " PyArg_ParseTupleAndKeywords(args, kwds, \"" - << format_specifiers1 << "\", keyword_list" << parameter_list - << ");\n"; - indent(out, indent_level) - << "} else {\n"; - indent(out, indent_level) - << " PyArg_Parse(args, \"" << format_specifiers1 << "\"" - << parameter_list << ");\n"; - indent(out, indent_level) - << "}\n"; - indent(out, indent_level) - << "if (!PyErr_Occurred()) {\n"; - - } else { + << "static char *keyword_list[] = {" << keyword_list << "NULL};\n"; indent(out, indent_level) << "if (PyArg_ParseTupleAndKeywords(args, kwds, \"" - << format_specifiers1 << "\", keyword_list" + << format_specifiers1 << "\", keyword_list" << parameter_list + << ")) {\n"; + + ++open_scopes; + indent_level += 2; + + } else if (have_varargs) { + if (only_pyobjects) { + // All parameters are PyObject*, so we can use the slightly + // more efficient PyArg_UnpackTuple function instead. + indent(out, indent_level) + << "if (PyArg_UnpackTuple(args, \"" + << methodNameFromCppName(func, "", false) + << "\", " << num_params + << ", " << num_params + << parameter_list << ")) {\n"; + } else { + indent(out, indent_level) + << "if (PyArg_ParseTuple(args, \"" + << format_specifiers1 << "\"" + << parameter_list << ")) {\n"; + } + ++open_scopes; + indent_level += 2; + + } else if (single_arg && !only_pyobjects && + format_specifiers != "" && + format_specifiers != "O") { + indent(out, indent_level) + << "if (PyArg_Parse(arg, \"" << format_specifiers << "\"" << parameter_list << ")) {\n"; + + ++open_scopes; + indent_level += 2; } - } else { - indent(out, indent_level) << "{\n"; } if (!extra_convert.empty()) { - indent(out, indent_level + 2) + indent(out, indent_level) << extra_convert << "\n"; } - int extra_indent_level = indent_level + 2; - if (!extra_param_check.empty()) { - indent(out, extra_indent_level) - << "if (!(" << extra_param_check.substr(3) << ")) {\n"; - extra_indent_level += 2; - } - - if ((remap->_flags & (FunctionRemap::F_getitem_int | FunctionRemap::F_setitem_int)) && - (obj != (Object *)NULL && (obj->_protocol_types & Object::PT_sequence))) { - // This is a getitem or setitem of a sequence type. This means we - // *need* to raise IndexError if we're out of bounds. We have to - // assume the bounds are 0 .. this->size() (this is the same - // assumption that Python makes). - string pexpr_string = pexprs[1]; - indent(out, extra_indent_level) - << "if ((" << pexpr_string << ") < 0 || (" << pexpr_string << ") >= (" - << container << ")->size()) {\n"; - if (coercion_possible) { - indent(out, extra_indent_level + 2) - << "Py_XDECREF(coerced);\n"; - } - if (!args_cleanup.empty()) { - indent(out, extra_indent_level + 2) - << args_cleanup << "\n"; - } - indent(out, extra_indent_level + 2) - << "PyErr_SetString(PyExc_IndexError, \"Out of bounds.\");\n"; - indent(out, extra_indent_level + 2) - << "return NULL;\n"; - indent(out, extra_indent_level) - << "}\n"; + indent(out, indent_level) + << "if (" << extra_param_check.substr(4) << ") {\n"; + + ++open_scopes; + indent_level += 2; } + string return_expr; if (!remap->_void_return && remap->_return_type->new_type_is_atomic_string()) { // Treat strings as a special case. We don't want to format the @@ -3160,117 +3382,175 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, // With SIMPLE_THREADS, it's important that we never release the // interpreter lock. out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; - indent(out, extra_indent_level) + indent(out, indent_level) << "PyThreadState *_save;\n"; - indent(out, extra_indent_level) + indent(out, indent_level) << "Py_UNBLOCK_THREADS\n"; out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; } if (track_interpreter) { - indent(out, extra_indent_level) << "in_interpreter = 0;\n"; + indent(out, indent_level) << "in_interpreter = 0;\n"; } string tt; - string return_expr = remap->call_function(out, extra_indent_level, false, container, pexprs); + return_expr = remap->call_function(out, indent_level, false, container, pexprs); CPPType *type = remap->_return_type->get_orig_type(); - indent(out, extra_indent_level); + indent(out, indent_level); type->output_instance(out, "return_value", &parser); // type->output_instance(tt, "return_value", &parser); out << " = " << return_expr << ";\n"; if (track_interpreter) { - indent(out, extra_indent_level) << "in_interpreter = 1;\n"; + indent(out, indent_level) << "in_interpreter = 1;\n"; } if (remap->_blocking) { out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; - indent(out, extra_indent_level) + indent(out, indent_level) << "Py_BLOCK_THREADS\n"; out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; } if (!extra_cleanup.empty()) { - indent(out, extra_indent_level) << extra_cleanup << "\n"; + indent(out, indent_level) << extra_cleanup << "\n"; } return_expr = manage_return_value(out, 4, remap, "return_value"); - do_assert_init(out, extra_indent_level, is_constructor, args_cleanup); - pack_return_value(out, extra_indent_level, remap, return_expr, is_inplace); } else { if (remap->_blocking) { out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; - indent(out, extra_indent_level) + indent(out, indent_level) << "PyThreadState *_save;\n"; - indent(out, extra_indent_level) + indent(out, indent_level) << "Py_UNBLOCK_THREADS\n"; out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; } if (track_interpreter) { - indent(out, extra_indent_level) << "in_interpreter = 0;\n"; + indent(out, indent_level) << "in_interpreter = 0;\n"; } - string return_expr = remap->call_function(out, extra_indent_level, true, container, pexprs); + return_expr = remap->call_function(out, indent_level, true, container, pexprs); if (return_expr.empty()) { if (track_interpreter) { - indent(out, extra_indent_level) << "in_interpreter = 1;\n"; + indent(out, indent_level) << "in_interpreter = 1;\n"; } if (remap->_blocking) { out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; - indent(out, extra_indent_level) + indent(out, indent_level) << "Py_BLOCK_THREADS\n"; out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; } if (!extra_cleanup.empty()) { - indent(out, extra_indent_level) << extra_cleanup << "\n"; + indent(out, indent_level) << extra_cleanup << "\n"; } if (coercion_possible) { - indent(out, extra_indent_level) + indent(out, indent_level) << "Py_XDECREF(coerced);\n"; } - do_assert_init(out, extra_indent_level, is_constructor, args_cleanup); - indent(out, extra_indent_level) << "return Py_BuildValue(\"\");\n"; } else { CPPType *type = remap->_return_type->get_temporary_type(); if (!is_inplace) { - indent(out, extra_indent_level); + indent(out, indent_level); type->output_instance(out, "return_value", &parser); out << " = " << return_expr << ";\n"; } if (track_interpreter) { - indent(out, extra_indent_level) << "in_interpreter = 1;\n"; + indent(out, indent_level) << "in_interpreter = 1;\n"; } if (remap->_blocking) { out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; - indent(out, extra_indent_level) + indent(out, indent_level) << "Py_BLOCK_THREADS\n"; out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; } if (!extra_cleanup.empty()) { - indent(out, extra_indent_level) << extra_cleanup << "\n"; + indent(out, indent_level) << extra_cleanup << "\n"; } if (!is_inplace) { - return_expr = manage_return_value(out, extra_indent_level, remap, "return_value"); + return_expr = manage_return_value(out, indent_level, remap, "return_value"); } if (coercion_possible) { - indent(out, extra_indent_level) + indent(out, indent_level) << "Py_XDECREF(coerced);\n"; } - do_assert_init(out, extra_indent_level, is_constructor, args_cleanup); - pack_return_value(out, extra_indent_level, remap, remap->_return_type->temporary_to_return(return_expr), is_inplace); + return_expr = remap->_return_type->temporary_to_return(return_expr); } } - // Close the extra brace opened within do_assert_init(). - extra_indent_level -= 2; - indent(out, extra_indent_level) - << "}\n"; + // If a method raises TypeError, continue. + if (check_exceptions) { + indent(out, indent_level) + << "if (PyErr_Occurred()) {\n"; + if (return_int && !return_expr.empty()) { + indent(out, indent_level + 2) + << "delete return_value;\n"; + } + indent(out, indent_level) + << " if (PyErr_ExceptionMatches(PyExc_TypeError)) {\n"; + indent(out, indent_level) + << " // TypeError raised; continue to next overload type.\n"; + indent(out, indent_level) + << " } else {\n"; + if (return_int) { + indent(out, indent_level + 2) + << " return -1;\n"; + } else { + indent(out, indent_level + 2) + << " return (PyObject *)NULL;\n"; + } + indent(out, indent_level) + << " }\n"; + indent(out, indent_level) + << "} else {\n"; - if (!extra_param_check.empty()) { - extra_indent_level -= 2; - indent(out, extra_indent_level) << "}\n"; + ++open_scopes; + indent_level += 2; } - indent(out, indent_level) << "}\n"; + // Outputs code to check to see if an assertion has failed while + // the C++ code was executing, and report this failure back to Python. + if (watch_asserts) { + out << "#ifndef NDEBUG\n"; + indent(out, indent_level) + << "Notify *notify = Notify::ptr();\n"; + indent(out, indent_level) + << "if (notify->has_assert_failed()) {\n"; + indent(out, indent_level + 2) + << "PyErr_SetString(PyExc_AssertionError, notify->get_assert_error_message().c_str());\n"; + indent(out, indent_level + 2) + << "notify->clear_assert_failed();\n"; + if (return_int) { + if (!return_expr.empty()) { + indent(out, indent_level + 2) << "delete return_value;\n"; + } + indent(out, indent_level + 2) << "return -1;\n"; + } else { + indent(out, indent_level + 2) << "return (PyObject *)NULL;\n"; + } + + indent(out, indent_level) + << "}\n"; + out << "#endif\n"; + } + + if (return_expr.empty()) { + if (return_int) { + indent(out, indent_level) << "return 0;\n"; + } else { + indent(out, indent_level) << "Py_INCREF(Py_None);\n"; + indent(out, indent_level) << "return Py_None;\n"; + } + } else { + pack_return_value(out, indent_level, remap, return_expr, is_inplace); + } + + // Close the extra braces opened earlier. + while (open_scopes > 0) { + indent_level -= 2; + indent(out, indent_level) << "}\n"; + + --open_scopes; + } } //////////////////////////////////////////////////////////////////// @@ -3311,7 +3591,8 @@ pack_return_value(ostream &out, int indent_level, FunctionRemap *remap, //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, - ParameterRemap *return_type, const string &return_expr, const string &assign_to, bool is_inplace) { + ParameterRemap *return_type, const string &return_expr, + const string &assign_to, bool is_inplace) { CPPType *orig_type = return_type->get_orig_type(); CPPType *type = return_type->get_new_type(); @@ -3491,7 +3772,6 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, if (!isExportThisRun(itype._cpptype)) { _external_imports.insert(make_safe_name(itype.get_scoped_name())); - //ForwardDeclrs << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" << make_safe_name(itype.get_scoped_name()) << ";\n"; } write_python_instance(out, indent_level, return_expr, assign_to, owns_memory_flag, itype.get_scoped_name(), itype._cpptype, is_inplace, const_flag); @@ -3511,10 +3791,8 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, if (!isExportThisRun(itype._cpptype)) { _external_imports.insert(make_safe_name(itype.get_scoped_name())); - //ForwardDeclrs << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" << make_safe_name(itype.get_scoped_name()) << ";\n"; } - // ForwardDeclrs << "extern \"C\" struct Dtool_PyTypedObject Dtool_" << make_safe_name(itype.get_scoped_name()) << ";\n"; write_python_instance(out, indent_level, return_expr, assign_to, owns_memory_flag, itype.get_scoped_name(), itype._cpptype, is_inplace, const_flag); } else { TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false); @@ -3523,10 +3801,8 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, if (!isExportThisRun(itype._cpptype)) { _external_imports.insert(make_safe_name(itype.get_scoped_name())); - //ForwardDeclrs << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" << make_safe_name(itype.get_scoped_name()) << ";\n"; } - // ForwardDeclrs << "extern \"C\" struct Dtool_PyTypedObject Dtool_" << make_safe_name(itype.get_scoped_name()) << ";\n"; write_python_instance(out, indent_level, return_expr, assign_to, owns_memory_flag, itype.get_scoped_name(), itype._cpptype, is_inplace, const_flag); } } @@ -3544,10 +3820,8 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, if (!isExportThisRun(itype._cpptype)) { _external_imports.insert(make_safe_name(itype.get_scoped_name())); - //ForwardDeclrs << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" << make_safe_name(itype.get_scoped_name()) << ";\n"; } - // ForwardDeclrs << "extern \"C\" struct Dtool_PyTypedObject Dtool_" << make_safe_name(itype.get_scoped_name()) << ";\n"; write_python_instance(out, indent_level, return_expr, assign_to, owns_memory_flag, itype.get_scoped_name(), itype._cpptype, is_inplace, const_flag); } else { @@ -3834,21 +4108,25 @@ isExportThisRun(Function *func) { // Function : is_remap_legal ////////////////////////////////////////////// bool InterfaceMakerPythonNative:: -is_remap_legal(FunctionRemap &remap) { +is_remap_legal(FunctionRemap *remap) { + if (remap == NULL) { + return false; + } + // return must be legal and managable.. - if (!is_cpp_type_legal(remap._return_type->get_orig_type())) { -// printf(" is_remap_legal Return Is Bad %s\n",remap._return_type->get_orig_type()->get_fully_scoped_name().c_str()); + if (!is_cpp_type_legal(remap->_return_type->get_orig_type())) { +// printf(" is_remap_legal Return Is Bad %s\n",remap->_return_type->get_orig_type()->get_fully_scoped_name().c_str()); return false; } // ouch .. bad things will happen here .. do not even try.. - if (remap._ForcedVoidReturn) { + if (remap->_ForcedVoidReturn) { return false; } // all params must be legal - for (int pn = 0; pn < (int)remap._parameters.size(); pn++) { - CPPType *orig_type = remap._parameters[pn]._remap->get_orig_type(); + for (int pn = 0; pn < (int)remap->_parameters.size(); pn++) { + CPPType *orig_type = remap->_parameters[pn]._remap->get_orig_type(); if (!is_cpp_type_legal(orig_type)) { return false; } @@ -3866,7 +4144,7 @@ is_function_legal(Function *func) { Function::Remaps::const_iterator ri; for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { FunctionRemap *remap = (*ri); - if (is_remap_legal(*remap)) { + if (is_remap_legal(remap)) { // printf(" Function Is Marked Legal %s\n",func->_name.c_str()); return true; @@ -3894,65 +4172,6 @@ isFunctionWithThis(Function *func) { return false; } -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPython::test_assert -// Access: Protected -// Description: Outputs code to check to see if an assertion has -// failed while the C++ code was executing, and report -// this failure back to Python. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonNative::do_assert_init(ostream &out, int &indent_level, bool constructor, const string &args_cleanup) const { - // If a method raises TypeError, continue. - indent(out, indent_level) - << "if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_TypeError)) {\n"; - indent(out, indent_level + 2) - << "// TypeError raised; continue to next overload type.\n"; - if (constructor) { - indent(out, indent_level + 2) - << "delete return_value;\n"; - } - indent(out, indent_level) - << "} else {\n"; - indent_level += 2; - - if (!args_cleanup.empty()) { - indent(out, indent_level) - << args_cleanup << "\n"; - } - - if (watch_asserts) { - out << "#ifndef NDEBUG\n"; - indent(out, indent_level) - << "Notify *notify = Notify::ptr();\n"; - indent(out, indent_level) - << "if (notify->has_assert_failed()) {\n"; - indent(out, indent_level + 2) - << "PyErr_SetString(PyExc_AssertionError, notify->get_assert_error_message().c_str());\n"; - indent(out, indent_level + 2) - << "notify->clear_assert_failed();\n"; - if (constructor) { - indent(out, indent_level + 2) << "delete return_value;\n"; - indent(out, indent_level + 2) << "return -1;\n"; - } else { - indent(out, indent_level + 2) << "return (PyObject *)NULL;\n"; - } - - indent(out, indent_level) - << "}\n"; - out << "#endif\n"; - indent(out, indent_level) - << "if (PyErr_Occurred()) {\n"; - if (constructor) { - indent(out, indent_level + 2) << "delete return_value;\n"; - indent(out, indent_level + 2) << "return -1;\n"; - } else { - indent(out, indent_level + 2) << "return (PyObject *)NULL;\n"; - } - - indent(out, indent_level) - << "}\n"; - } -} //////////////////////////////////////////////////////// // Function : IsRunTimeTyped /////////////////////////////////////////////////////// diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.h b/dtool/src/interrogate/interfaceMakerPythonNative.h index 85d49b8039..e8f0e15352 100755 --- a/dtool/src/interrogate/interfaceMakerPythonNative.h +++ b/dtool/src/interrogate/interfaceMakerPythonNative.h @@ -89,25 +89,31 @@ private: }; static bool get_slotted_function_def(Object *obj, Function *func, SlottedFunctionDef &def); - + void write_prototype_for_name(ostream &out, Function *func, const std::string &name); void write_prototype_for(ostream &out, Function *func); - void write_function_for_name(ostream &out, Object *obj, Function *func, const std::string &name, const std::string &PreProcess, const std::string &ClassName, - bool coercion_allowed, bool &coercion_attempted); void write_function_for_top(ostream &out, Object *obj, Function *func, const std::string &PreProcess); - void write_function_instance(ostream &out, Object *obj, Function *func, - FunctionRemap *remap, string &expected_params, - int indent_level, bool errors_fatal, - ostream &forwarddecl, const std::string &functionnamestr, - bool is_inplace, bool coercion_allowed, - bool &coercion_attempted, - const string &args_cleanup); - + void write_function_for_name(ostream &out, Object *obj, Function *func, + const std::string &name, const std::string &PreProcess, + bool coercion_allowed, bool &coercion_attempted, + bool single_arg, bool have_varargs, + bool have_kwargs, bool return_int); + void write_function_forset(ostream &out, Object *obj, Function *func, std::set &remaps, string &expected_params, - int indent_level, ostream &forwarddecl, bool inplace, - bool coercion_allowed, bool &coercion_attempted, - const string &args_cleanup); + int indent_level, bool inplace, + bool coercion_allowed, bool &coercion_attempted, + bool single_arg, bool have_varargs, + bool have_kwargs, bool return_int, + const string &first_expr = string()); + + void write_function_instance(ostream &out, Object *obj, Function *func, + FunctionRemap *remap, string &expected_params, + int indent_level, bool is_inplace, + bool coercion_allowed, bool &coercion_attempted, + bool single_arg, bool have_varargs, + bool have_kwargs, bool return_int, + const string &first_pexpr = string()); void pack_return_value(ostream &out, int indent_level, FunctionRemap *remap, const std::string &return_expr, bool in_place); @@ -121,17 +127,16 @@ private: void write_class_prototypes(ostream &out) ; void write_class_declarations(ostream &out, ostream *out_h, Object *obj); void write_class_details(ostream &out, Object *obj); - - void do_assert_init(ostream &out, int &indent_level, bool constructor, const string &args_cleanup) const; + public: - bool is_remap_legal(FunctionRemap &remap); + bool is_remap_legal(FunctionRemap *remap); bool is_function_legal( Function *func); bool is_cpp_type_legal(CPPType *ctype); bool isExportThisRun(CPPType *ctype); bool isExportThisRun(Function *func); bool isFunctionWithThis( Function *func); bool IsRunTimeTyped(const InterrogateType &itype); - + // comunicates the cast capabilites among methods.. struct CastDetails { CPPStructType *_structType; diff --git a/dtool/src/interrogate/typeManager.cxx b/dtool/src/interrogate/typeManager.cxx index 03b742e6a3..245724b6ef 100644 --- a/dtool/src/interrogate/typeManager.cxx +++ b/dtool/src/interrogate/typeManager.cxx @@ -934,7 +934,7 @@ bool TypeManager:: is_double(CPPType *type) { switch (type->get_subtype()) { case CPPDeclaration::ST_const: - return is_float(type->as_const_type()->_wrapped_around); + return is_double(type->as_const_type()->_wrapped_around); case CPPDeclaration::ST_simple: { @@ -1199,7 +1199,8 @@ is_PyObject(CPPType *type) { return is_PyObject(type->as_const_type()->_wrapped_around); case CPPDeclaration::ST_extension: - return (type->get_local_name(&parser) == "PyObject"); + return (type->get_local_name(&parser) == "PyObject" || + type->get_local_name(&parser) == "_object"); default: return false; diff --git a/dtool/src/interrogatedb/dtool_super_base.cxx b/dtool/src/interrogatedb/dtool_super_base.cxx index 8b780e11f2..b92ce15f94 100644 --- a/dtool/src/interrogatedb/dtool_super_base.cxx +++ b/dtool/src/interrogatedb/dtool_super_base.cxx @@ -83,7 +83,7 @@ int Dtool_Init_DTOOL_SUPER_BASE(PyObject *self, PyObject *args, PyObject *kwds) return -1; } -int Dtool_InitNoCoerce_DTOOL_SUPER_BASE(PyObject *self, PyObject *args, PyObject *kwds) { +int Dtool_InitNoCoerce_DTOOL_SUPER_BASE(PyObject *self, PyObject *args) { PyErr_SetString(PyExc_TypeError, "cannot init super base"); return -1; } diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index f0123e459e..f7606ec407 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -79,9 +79,21 @@ attempt_coercion(PyObject *self, Dtool_PyTypedObject *classdef, if (classdef->_PyType.tp_new != NULL) { obj = classdef->_PyType.tp_new(&classdef->_PyType, self, NULL); assert(obj != NULL); - if (classdef->_Dtool_InitNoCoerce(obj, self, NULL) != 0) { - Py_DECREF(obj); - obj = NULL; + + if (PyTuple_Check(self)) { + // A tuple was passed, which we assume are the constructor arguments. + if (classdef->_Dtool_InitNoCoerce(obj, self) != 0) { + Py_DECREF(obj); + obj = NULL; + } + } else { + // We need to pack the value into an args tuple. + PyObject *args = PyTuple_Pack(1, self); + if (classdef->_Dtool_InitNoCoerce(obj, args) != 0) { + Py_DECREF(obj); + obj = NULL; + } + Py_DECREF(args); } } if (obj == NULL) { @@ -93,7 +105,11 @@ attempt_coercion(PyObject *self, Dtool_PyTypedObject *classdef, PyObject *make = PyObject_GetAttrString((PyObject *)classdef, "make"); if (make != NULL) { PyErr_Clear(); - obj = PyObject_Call(make, self, NULL); + if (PyTuple_Check(self)) { + obj = PyObject_CallObject(make, self); + } else { + obj = PyObject_CallFunctionObjArgs(make, self, NULL); + } Py_DECREF(make); } } @@ -598,7 +614,7 @@ int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2) { PyErr_Clear(); } else { PyObject *res = NULL; - PyObject *args = Py_BuildValue("(O)", v2); + PyObject *args = PyTuple_Pack(1, v2); if (args != NULL) { res = PyObject_Call(func, args, NULL); Py_DECREF(args); diff --git a/dtool/src/interrogatedb/py_panda.h b/dtool/src/interrogatedb/py_panda.h index a9779300f4..0811c6c795 100755 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -42,18 +42,23 @@ #if defined(HAVE_PYTHON) && !defined(CPPPARSER) -#ifdef HAVE_LONG_LONG -#undef HAVE_LONG_LONG -#endif #ifdef _POSIX_C_SOURCE #undef _POSIX_C_SOURCE #endif +#define PY_SSIZE_T_CLEAN 1 + #include "Python.h" #include "structmember.h" -#ifdef HAVE_LONG_LONG -#undef HAVE_LONG_LONG -#endif + +#ifndef HAVE_LONG_LONG +#define PyLong_FromLongLong(x) PyLong_FromLong((long) (x)) +#define PyLong_FromUnsignedLongLong(x) PyLong_FromUnsignedLong((unsigned long) (x)) +#define PyLong_AsLongLong(x) PyLong_AsLong(x) +#define PyLong_AsUnsignedLongLong(x) PyLong_AsUnsignedLong(x) +#define PyLong_AsUnsignedLongLongMask(x) PyLong_AsUnsignedLongMask(x) +#define PyLong_AsLongLongAndOverflow(x) PyLong_AsLongAndOverflow(x) +#endif #if PY_VERSION_HEX < 0x02050000 @@ -139,7 +144,7 @@ typedef void * ( * ConvertFunctionType )(PyObject *,Dtool_PyTypedObject * ); typedef void * ( * ConvertFunctionType1 )(void *, Dtool_PyTypedObject *); typedef void ( *FreeFunction )(PyObject *); typedef void ( *PyModuleClassInit)(PyObject *module); -typedef int ( *InitNoCoerce)(PyObject *self, PyObject *args, PyObject *kwds); +typedef int ( *InitNoCoerce)(PyObject *self, PyObject *args); //inline Dtool_PyTypedObject * Dtool_RuntimeTypeDtoolType(int type); inline void Dtool_Deallocate_General(PyObject * self); @@ -369,7 +374,7 @@ EXPCL_DTOOLCONFIG PyObject *DTool_CreatePyInstance(void *local_this, Dtool_PyTyp extern EXPORT_THIS Dtool_PyTypedObject Dtool_##CLASS_NAME;\ extern struct PyMethodDef Dtool_Methods_##CLASS_NAME[];\ int Dtool_Init_##CLASS_NAME(PyObject *self, PyObject *args, PyObject *kwds);\ -int Dtool_InitNoCoerce_##CLASS_NAME(PyObject *self, PyObject *args, PyObject *kwds);\ +int Dtool_InitNoCoerce_##CLASS_NAME(PyObject *self, PyObject *args);\ PyObject * Dtool_new_##CLASS_NAME(PyTypeObject *type, PyObject *args, PyObject *kwds);\ void * Dtool_UpcastInterface_##CLASS_NAME(PyObject *self, Dtool_PyTypedObject *requested_type);\ void * Dtool_DowncastInterface_##CLASS_NAME(void *self, Dtool_PyTypedObject *requested_type);\ diff --git a/dtool/src/parser-inc/Python.h b/dtool/src/parser-inc/Python.h index d63928fdfe..a059adca2c 100755 --- a/dtool/src/parser-inc/Python.h +++ b/dtool/src/parser-inc/Python.h @@ -20,7 +20,9 @@ #ifndef PYTHON_H #define PYTHON_H -class PyObject; +struct _object; +typedef _object PyObject; + class PyThreadState; typedef int Py_ssize_t; struct Py_buffer; diff --git a/dtool/src/pystub/pystub.cxx b/dtool/src/pystub/pystub.cxx index 75724d427c..214ea01ba0 100644 --- a/dtool/src/pystub/pystub.cxx +++ b/dtool/src/pystub/pystub.cxx @@ -18,6 +18,7 @@ extern "C" { EXPCL_PYSTUB int PyArg_Parse(...); EXPCL_PYSTUB int PyArg_ParseTuple(...); EXPCL_PYSTUB int PyArg_ParseTupleAndKeywords(...); + EXPCL_PYSTUB int PyArg_UnpackTuple(...); EXPCL_PYSTUB int PyBool_FromLong(...); EXPCL_PYSTUB int PyBuffer_Release(...); EXPCL_PYSTUB int PyBytes_AsString(...); @@ -84,12 +85,14 @@ extern "C" { EXPCL_PYSTUB int PyModule_AddObject(...); EXPCL_PYSTUB int PyModule_AddStringConstant(...); EXPCL_PYSTUB int PyModule_Create2(...); + EXPCL_PYSTUB int PyNumber_Check(...); EXPCL_PYSTUB int PyNumber_Float(...); EXPCL_PYSTUB int PyNumber_Int(...); EXPCL_PYSTUB int PyNumber_Long(...); EXPCL_PYSTUB int PyObject_ASCII(...); EXPCL_PYSTUB int PyObject_Call(...); EXPCL_PYSTUB int PyObject_CallFunction(...); + EXPCL_PYSTUB int PyObject_CallFunctionObjArgs(...); EXPCL_PYSTUB int PyObject_CallMethod(...); EXPCL_PYSTUB int PyObject_CallMethodObjArgs(...); EXPCL_PYSTUB int PyObject_CallObject(...); @@ -153,8 +156,14 @@ extern "C" { EXPCL_PYSTUB int Py_InitModule4(...); EXPCL_PYSTUB int Py_InitModule4_64(...); EXPCL_PYSTUB int Py_InitModule4TraceRefs(...); + EXPCL_PYSTUB int _PyArg_ParseTuple_SizeT(...); + EXPCL_PYSTUB int _PyArg_ParseTupleAndKeywords_SizeT(...); + EXPCL_PYSTUB int _PyArg_Parse_SizeT(...); + EXPCL_PYSTUB int _PyObject_CallFunction_SizeT(...); + EXPCL_PYSTUB int _PyObject_CallMethod_SizeT(...); EXPCL_PYSTUB int _PyObject_DebugFree(...); EXPCL_PYSTUB int _PyObject_Del(...); + EXPCL_PYSTUB int _Py_BuildValue_SizeT(...); EXPCL_PYSTUB int _Py_Dealloc(...); EXPCL_PYSTUB int _Py_NegativeRefcount(...); EXPCL_PYSTUB int _Py_RefTotal(...); @@ -183,6 +192,7 @@ extern "C" { int PyArg_Parse(...) { return 0; }; int PyArg_ParseTuple(...) { return 0; } int PyArg_ParseTupleAndKeywords(...) { return 0; } +int PyArg_UnpackTuple(...) { return 0; }; int PyBool_FromLong(...) { return 0; } int PyBuffer_Release(...) { return 0; } int PyBytes_AsString(...) { return 0; } @@ -249,12 +259,14 @@ int PyModule_AddIntConstant(...) { return 0; }; int PyModule_AddObject(...) { return 0; }; int PyModule_AddStringConstant(...) { return 0; }; int PyModule_Create2(...) { return 0; }; +int PyNumber_Check(...) { return 0; } int PyNumber_Float(...) { return 0; } int PyNumber_Int(...) { return 0; } int PyNumber_Long(...) { return 0; } int PyObject_ASCII(...) { return 0; } int PyObject_Call(...) { return 0; } int PyObject_CallFunction(...) { return 0; } +int PyObject_CallFunctionObjArgs(...) { return 0; } int PyObject_CallMethod(...) { return 0; } int PyObject_CallMethodObjArgs(...) { return 0; } int PyObject_CallObject(...) { return 0; } @@ -318,8 +330,14 @@ int Py_BuildValue(...) { return 0; } int Py_InitModule4(...) { return 0; } int Py_InitModule4_64(...) { return 0; } int Py_InitModule4TraceRefs(...) { return 0; }; +int _PyArg_ParseTuple_SizeT(...) { return 0; }; +int _PyArg_ParseTupleAndKeywords_SizeT(...) { return 0; }; +int _PyArg_Parse_SizeT(...) { return 0; }; +int _PyObject_CallFunction_SizeT(...) { return 0; }; +int _PyObject_CallMethod_SizeT(...) { return 0; }; int _PyObject_DebugFree(...) { return 0; }; int _PyObject_Del(...) { return 0; }; +int _Py_BuildValue_SizeT(...) { return 0; }; int _Py_Dealloc(...) { return 0; }; int _Py_NegativeRefcount(...) { return 0; }; int _Py_RefTotal(...) { return 0; }; diff --git a/panda/src/pgraph/nodePath_ext.cxx b/panda/src/pgraph/nodePath_ext.cxx index 0a9b570194..4192675f1b 100644 --- a/panda/src/pgraph/nodePath_ext.cxx +++ b/panda/src/pgraph/nodePath_ext.cxx @@ -168,7 +168,7 @@ __reduce_persist__(PyObject *self, PyObject *pickler) const { } } - PyObject *result = Py_BuildValue("(O(s#))", func, bam_stream.data(), bam_stream.size()); + PyObject *result = Py_BuildValue("(O(s#))", func, bam_stream.data(), (Py_ssize_t) bam_stream.size()); Py_DECREF(func); Py_DECREF(this_class); return result; diff --git a/panda/src/putil/typedWritable_ext.cxx b/panda/src/putil/typedWritable_ext.cxx index ebe5f966d5..1cf11e0852 100644 --- a/panda/src/putil/typedWritable_ext.cxx +++ b/panda/src/putil/typedWritable_ext.cxx @@ -119,7 +119,7 @@ __reduce_persist__(PyObject *self, PyObject *pickler) const { } } - PyObject *result = Py_BuildValue("(O(Os#))", func, this_class, bam_stream.data(), bam_stream.size()); + PyObject *result = Py_BuildValue("(O(Os#))", func, this_class, bam_stream.data(), (Py_ssize_t) bam_stream.size()); Py_DECREF(func); Py_DECREF(this_class); return result; @@ -234,10 +234,10 @@ py_decode_TypedWritable_from_bam_stream_persist(PyObject *pickler, PyObject *thi PyObject *result; if (py_reader != NULL){ - result = PyObject_CallFunction(func, (char *)"(s#O)", data.data(), data.size(), py_reader); + result = PyObject_CallFunction(func, (char *)"(s#O)", data.data(), (Py_ssize_t) data.size(), py_reader); Py_DECREF(py_reader); } else { - result = PyObject_CallFunction(func, (char *)"(s#)", data.data(), data.size()); + result = PyObject_CallFunction(func, (char *)"(s#)", data.data(), (Py_ssize_t) data.size()); } if (result == NULL) {