mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 17:35:34 -04:00
fix problem with infinite recursion on coercion within constructors
This commit is contained in:
parent
9946d7803a
commit
5178fb9523
@ -856,12 +856,37 @@ write_class_details(ostream &out, Object * obj) {
|
|||||||
out << " return -1;\n" ;
|
out << " return -1;\n" ;
|
||||||
out << "}\n";
|
out << "}\n";
|
||||||
|
|
||||||
|
out
|
||||||
|
<< "int Dtool_InitNoCoerce_" << ClassName << "(PyObject *self, PyObject *args, PyObject *kwds) {\n"
|
||||||
|
<< " return Dtool_Init_" << ClassName << "(self, args, kwds);\n"
|
||||||
|
<< "}\n\n";
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
bool coercion_attempted = false;
|
||||||
for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) {
|
for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) {
|
||||||
Function *func = (*fi);
|
Function *func = (*fi);
|
||||||
std::string fname = "int Dtool_Init_"+ClassName+"(PyObject *self, PyObject *args, PyObject *kwds)";
|
std::string fname = "int Dtool_Init_"+ClassName+"(PyObject *self, PyObject *args, PyObject *kwds)";
|
||||||
|
|
||||||
write_function_for_name(out, obj, func,fname,"",ClassName);
|
write_function_for_name(out, obj, func,fname,"",ClassName, true, coercion_attempted);
|
||||||
|
}
|
||||||
|
if (coercion_attempted) {
|
||||||
|
// If a coercion attempt was written into the above constructor,
|
||||||
|
// then write a secondary constructor, that won't attempt any
|
||||||
|
// 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)";
|
||||||
|
|
||||||
|
write_function_for_name(out, obj, func,fname,"",ClassName, false, coercion_attempted);
|
||||||
|
}
|
||||||
|
} 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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1657,7 +1682,8 @@ write_module_class(ostream &out, Object *obj) {
|
|||||||
}
|
}
|
||||||
ostringstream forward_decl;
|
ostringstream forward_decl;
|
||||||
string expected_params;
|
string expected_params;
|
||||||
write_function_forset(out, obj, func, remaps, expected_params, 2, forward_decl, false, args_cleanup);
|
bool coercion_attempted = false;
|
||||||
|
write_function_forset(out, obj, func, remaps, expected_params, 2, forward_decl, false, true, coercion_attempted, args_cleanup);
|
||||||
out << " if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_TypeError)) {\n";
|
out << " if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_TypeError)) {\n";
|
||||||
out << " PyErr_Clear();\n";
|
out << " PyErr_Clear();\n";
|
||||||
out << " }\n";
|
out << " }\n";
|
||||||
@ -1966,7 +1992,8 @@ void InterfaceMakerPythonNative::
|
|||||||
write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker::Function *func, const std::string &PreProcess) {
|
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 = "static PyObject *" + func->_name + "(PyObject *self, PyObject *args, PyObject *kwds)";
|
||||||
|
|
||||||
write_function_for_name(out, obj, func, fname, PreProcess, "");
|
bool coercion_attempted = false;
|
||||||
|
write_function_for_name(out, obj, func, fname, PreProcess, "", true, coercion_attempted);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -1978,7 +2005,7 @@ void InterfaceMakerPythonNative::
|
|||||||
write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMaker::Function *func,
|
write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMaker::Function *func,
|
||||||
const std::string &function_name,
|
const std::string &function_name,
|
||||||
const std::string &PreProcess,
|
const std::string &PreProcess,
|
||||||
const std::string &ClassName) {
|
const std::string &ClassName, bool coercion_allowed, bool &coercion_attempted) {
|
||||||
ostringstream forward_decl;
|
ostringstream forward_decl;
|
||||||
ostringstream out;
|
ostringstream out;
|
||||||
|
|
||||||
@ -2042,7 +2069,7 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak
|
|||||||
for (mii = MapSets.begin(); mii != MapSets.end(); mii ++) {
|
for (mii = MapSets.begin(); mii != MapSets.end(); mii ++) {
|
||||||
indent(out, 2) << "case " << mii->first << ": {\n";
|
indent(out, 2) << "case " << mii->first << ": {\n";
|
||||||
|
|
||||||
write_function_forset(out, obj, func, mii->second, expected_params, 4, forward_decl, is_inplace, "");
|
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) {
|
if ((*mii->second.begin())->_type == FunctionRemap::T_constructor) {
|
||||||
constructor = true;
|
constructor = true;
|
||||||
}
|
}
|
||||||
@ -2107,7 +2134,7 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak
|
|||||||
} else {
|
} else {
|
||||||
string expected_params = "";
|
string expected_params = "";
|
||||||
for (mii = MapSets.begin(); mii != MapSets.end(); mii++) {
|
for (mii = MapSets.begin(); mii != MapSets.end(); mii++) {
|
||||||
write_function_forset(out, obj, func, mii->second, expected_params, 2, forward_decl, is_inplace, "");
|
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) {
|
if ((*mii->second.begin())->_type == FunctionRemap::T_constructor) {
|
||||||
constructor = true;
|
constructor = true;
|
||||||
}
|
}
|
||||||
@ -2256,10 +2283,13 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj,
|
|||||||
std::set<FunctionRemap *> &remapsin,
|
std::set<FunctionRemap *> &remapsin,
|
||||||
string &expected_params, int indent_level,
|
string &expected_params, int indent_level,
|
||||||
ostream &forward_decl, bool is_inplace,
|
ostream &forward_decl, bool is_inplace,
|
||||||
|
bool coercion_allowed,
|
||||||
|
bool &coercion_attempted,
|
||||||
const string &args_cleanup) {
|
const string &args_cleanup) {
|
||||||
// Do we accept any parameters that are class objects? If so, we
|
// Do we accept any parameters that are class objects? If so, we
|
||||||
// might need to check for parameter coercion.
|
// might need to check for parameter coercion.
|
||||||
bool coercion_possible = false;
|
bool coercion_possible = false;
|
||||||
|
if (coercion_allowed) {
|
||||||
std::set<FunctionRemap *>::const_iterator sii;
|
std::set<FunctionRemap *>::const_iterator sii;
|
||||||
for (sii = remapsin.begin(); sii != remapsin.end() && !coercion_possible; ++sii) {
|
for (sii = remapsin.begin(); sii != remapsin.end() && !coercion_possible; ++sii) {
|
||||||
FunctionRemap *remap = (*sii);
|
FunctionRemap *remap = (*sii);
|
||||||
@ -2285,6 +2315,7 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (coercion_possible) {
|
if (coercion_possible) {
|
||||||
// These objects keep track of whether we have attempted automatic
|
// These objects keep track of whether we have attempted automatic
|
||||||
@ -2326,7 +2357,7 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj,
|
|||||||
indent(out, indent_level) << "// -2 ";
|
indent(out, indent_level) << "// -2 ";
|
||||||
remap->write_orig_prototype(out, 0); out << "\n";
|
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, args_cleanup);
|
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 + 2) << "PyErr_Clear(); \n";
|
||||||
indent(out, indent_level) << "}\n\n";
|
indent(out, indent_level) << "}\n\n";
|
||||||
@ -2353,7 +2384,7 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj,
|
|||||||
<< "// 1-" ;
|
<< "// 1-" ;
|
||||||
remap->write_orig_prototype(out, 0);
|
remap->write_orig_prototype(out, 0);
|
||||||
out << "\n" ;
|
out << "\n" ;
|
||||||
write_function_instance(out, obj, func, remap, expected_params, indent_level + 2, true, forward_decl, func->_name, is_inplace, coercion_possible, args_cleanup);
|
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) {
|
if (remap->_has_this && !remap->_const_method) {
|
||||||
indent(out, indent_level)
|
indent(out, indent_level)
|
||||||
@ -2435,7 +2466,8 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj,
|
|||||||
int indent_level, bool errors_fatal,
|
int indent_level, bool errors_fatal,
|
||||||
ostream &ForwardDeclrs,
|
ostream &ForwardDeclrs,
|
||||||
const std::string &functionnamestr, bool is_inplace,
|
const std::string &functionnamestr, bool is_inplace,
|
||||||
bool coercion_possible, const string &args_cleanup) {
|
bool coercion_possible, bool &coercion_attempted,
|
||||||
|
const string &args_cleanup) {
|
||||||
string format_specifiers;
|
string format_specifiers;
|
||||||
std::string keyword_list;
|
std::string keyword_list;
|
||||||
string parameter_list;
|
string parameter_list;
|
||||||
@ -2704,6 +2736,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj,
|
|||||||
// We never attempt to coerce a copy constructor parameter.
|
// We never attempt to coerce a copy constructor parameter.
|
||||||
// That would lead to infinite recursion.
|
// That would lead to infinite recursion.
|
||||||
str << ", coerced_ptr, report_errors";
|
str << ", coerced_ptr, report_errors";
|
||||||
|
coercion_attempted = true;
|
||||||
} else {
|
} else {
|
||||||
str << ", NULL, true";
|
str << ", NULL, true";
|
||||||
}
|
}
|
||||||
|
@ -86,18 +86,21 @@ private:
|
|||||||
|
|
||||||
void write_prototype_for_name(ostream &out, Function *func, const std::string &name);
|
void write_prototype_for_name(ostream &out, Function *func, const std::string &name);
|
||||||
void write_prototype_for(ostream &out, Function *func);
|
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);
|
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_for_top(ostream &out, Object *obj, Function *func, const std::string &PreProcess);
|
||||||
void write_function_instance(ostream &out, Object *obj, Function *func,
|
void write_function_instance(ostream &out, Object *obj, Function *func,
|
||||||
FunctionRemap *remap, string &expected_params,
|
FunctionRemap *remap, string &expected_params,
|
||||||
int indent_level, bool errors_fatal,
|
int indent_level, bool errors_fatal,
|
||||||
ostream &forwarddecl, const std::string &functionnamestr,
|
ostream &forwarddecl, const std::string &functionnamestr,
|
||||||
bool is_inplace, bool coercion_possible,
|
bool is_inplace, bool coercion_allowed,
|
||||||
|
bool &coercion_attempted,
|
||||||
const string &args_cleanup);
|
const string &args_cleanup);
|
||||||
|
|
||||||
void write_function_forset(ostream &out, Object *obj, Function *func,
|
void write_function_forset(ostream &out, Object *obj, Function *func,
|
||||||
std::set<FunctionRemap*> &remaps, string &expected_params,
|
std::set<FunctionRemap*> &remaps, string &expected_params,
|
||||||
int indent_level, ostream &forwarddecl, bool inplace,
|
int indent_level, ostream &forwarddecl, bool inplace,
|
||||||
|
bool coercion_allowed, bool &coercion_attempted,
|
||||||
const string &args_cleanup);
|
const string &args_cleanup);
|
||||||
|
|
||||||
void pack_return_value(ostream &out, int indent_level,
|
void pack_return_value(ostream &out, int indent_level,
|
||||||
|
@ -83,4 +83,9 @@ int Dtool_Init_DTOOL_SUPER_BASE(PyObject *self, PyObject *args, PyObject *kwds)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Dtool_InitNoCoerce_DTOOL_SUPER_BASE(PyObject *self, PyObject *args, PyObject *kwds) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "cannot init super base");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // HAVE_PYTHON
|
#endif // HAVE_PYTHON
|
||||||
|
@ -71,9 +71,25 @@ attempt_coercion(PyObject *self, Dtool_PyTypedObject *classdef,
|
|||||||
if (coerced != NULL) {
|
if (coerced != NULL) {
|
||||||
// Attempt coercion: try to create a temporary instance of the
|
// Attempt coercion: try to create a temporary instance of the
|
||||||
// required class using the supplied parameter.
|
// required class using the supplied parameter.
|
||||||
PyObject *obj = PyObject_Call((PyObject *)classdef, self, NULL);
|
// Because we want to use the special InitNoCoerce constructor
|
||||||
|
// here instead of the regular constructor (we don't want to risk
|
||||||
|
// recursive coercion on the nested type we're creating), we have
|
||||||
|
// to call the constructor with a few more steps.
|
||||||
|
PyObject *obj = NULL;
|
||||||
|
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 (obj == NULL) {
|
if (obj == NULL) {
|
||||||
// That didn't work; try to call a static "make" method instead.
|
// That didn't work; try to call a static "make" method instead.
|
||||||
|
// Presently, we don't bother filtering this for coercion,
|
||||||
|
// because none of our classes suffer from a recursion danger
|
||||||
|
// here. Maybe one day we will need to also construct a
|
||||||
|
// makeNoCoerce wrapper?
|
||||||
PyObject *make = PyObject_GetAttrString((PyObject *)classdef, "make");
|
PyObject *make = PyObject_GetAttrString((PyObject *)classdef, "make");
|
||||||
if (make != NULL) {
|
if (make != NULL) {
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
|
@ -139,6 +139,8 @@ typedef void * ( * ConvertFunctionType )(PyObject *,Dtool_PyTypedObject * );
|
|||||||
typedef void * ( * ConvertFunctionType1 )(void *, Dtool_PyTypedObject *);
|
typedef void * ( * ConvertFunctionType1 )(void *, Dtool_PyTypedObject *);
|
||||||
typedef void ( *FreeFunction )(PyObject *);
|
typedef void ( *FreeFunction )(PyObject *);
|
||||||
typedef void ( *PyModuleClassInit)(PyObject *module);
|
typedef void ( *PyModuleClassInit)(PyObject *module);
|
||||||
|
typedef int ( *InitNoCoerce)(PyObject *self, PyObject *args, PyObject *kwds);
|
||||||
|
|
||||||
//inline Dtool_PyTypedObject * Dtool_RuntimeTypeDtoolType(int type);
|
//inline Dtool_PyTypedObject * Dtool_RuntimeTypeDtoolType(int type);
|
||||||
inline void Dtool_Deallocate_General(PyObject * self);
|
inline void Dtool_Deallocate_General(PyObject * self);
|
||||||
//inline int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2);
|
//inline int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2);
|
||||||
@ -186,7 +188,8 @@ struct Dtool_PyTypedObject {
|
|||||||
ConvertFunctionType _Dtool_UpcastInterface; // The Upcast Function By Slot
|
ConvertFunctionType _Dtool_UpcastInterface; // The Upcast Function By Slot
|
||||||
ConvertFunctionType1 _Dtool_DowncastInterface; // The Downcast Function By Slot
|
ConvertFunctionType1 _Dtool_DowncastInterface; // The Downcast Function By Slot
|
||||||
FreeFunction _Dtool_FreeInstance;
|
FreeFunction _Dtool_FreeInstance;
|
||||||
PyModuleClassInit _Dtool_ClassInit; // The init function pointer
|
PyModuleClassInit _Dtool_ClassInit; // The module init function pointer
|
||||||
|
InitNoCoerce _Dtool_InitNoCoerce; // A variant of the constructor that does not attempt to perform coercion of its arguments.
|
||||||
|
|
||||||
// some convenience functions..
|
// some convenience functions..
|
||||||
inline PyTypeObject &As_PyTypeObject() { return _PyType; };
|
inline PyTypeObject &As_PyTypeObject() { return _PyType; };
|
||||||
@ -245,7 +248,8 @@ struct Dtool_PyTypedObject {
|
|||||||
Dtool_UpcastInterface_##CLASS_NAME, \
|
Dtool_UpcastInterface_##CLASS_NAME, \
|
||||||
Dtool_DowncastInterface_##CLASS_NAME, \
|
Dtool_DowncastInterface_##CLASS_NAME, \
|
||||||
Dtool_FreeInstance_##CLASS_NAME, \
|
Dtool_FreeInstance_##CLASS_NAME, \
|
||||||
Dtool_PyModuleClassInit_##CLASS_NAME \
|
Dtool_PyModuleClassInit_##CLASS_NAME, \
|
||||||
|
Dtool_InitNoCoerce_##CLASS_NAME \
|
||||||
};
|
};
|
||||||
|
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
@ -482,6 +486,7 @@ EXPCL_DTOOLCONFIG PyObject *DTool_CreatePyInstance(void *local_this, Dtool_PyTyp
|
|||||||
extern EXPORT_THIS Dtool_PyTypedObject Dtool_##CLASS_NAME;\
|
extern EXPORT_THIS Dtool_PyTypedObject Dtool_##CLASS_NAME;\
|
||||||
extern struct PyMethodDef Dtool_Methods_##CLASS_NAME[];\
|
extern struct PyMethodDef Dtool_Methods_##CLASS_NAME[];\
|
||||||
int Dtool_Init_##CLASS_NAME(PyObject *self, PyObject *args, PyObject *kwds);\
|
int Dtool_Init_##CLASS_NAME(PyObject *self, PyObject *args, PyObject *kwds);\
|
||||||
|
int Dtool_InitNoCoerce_##CLASS_NAME(PyObject *self, PyObject *args, PyObject *kwds);\
|
||||||
PyObject * Dtool_new_##CLASS_NAME(PyTypeObject *type, PyObject *args, PyObject *kwds);\
|
PyObject * Dtool_new_##CLASS_NAME(PyTypeObject *type, PyObject *args, PyObject *kwds);\
|
||||||
void * Dtool_UpcastInterface_##CLASS_NAME(PyObject *self, Dtool_PyTypedObject *requested_type);\
|
void * Dtool_UpcastInterface_##CLASS_NAME(PyObject *self, Dtool_PyTypedObject *requested_type);\
|
||||||
void * Dtool_DowncastInterface_##CLASS_NAME(void *self, Dtool_PyTypedObject *requested_type);\
|
void * Dtool_DowncastInterface_##CLASS_NAME(void *self, Dtool_PyTypedObject *requested_type);\
|
||||||
|
Loading…
x
Reference in New Issue
Block a user