From 7ff8b62fb8e90d692ec098f06b97cd2d8a14747c Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 19 Sep 2017 18:46:21 +0200 Subject: [PATCH] interrogate: various fixes: * handle static methods with explicit self * fix len() and __setitem__ of mapping types * fix inheritance of __getattr__ and __setattr__ * fix overload resolution error with nullptr_t arguments * bool overloads now come after float/double overloads * record whether class is final in interrogatedb * add Dtool_EmptyTuple * optimization for final classes: no need for downcast --- dtool/src/interrogate/functionRemap.cxx | 18 +++--- dtool/src/interrogate/interfaceMaker.cxx | 2 +- .../interfaceMakerPythonNative.cxx | 64 ++++++++++++------- dtool/src/interrogate/interrogateBuilder.cxx | 24 +++++-- dtool/src/interrogatedb/interrogateType.I | 8 +++ dtool/src/interrogatedb/interrogateType.h | 2 + dtool/src/interrogatedb/py_panda.cxx | 14 ++-- dtool/src/interrogatedb/py_panda.h | 6 ++ 8 files changed, 90 insertions(+), 48 deletions(-) diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index d58cdf8b88..5b04209c48 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -772,16 +772,14 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak first_param = 1; } - if (_has_this || _type == T_constructor) { - if (_parameters.size() > (size_t)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() > (size_t)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 ((int)_parameters.size() == first_param) { diff --git a/dtool/src/interrogate/interfaceMaker.cxx b/dtool/src/interrogate/interfaceMaker.cxx index 527f983f8a..5c2e589e3c 100644 --- a/dtool/src/interrogate/interfaceMaker.cxx +++ b/dtool/src/interrogate/interfaceMaker.cxx @@ -616,7 +616,7 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) { // If *any* of the variants of this function has a "this" pointer, // the entire set of functions is deemed to have a "this" pointer. - if (remap->_has_this) { + if (remap->_has_this || (remap->_flags & FunctionRemap::F_explicit_self) != 0) { func->_has_this = true; } diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 4042136bab..7b9aa8487a 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -478,6 +478,11 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap, def._wrapper_type = WT_mapping_setitem; return true; } + if (remap->_flags & FunctionRemap::F_size) { + def._answer_location = "mp_length"; + def._wrapper_type = WT_sequence_size; + return true; + } } if (obj->_protocol_types & Object::PT_iter) { @@ -707,7 +712,10 @@ write_python_instance(ostream &out, int indent_level, const string &return_expr, string class_name = itype.get_scoped_name(); - if (IsPandaTypedObject(itype._cpptype->as_struct_type())) { + // We don't handle final classes via DTool_CreatePyInstanceTyped since we + // know it can't be of a subclass type, so we don't need to do the downcast. + CPPStructType *struct_type = itype._cpptype->as_struct_type(); + if (IsPandaTypedObject(struct_type) && !struct_type->is_final()) { // We can't let DTool_CreatePyInstanceTyped do the NULL check since we // will be grabbing the type index (which would obviously crash when // called on a NULL pointer), so we do it here. @@ -2100,10 +2108,10 @@ write_module_class(ostream &out, Object *obj) { for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) { FunctionRemap *remap = (*ri); - if (remap->_flags & FunctionRemap::F_setitem_int) { + if (remap->_flags & FunctionRemap::F_setitem) { setitem_remaps.insert(remap); - } else if (remap->_flags & FunctionRemap::F_delitem_int) { + } else if (remap->_flags & FunctionRemap::F_delitem) { delitem_remaps.insert(remap); } } @@ -2137,14 +2145,21 @@ write_module_class(ostream &out, Object *obj) { out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n"; out << "//////////////////\n"; out << "static int " << def._wrapper_name << "(PyObject *self) {\n"; - out << " " << cClassName << " *local_this = NULL;\n"; - out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; - out << " return -1;\n"; - out << " }\n\n"; + // Find the remap. There should be only one. FunctionRemap *remap = *def._remaps.begin(); + const char *container = ""; + + if (remap->_has_this) { + out << " " << cClassName << " *local_this = NULL;\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; + out << " return -1;\n"; + out << " }\n\n"; + container = "local_this"; + } + vector_string params; - out << " return (int) " << remap->call_function(out, 4, false, "local_this", params) << ";\n"; + out << " return (int) " << remap->call_function(out, 4, false, container, params) << ";\n"; out << "}\n\n"; } break; @@ -2374,23 +2389,25 @@ write_module_class(ostream &out, Object *obj) { out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n"; out << "//////////////////\n"; out << "static int " << def._wrapper_name << "(PyObject *self, visitproc visit, void *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 << " return 0;\n"; - out << " }\n\n"; // Find the remap. There should be only one. FunctionRemap *remap = *def._remaps.begin(); + const char *container = ""; - vector_string params(1); - if (remap->_flags & FunctionRemap::F_explicit_self) { - params.push_back("self"); + if (remap->_has_this) { + out << " " << cClassName << " *local_this = NULL;\n"; + out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **) &local_this);\n"; + out << " if (local_this == NULL) {\n"; + out << " return 0;\n"; + out << " }\n\n"; + container = "local_this"; } + + vector_string params((int)remap->_has_this); params.push_back("visit"); params.push_back("arg"); - out << " return " << remap->call_function(out, 2, false, "local_this", params) << ";\n"; + out << " return " << remap->call_function(out, 2, false, container, params) << ";\n"; out << "}\n\n"; } break; @@ -2857,11 +2874,9 @@ write_module_class(ostream &out, Object *obj) { } // getattrofunc tp_getattro; - write_function_slot(out, 4, slots, "tp_getattro", - "PyObject_GenericGetAttr"); + write_function_slot(out, 4, slots, "tp_getattro"); // setattrofunc tp_setattro; - write_function_slot(out, 4, slots, "tp_setattro", - "PyObject_GenericSetAttr"); + write_function_slot(out, 4, slots, "tp_setattro"); // PyBufferProcs *tp_as_buffer; if (has_parent_class || has_local_getbuffer) { @@ -4180,7 +4195,7 @@ int get_type_sort(CPPType *type) { return 7; } else if (TypeManager::is_longlong(type)) { return 6; - } else if (TypeManager::is_integer(type)) { + } else if (TypeManager::is_integer(type) && !TypeManager::is_bool(type)) { return 5; } else if (TypeManager::is_double(type)) { return 4; @@ -4973,7 +4988,7 @@ write_function_instance(ostream &out, FunctionRemap *remap, format_specifiers += "O"; parameter_list += ", &" + param_name; } - pexpr_string = "NULL"; + pexpr_string = "nullptr"; expected_params += "NoneType"; } else if (TypeManager::is_char(type)) { @@ -5800,7 +5815,8 @@ write_function_instance(ostream &out, FunctionRemap *remap, indent_level += 2; } - if (!remap->_has_this && (remap->_flags & FunctionRemap::F_explicit_self) != 0) { + if (is_constructor && !remap->_has_this && + (remap->_flags & FunctionRemap::F_explicit_self) != 0) { // If we'll be passing "self" to the constructor, we need to pre- // initialize it here. Unfortunately, we can't pre-load the "this" // pointer, but the constructor itself can do this. diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index a36b370942..349e9c46ad 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -1850,12 +1850,24 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP continue; } + const CPPParameterList::Parameters ¶ms = ftype->_parameters->_parameters; + + size_t expected_num_args = (size_t)is_seq; + size_t index_arg = 0; + + if (!params.empty() && params[0]->get_simple_name() == "self" && + TypeManager::is_pointer_to_PyObject(params[0]->_type)) { + // Taking a PyObject *self argument. + expected_num_args += 1; + index_arg += 1; + } + // The getter must either take no arguments, or all defaults. - if (ftype->_parameters->_parameters.size() == (size_t)is_seq || - (ftype->_parameters->_parameters.size() > (size_t)is_seq && - ftype->_parameters->_parameters[(size_t)is_seq]->_initializer != NULL)) { + if (params.size() == expected_num_args || + (params.size() > expected_num_args && + params[expected_num_args]->_initializer != NULL)) { // If this is a sequence getter, it must take an index argument. - if (is_seq && !TypeManager::is_integer(ftype->_parameters->_parameters[0]->_type)) { + if (is_seq && !TypeManager::is_integer(params[index_arg]->_type)) { continue; } @@ -2428,6 +2440,10 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, break; } + if (cpptype->is_final()) { + itype._flags |= InterrogateType::F_final; + } + if (cpptype->_file.is_c_file()) { // This type declaration appears in a .C file. We can only export types // defined in a .h file. diff --git a/dtool/src/interrogatedb/interrogateType.I b/dtool/src/interrogatedb/interrogateType.I index b5527bbd23..840fdb18f3 100644 --- a/dtool/src/interrogatedb/interrogateType.I +++ b/dtool/src/interrogatedb/interrogateType.I @@ -289,6 +289,14 @@ is_union() const { return (_flags & F_union) != 0; } +/** + * + */ +INLINE bool InterrogateType:: +is_final() const { + return (_flags & F_final) != 0; +} + /** * */ diff --git a/dtool/src/interrogatedb/interrogateType.h b/dtool/src/interrogatedb/interrogateType.h index 151e10b5f5..176efb4fb9 100644 --- a/dtool/src/interrogatedb/interrogateType.h +++ b/dtool/src/interrogatedb/interrogateType.h @@ -75,6 +75,7 @@ public: INLINE bool is_struct() const; INLINE bool is_class() const; INLINE bool is_union() const; + INLINE bool is_final() const; INLINE bool is_fully_defined() const; INLINE bool is_unpublished() const; @@ -139,6 +140,7 @@ private: F_typedef = 0x200000, F_array = 0x400000, F_scoped_enum = 0x800000, + F_final =0x1000000, }; public: diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index 7b920979ef..1156b6dfef 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -17,6 +17,8 @@ #ifdef HAVE_PYTHON +PyTupleObject Dtool_EmptyTuple; + PyMemberDef standard_type_members[] = { {(char *)"this", (sizeof(void*) == sizeof(int)) ? T_UINT : T_ULONGLONG, offsetof(Dtool_PyInstDef, _ptr_to_object), READONLY, (char *)"C++ 'this' pointer, if any"}, {(char *)"this_ownership", T_BOOL, offsetof(Dtool_PyInstDef, _memory_rules), READONLY, (char *)"C++ 'this' ownership rules"}, @@ -65,15 +67,6 @@ size_t PyLongOrInt_AsSize_t(PyObject *vv) { } #endif -#if PY_VERSION_HEX < 0x03060000 -INLINE static PyObject *_PyObject_CallNoArg(PyObject *func) { - PyObject *args = PyTuple_New(0); - PyObject *result = PyObject_Call(func, args, NULL); - Py_DECREF(args); - return result; -} -#endif - /** * Given a valid (non-NULL) PyObject, does a simple check to see if it might * be an instance of a Panda type. It does this using a signature that is @@ -625,6 +618,9 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) { return Dtool_Raise_TypeError("PyType_Ready(Dtool_StaticProperty_Type)"); } + // Initialize the "empty tuple". + (void)PyObject_INIT_VAR((PyObject *)&Dtool_EmptyTuple, &PyTuple_Type, 0); + // Initialize the base class of everything. Dtool_PyModuleClassInit_DTOOL_SUPER_BASE(NULL); } diff --git a/dtool/src/interrogatedb/py_panda.h b/dtool/src/interrogatedb/py_panda.h index 05a787de9f..9df9fad4af 100644 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -138,6 +138,12 @@ typedef long Py_hash_t; #define FMTCHAR_BYTES "s" #endif +extern EXPCL_INTERROGATEDB PyTupleObject Dtool_EmptyTuple; + +#ifndef _PyObject_CallNoArg +#define _PyObject_CallNoArg(func) PyObject_Call((func), (PyObject *)&Dtool_EmptyTuple, NULL) +#endif + using namespace std; // this is tempory .. untill this is glued better into the panda build system