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
This commit is contained in:
rdb 2017-09-19 18:46:21 +02:00
parent 4018efc50d
commit 7ff8b62fb8
8 changed files with 90 additions and 48 deletions

View File

@ -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) {

View File

@ -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;
}

View File

@ -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.

View File

@ -1850,12 +1850,24 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
continue;
}
const CPPParameterList::Parameters &params = 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.

View File

@ -289,6 +289,14 @@ is_union() const {
return (_flags & F_union) != 0;
}
/**
*
*/
INLINE bool InterrogateType::
is_final() const {
return (_flags & F_final) != 0;
}
/**
*
*/

View File

@ -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:

View File

@ -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);
}

View File

@ -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