diff options
Diffstat (limited to 'source/gameengine/Expressions/PyObjectPlus.cpp')
-rw-r--r-- | source/gameengine/Expressions/PyObjectPlus.cpp | 744 |
1 files changed, 464 insertions, 280 deletions
diff --git a/source/gameengine/Expressions/PyObjectPlus.cpp b/source/gameengine/Expressions/PyObjectPlus.cpp index 2d4cc612aef..7adbf5ac651 100644 --- a/source/gameengine/Expressions/PyObjectPlus.cpp +++ b/source/gameengine/Expressions/PyObjectPlus.cpp @@ -51,19 +51,14 @@ #include "PyObjectPlus.h" #include "STR_String.h" #include "MT_Vector3.h" +#include "MEM_guardedalloc.h" /*------------------------------ * PyObjectPlus Type -- Every class, even the abstract one should have a Type ------------------------------*/ PyTypeObject PyObjectPlus::Type = { -#if (PY_VERSION_HEX >= 0x02060000) PyVarObject_HEAD_INIT(NULL, 0) -#else - /* python 2.5 and below */ - PyObject_HEAD_INIT( NULL ) /* required py macro */ - 0, /*ob_size*/ -#endif "PyObjectPlus", /*tp_name*/ sizeof(PyObjectPlus_Proxy), /*tp_basicsize*/ 0, /*tp_itemsize*/ @@ -74,168 +69,177 @@ PyTypeObject PyObjectPlus::Type = { 0, 0, py_base_repr, - 0,0,0,0,0,0, - py_base_getattro, - py_base_setattro, 0,0,0,0,0,0,0,0,0, - Methods + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + 0,0,0,0,0,0,0, + Methods, + 0, + 0, + NULL // no subtype }; PyObjectPlus::~PyObjectPlus() { if(m_proxy) { - Py_DECREF(m_proxy); /* Remove own reference, python may still have 1 */ BGE_PROXY_REF(m_proxy)= NULL; + Py_DECREF(m_proxy); /* Remove own reference, python may still have 1 */ } // assert(ob_refcnt==0); } -void PyObjectPlus::py_base_dealloc(PyObject *self) // python wrapper + +PyObject *PyObjectPlus::py_base_repr(PyObject *self) // This should be the entry in Type. { PyObjectPlus *self_plus= BGE_PROXY_REF(self); - if(self_plus) { - if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it */ - self_plus->m_proxy = NULL; /* Need this to stop ~PyObjectPlus from decrefing m_proxy otherwise its decref'd twice and py-debug crashes */ - delete self_plus; + if(self_plus==NULL) { + PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG); + return NULL; + } + return self_plus->py_repr(); +} + + +PyObject * PyObjectPlus::py_base_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyTypeObject *base_type; + PyObjectPlus_Proxy *base = NULL; + + if (!PyArg_ParseTuple(args, "O:Base PyObjectPlus", &base)) + return NULL; + + /* the 'base' PyObject may be subclassed (multiple times even) + * we need to find the first C++ defined class to check 'type' + * is a subclass of the base arguments type. + * + * This way we can share one tp_new function for every PyObjectPlus + * + * eg. + * + * # CustomOb is called 'type' in this C code + * class CustomOb(GameTypes.KX_GameObject): + * pass + * + * # this calls py_base_new(...), the type of 'CustomOb' is checked to be a subclass of the 'cont.owner' type + * ob = CustomOb(cont.owner) + * + * */ + base_type= Py_TYPE(base); + while(base_type && !BGE_PROXY_CHECK_TYPE(base_type)) + base_type= base_type->tp_base; + + if(base_type==NULL || !BGE_PROXY_CHECK_TYPE(base_type)) { + PyErr_SetString(PyExc_TypeError, "can't subclass from a blender game type because the argument given is not a game class or subclass"); + return NULL; + } + + /* use base_type rather then Py_TYPE(base) because we could alredy be subtyped */ + if(!PyType_IsSubtype(type, base_type)) { + PyErr_Format(PyExc_TypeError, "can't subclass blender game type <%s> from <%s> because it is not a subclass", base_type->tp_name, type->tp_name); + return NULL; + } + + /* invalidate the existing base and return a new subclassed one, + * this is a bit dodgy in that it also attaches its self to the existing object + * which is not really 'correct' python OO but for our use its OK. */ + + PyObjectPlus_Proxy *ret = (PyObjectPlus_Proxy *) type->tp_alloc(type, 0); /* starts with 1 ref, used for the return ref' */ + ret->ref= base->ref; + ret->ptr= base->ptr; + ret->py_owns= base->py_owns; + ret->py_ref = base->py_ref; + + if (ret->py_ref) { + base->ref= NULL; /* invalidate! disallow further access */ + base->ptr = NULL; + if (ret->ref) + ret->ref->m_proxy= NULL; + /* 'base' may be free'd after this func finished but not necessarily + * there is no reference to the BGE data now so it will throw an error on access */ + Py_DECREF(base); + if (ret->ref) { + ret->ref->m_proxy= (PyObject *)ret; /* no need to add a ref because one is added when creating. */ + Py_INCREF(ret); /* we return a new ref but m_proxy holds a ref so we need to add one */ } - - BGE_PROXY_REF(self)= NULL; // not really needed + } else { + // generic structures don't hold a reference to this proxy, so don't increment ref count + if (ret->py_owns) + // but if the proxy owns the structure, there can be only one owner + base->ptr= NULL; } + + return (PyObject *)ret; +} + +void PyObjectPlus::py_base_dealloc(PyObject *self) // python wrapper +{ + if (BGE_PROXY_PYREF(self)) { + PyObjectPlus *self_plus= BGE_PROXY_REF(self); + if(self_plus) { + if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it */ + self_plus->m_proxy = NULL; /* Need this to stop ~PyObjectPlus from decrefing m_proxy otherwise its decref'd twice and py-debug crashes */ + delete self_plus; + } + BGE_PROXY_REF(self)= NULL; // not really needed + } + // the generic pointer is not deleted directly, only through self_plus + BGE_PROXY_PTR(self)= NULL; // not really needed + } else { + void *ptr= BGE_PROXY_PTR(self); + if(ptr) { + if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it */ + // generic structure owned by python MUST be created though MEM_alloc + MEM_freeN(ptr); + } + BGE_PROXY_PTR(self)= NULL; // not really needed + } + } +#if 0 + /* is ok normally but not for subtyping, use tp_free instead. */ PyObject_DEL( self ); +#else + Py_TYPE(self)->tp_free(self); +#endif }; -PyObjectPlus::PyObjectPlus(PyTypeObject *T) : SG_QList() // constructor +PyObjectPlus::PyObjectPlus() : SG_QList() // constructor { - MT_assert(T != NULL); m_proxy= NULL; }; - + /*------------------------------ * PyObjectPlus Methods -- Every class, even the abstract one should have a Methods ------------------------------*/ PyMethodDef PyObjectPlus::Methods[] = { - {"isA", (PyCFunction) sPyisA, METH_O}, {NULL, NULL} /* Sentinel */ }; +#define attr_invalid (&(PyObjectPlus::Attributes[0])) PyAttributeDef PyObjectPlus::Attributes[] = { KX_PYATTRIBUTE_RO_FUNCTION("invalid", PyObjectPlus, pyattr_get_invalid), {NULL} //Sentinel }; -PyObject* PyObjectPlus::pyattr_get_invalid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) -{ - Py_RETURN_FALSE; -} - -/*------------------------------ - * PyObjectPlus Parents -- Every class, even the abstract one should have parents -------------------------------*/ -PyParentObject PyObjectPlus::Parents[] = {&PyObjectPlus::Type, NULL}; - -/*------------------------------ - * PyObjectPlus attributes -- attributes -------------------------------*/ -/* This should be the entry in Type since it takes the C++ class from PyObjectPlus_Proxy */ -PyObject *PyObjectPlus::py_base_getattro(PyObject * self, PyObject *attr) +PyObject* PyObjectPlus::pyattr_get_invalid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { - PyObjectPlus *self_plus= BGE_PROXY_REF(self); - if(self_plus==NULL) { - if(!strcmp("invalid", PyString_AsString(attr))) { - Py_RETURN_TRUE; - } - PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG); - return NULL; - } - - PyObject *ret= self_plus->py_getattro(attr); - - /* Attribute not found, was this a __dict__ lookup?, otherwise set an error if none is set */ - if(ret==NULL) { - char *attr_str= PyString_AsString(attr); - - if (strcmp(attr_str, "__dict__")==0) - { - /* the error string will probably not - * be set but just incase clear it */ - PyErr_Clear(); - ret= self_plus->py_getattro_dict(); - } - else if (!PyErr_Occurred()) { - /* We looked for an attribute but it wasnt found - * since py_getattro didnt set the error, set it here */ - PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '%s'", self->ob_type->tp_name, attr_str); - } - } - return ret; + return PyBool_FromLong(self_v ? 1:0); } -/* This should be the entry in Type since it takes the C++ class from PyObjectPlus_Proxy */ -int PyObjectPlus::py_base_setattro(PyObject *self, PyObject *attr, PyObject *value) +/* note, this is called as a python 'getset, where the PyAttributeDef is the closure */ +PyObject *PyObjectPlus::py_get_attrdef(PyObject *self_py, const PyAttributeDef *attrdef) { - PyObjectPlus *self_plus= BGE_PROXY_REF(self); - if(self_plus==NULL) { - PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG); - return -1; - } - - if (value==NULL) - return self_plus->py_delattro(attr); - - return self_plus->py_setattro(attr, value); -} + PyObjectPlus *ref= (BGE_PROXY_REF(self_py)); + char* ptr = (attrdef->m_usePtr) ? (char*)BGE_PROXY_PTR(self_py) : (char*)ref; + if(ptr == NULL || (BGE_PROXY_PYREF(self_py) && (ref==NULL || !ref->py_is_valid()))) { + if(attrdef == attr_invalid) + Py_RETURN_TRUE; // dont bother running the function -PyObject *PyObjectPlus::py_base_repr(PyObject *self) // This should be the entry in Type. -{ - - PyObjectPlus *self_plus= BGE_PROXY_REF(self); - if(self_plus==NULL) { PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG); return NULL; } - - return self_plus->py_repr(); -} - -PyObject *PyObjectPlus::py_getattro(PyObject* attr) -{ - PyObject *descr = PyDict_GetItem(Type.tp_dict, attr); \ - if (descr == NULL) { - return NULL; /* py_base_getattro sets the error, this way we can avoid setting the error at many levels */ - } else { - /* Copied from py_getattro_up */ - if (PyCObject_Check(descr)) { - return py_get_attrdef((void *)this, (const PyAttributeDef*)PyCObject_AsVoidPtr(descr)); - } else if (descr->ob_type->tp_descr_get) { - return PyCFunction_New(((PyMethodDescrObject *)descr)->d_method, this->m_proxy); - } else { - return NULL; - } - /* end py_getattro_up copy */ - } -} - -PyObject* PyObjectPlus::py_getattro_dict() { - return py_getattr_dict(NULL, Type.tp_dict); -} - -int PyObjectPlus::py_delattro(PyObject* attr) -{ - PyErr_SetString(PyExc_AttributeError, "attribute cant be deleted"); - return 1; -} -int PyObjectPlus::py_setattro(PyObject *attr, PyObject* value) -{ - PyErr_SetString(PyExc_AttributeError, "attribute cant be set"); - return PY_SET_ATTR_MISSING; -} - -PyObject *PyObjectPlus::py_get_attrdef(void *self, const PyAttributeDef *attrdef) -{ if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY) { // fake attribute, ignore @@ -246,9 +250,9 @@ PyObject *PyObjectPlus::py_get_attrdef(void *self, const PyAttributeDef *attrdef // the attribute has no field correspondance, handover processing to function. if (attrdef->m_getFunction == NULL) return NULL; - return (*attrdef->m_getFunction)(self, attrdef); + return (*attrdef->m_getFunction)(ref, attrdef); } - char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset; + ptr += attrdef->m_offset; if (attrdef->m_length > 1) { PyObject* resultlist = PyList_New(attrdef->m_length); @@ -259,14 +263,14 @@ PyObject *PyObjectPlus::py_get_attrdef(void *self, const PyAttributeDef *attrdef { bool *val = reinterpret_cast<bool*>(ptr); ptr += sizeof(bool); - PyList_SET_ITEM(resultlist,i,PyInt_FromLong(*val)); + PyList_SET_ITEM(resultlist,i,PyLong_FromSsize_t(*val)); break; } case KX_PYATTRIBUTE_TYPE_SHORT: { short int *val = reinterpret_cast<short int*>(ptr); ptr += sizeof(short int); - PyList_SET_ITEM(resultlist,i,PyInt_FromLong(*val)); + PyList_SET_ITEM(resultlist,i,PyLong_FromSsize_t(*val)); break; } case KX_PYATTRIBUTE_TYPE_ENUM: @@ -281,7 +285,7 @@ PyObject *PyObjectPlus::py_get_attrdef(void *self, const PyAttributeDef *attrdef { int *val = reinterpret_cast<int*>(ptr); ptr += sizeof(int); - PyList_SET_ITEM(resultlist,i,PyInt_FromLong(*val)); + PyList_SET_ITEM(resultlist,i,PyLong_FromSsize_t(*val)); break; } case KX_PYATTRIBUTE_TYPE_FLOAT: @@ -302,15 +306,44 @@ PyObject *PyObjectPlus::py_get_attrdef(void *self, const PyAttributeDef *attrdef else { switch (attrdef->m_type) { + case KX_PYATTRIBUTE_TYPE_FLAG: + { + bool bval; + switch (attrdef->m_size) { + case 1: + { + unsigned char *val = reinterpret_cast<unsigned char*>(ptr); + bval = (*val & attrdef->m_imin); + break; + } + case 2: + { + unsigned short *val = reinterpret_cast<unsigned short*>(ptr); + bval = (*val & attrdef->m_imin); + break; + } + case 4: + { + unsigned int *val = reinterpret_cast<unsigned int*>(ptr); + bval = (*val & attrdef->m_imin); + break; + } + default: + return NULL; + } + if (attrdef->m_imax) + bval = !bval; + return PyLong_FromSsize_t(bval); + } case KX_PYATTRIBUTE_TYPE_BOOL: { bool *val = reinterpret_cast<bool*>(ptr); - return PyInt_FromLong(*val); + return PyLong_FromSsize_t(*val); } case KX_PYATTRIBUTE_TYPE_SHORT: { short int *val = reinterpret_cast<short int*>(ptr); - return PyInt_FromLong(*val); + return PyLong_FromSsize_t(*val); } case KX_PYATTRIBUTE_TYPE_ENUM: // enum are like int, just make sure the field size is the same @@ -322,19 +355,61 @@ PyObject *PyObjectPlus::py_get_attrdef(void *self, const PyAttributeDef *attrdef case KX_PYATTRIBUTE_TYPE_INT: { int *val = reinterpret_cast<int*>(ptr); - return PyInt_FromLong(*val); + return PyLong_FromSsize_t(*val); } case KX_PYATTRIBUTE_TYPE_FLOAT: { float *val = reinterpret_cast<float*>(ptr); - return PyFloat_FromDouble(*val); + if (attrdef->m_imin == 0) { + if (attrdef->m_imax == 0) { + return PyFloat_FromDouble(*val); + } else { + // vector, verify size + if (attrdef->m_size != attrdef->m_imax*sizeof(float)) + { + return NULL; + } +#ifdef USE_MATHUTILS + return newVectorObject(val, attrdef->m_imax, Py_NEW, NULL); +#else + PyObject* resultlist = PyList_New(attrdef->m_imax); + for (unsigned int i=0; i<attrdef->m_imax; i++) + { + PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble(val[i])); + } + return resultlist; +#endif + } + } else { + // matrix case + if (attrdef->m_size != attrdef->m_imax*attrdef->m_imin*sizeof(float)) + { + return NULL; + } +#ifdef USE_MATHUTILS + return newMatrixObject(val, attrdef->m_imin, attrdef->m_imax, Py_WRAP, NULL); +#else + PyObject* rowlist = PyList_New(attrdef->m_imin); + for (unsigned int i=0; i<attrdef->m_imin; i++) + { + PyObject* collist = PyList_New(attrdef->m_imax); + for (unsigned int j=0; j<attrdef->m_imax; j++) + { + PyList_SET_ITEM(collist,j,PyFloat_FromDouble(val[j])); + } + PyList_SET_ITEM(rowlist,i,collist); + val += attrdef->m_imax; + } + return rowlist; +#endif + } } case KX_PYATTRIBUTE_TYPE_VECTOR: { MT_Vector3 *val = reinterpret_cast<MT_Vector3*>(ptr); #ifdef USE_MATHUTILS float fval[3]= {(*val)[0], (*val)[1], (*val)[2]}; - return newVectorObject(fval, 3, Py_NEW); + return newVectorObject(fval, 3, Py_NEW, NULL); #else PyObject* resultlist = PyList_New(3); for (unsigned int i=0; i<3; i++) @@ -347,7 +422,11 @@ PyObject *PyObjectPlus::py_get_attrdef(void *self, const PyAttributeDef *attrdef case KX_PYATTRIBUTE_TYPE_STRING: { STR_String *val = reinterpret_cast<STR_String*>(ptr); - return PyString_FromString(*val); + return PyUnicode_FromString(*val); + } + case KX_PYATTRIBUTE_TYPE_CHAR: + { + return PyUnicode_FromString(ptr); } default: return NULL; @@ -355,13 +434,48 @@ PyObject *PyObjectPlus::py_get_attrdef(void *self, const PyAttributeDef *attrdef } } -int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyObject *value) + +static bool py_check_attr_float(float *var, PyObject *value, const PyAttributeDef *attrdef) +{ + double val = PyFloat_AsDouble(value); + if (val == -1.0 && PyErr_Occurred()) + { + PyErr_Format(PyExc_TypeError, "expected float value for attribute \"%s\"", attrdef->m_name); + return false; + } + if (attrdef->m_clamp) + { + if (val < attrdef->m_fmin) + val = attrdef->m_fmin; + else if (val > attrdef->m_fmax) + val = attrdef->m_fmax; + } + else if (val < attrdef->m_fmin || val > attrdef->m_fmax) + { + PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name); + return false; + } + *var = (float)val; + return true; +} + +/* note, this is called as a python getset */ +int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAttributeDef *attrdef) { + PyObjectPlus *ref= (BGE_PROXY_REF(self_py)); + char* ptr = (attrdef->m_usePtr) ? (char*)BGE_PROXY_PTR(self_py) : (char*)ref; + if(ref==NULL || !ref->py_is_valid() || ptr==NULL) { + PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG); + return PY_SET_ATTR_FAIL; + } + void *undoBuffer = NULL; void *sourceBuffer = NULL; size_t bufferSize = 0; + PyObject *item = NULL; // to store object that must be dereferenced in case of error + PyObject *list = NULL; // to store object that must be dereferenced in case of error - char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset; + ptr += attrdef->m_offset; if (attrdef->m_length > 1) { if (!PySequence_Check(value)) @@ -382,7 +496,7 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb PyErr_Format(PyExc_AttributeError, "function attribute without function for attribute \"%s\", report to blender.org", attrdef->m_name); return PY_SET_ATTR_FAIL; } - return (*attrdef->m_setFunction)(self, attrdef, value); + return (*attrdef->m_setFunction)(ref, attrdef, value); case KX_PYATTRIBUTE_TYPE_BOOL: bufferSize = sizeof(bool); break; @@ -411,19 +525,16 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb } for (int i=0; i<attrdef->m_length; i++) { - PyObject *item = PySequence_GetItem(value, i); /* new ref */ - // we can decrement the reference immediately, the reference count - // is at least 1 because the item is part of an array - Py_DECREF(item); + item = PySequence_GetItem(value, i); /* new ref */ switch (attrdef->m_type) { case KX_PYATTRIBUTE_TYPE_BOOL: { bool *var = reinterpret_cast<bool*>(ptr); ptr += sizeof(bool); - if (PyInt_Check(item)) + if (PyLong_Check(item)) { - *var = (PyInt_AsLong(item) != 0); + *var = (PyLong_AsSsize_t(item) != 0); } else if (PyBool_Check(item)) { @@ -440,9 +551,9 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb { short int *var = reinterpret_cast<short int*>(ptr); ptr += sizeof(short int); - if (PyInt_Check(item)) + if (PyLong_Check(item)) { - long val = PyInt_AsLong(item); + long val = PyLong_AsSsize_t(item); if (attrdef->m_clamp) { if (val < attrdef->m_imin) @@ -476,9 +587,9 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb { int *var = reinterpret_cast<int*>(ptr); ptr += sizeof(int); - if (PyInt_Check(item)) + if (PyLong_Check(item)) { - long val = PyInt_AsLong(item); + long val = PyLong_AsSsize_t(item); if (attrdef->m_clamp) { if (val < attrdef->m_imin) @@ -530,11 +641,14 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", report to blender.org", attrdef->m_name); goto UNDO_AND_ERROR; } + // finished using item, release + Py_DECREF(item); + item = NULL; } // no error, call check function if any if (attrdef->m_checkFunction != NULL) { - if ((*attrdef->m_checkFunction)(self, attrdef) != 0) + if ((*attrdef->m_checkFunction)(ref, attrdef) != 0) { // if the checing function didnt set an error then set a generic one here so we dont set an error with no exception if (PyErr_Occurred()==0) @@ -547,6 +661,8 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb memcpy(sourceBuffer, undoBuffer, bufferSize); free(undoBuffer); } + if (item) + Py_DECREF(item); return PY_SET_ATTR_FAIL; } } @@ -563,7 +679,7 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb PyErr_Format(PyExc_AttributeError, "function attribute without function \"%s\", report to blender.org", attrdef->m_name); return PY_SET_ATTR_FAIL; } - return (*attrdef->m_setFunction)(self, attrdef, value); + return (*attrdef->m_setFunction)(ref, attrdef, value); } if (attrdef->m_checkFunction != NULL || attrdef->m_type == KX_PYATTRIBUTE_TYPE_VECTOR) { @@ -578,11 +694,19 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb bufferSize = sizeof(short); break; case KX_PYATTRIBUTE_TYPE_ENUM: + case KX_PYATTRIBUTE_TYPE_FLAG: + case KX_PYATTRIBUTE_TYPE_CHAR: + bufferSize = attrdef->m_size; + break; case KX_PYATTRIBUTE_TYPE_INT: bufferSize = sizeof(int); break; case KX_PYATTRIBUTE_TYPE_FLOAT: bufferSize = sizeof(float); + if (attrdef->m_imax) + bufferSize *= attrdef->m_imax; + if (attrdef->m_imin) + bufferSize *= attrdef->m_imin; break; case KX_PYATTRIBUTE_TYPE_STRING: sourceBuffer = reinterpret_cast<STR_String*>(ptr)->Ptr(); @@ -611,9 +735,9 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb case KX_PYATTRIBUTE_TYPE_BOOL: { bool *var = reinterpret_cast<bool*>(ptr); - if (PyInt_Check(value)) + if (PyLong_Check(value)) { - *var = (PyInt_AsLong(value) != 0); + *var = (PyLong_AsSsize_t(value) != 0); } else if (PyBool_Check(value)) { @@ -626,12 +750,55 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb } break; } + case KX_PYATTRIBUTE_TYPE_FLAG: + { + bool bval; + if (PyLong_Check(value)) + { + bval = (PyLong_AsSsize_t(value) != 0); + } + else if (PyBool_Check(value)) + { + bval = (value == Py_True); + } + else + { + PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name); + goto FREE_AND_ERROR; + } + if (attrdef->m_imax) + bval = !bval; + switch (attrdef->m_size) { + case 1: + { + unsigned char *val = reinterpret_cast<unsigned char*>(ptr); + *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0); + break; + } + case 2: + { + unsigned short *val = reinterpret_cast<unsigned short*>(ptr); + *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0); + break; + } + case 4: + { + unsigned int *val = reinterpret_cast<unsigned int*>(ptr); + *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0); + break; + } + default: + PyErr_Format(PyExc_TypeError, "internal error: unsupported flag field \"%s\"", attrdef->m_name); + goto FREE_AND_ERROR; + } + break; + } case KX_PYATTRIBUTE_TYPE_SHORT: { short int *var = reinterpret_cast<short int*>(ptr); - if (PyInt_Check(value)) + if (PyLong_Check(value)) { - long val = PyInt_AsLong(value); + long val = PyLong_AsSsize_t(value); if (attrdef->m_clamp) { if (val < attrdef->m_imin) @@ -664,9 +831,9 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb case KX_PYATTRIBUTE_TYPE_INT: { int *var = reinterpret_cast<int*>(ptr); - if (PyInt_Check(value)) + if (PyLong_Check(value)) { - long val = PyInt_AsLong(value); + long val = PyLong_AsSsize_t(value); if (attrdef->m_clamp) { if (val < attrdef->m_imin) @@ -691,25 +858,71 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb case KX_PYATTRIBUTE_TYPE_FLOAT: { float *var = reinterpret_cast<float*>(ptr); - double val = PyFloat_AsDouble(value); - if (val == -1.0 && PyErr_Occurred()) + if (attrdef->m_imin != 0) { - PyErr_Format(PyExc_TypeError, "expected a float for attribute \"%s\"", attrdef->m_name); - goto FREE_AND_ERROR; - } - else if (attrdef->m_clamp) + if (attrdef->m_size != attrdef->m_imin*attrdef->m_imax*sizeof(float)) + { + PyErr_Format(PyExc_TypeError, "internal error: incorrect field size for attribute \"%s\"", attrdef->m_name); + goto FREE_AND_ERROR; + } + if (!PySequence_Check(value) || PySequence_Size(value) != attrdef->m_imin) + { + PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name); + goto FREE_AND_ERROR; + } + for (int i=0; i<attrdef->m_imin; i++) + { + PyObject *list = PySequence_GetItem(value, i); /* new ref */ + if (!PySequence_Check(list) || PySequence_Size(list) != attrdef->m_imax) + { + PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name); + goto RESTORE_AND_ERROR; + } + for (int j=0; j<attrdef->m_imax; j++) + { + item = PySequence_GetItem(list, j); /* new ref */ + if (!py_check_attr_float(var, item, attrdef)) + { + PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name); + goto RESTORE_AND_ERROR; + } + Py_DECREF(item); + item = NULL; + ++var; + } + Py_DECREF(list); + list = NULL; + } + } + else if (attrdef->m_imax != 0) { - if (val < attrdef->m_fmin) - val = attrdef->m_fmin; - else if (val > attrdef->m_fmax) - val = attrdef->m_fmax; - } - else if (val < attrdef->m_fmin || val > attrdef->m_fmax) + if (attrdef->m_size != attrdef->m_imax*sizeof(float)) + { + PyErr_Format(PyExc_TypeError, "internal error: incorrect field size for attribute \"%s\"", attrdef->m_name); + goto FREE_AND_ERROR; + } + if (!PySequence_Check(value) || PySequence_Size(value) != attrdef->m_imax) + { + PyErr_Format(PyExc_TypeError, "expected a sequence of [%d] floats for attribute \"%s\"", attrdef->m_imax, attrdef->m_name); + goto FREE_AND_ERROR; + } + for (int i=0; i<attrdef->m_imax; i++) + { + item = PySequence_GetItem(value, i); /* new ref */ + if (!py_check_attr_float(var, item, attrdef)) + { + goto RESTORE_AND_ERROR; + } + Py_DECREF(item); + item = NULL; + ++var; + } + } + else { - PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name); - goto FREE_AND_ERROR; + if (!py_check_attr_float(var, value, attrdef)) + goto FREE_AND_ERROR; } - *var = (float)val; break; } case KX_PYATTRIBUTE_TYPE_VECTOR: @@ -717,16 +930,15 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb if (!PySequence_Check(value) || PySequence_Size(value) != 3) { PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name); - return PY_SET_ATTR_FAIL; + goto FREE_AND_ERROR; } MT_Vector3 *var = reinterpret_cast<MT_Vector3*>(ptr); for (int i=0; i<3; i++) { - PyObject *item = PySequence_GetItem(value, i); /* new ref */ - // we can decrement the reference immediately, the reference count - // is at least 1 because the item is part of an array - Py_DECREF(item); + item = PySequence_GetItem(value, i); /* new ref */ double val = PyFloat_AsDouble(item); + Py_DECREF(item); + item = NULL; if (val == -1.0 && PyErr_Occurred()) { PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name); @@ -748,21 +960,38 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb } break; } + case KX_PYATTRIBUTE_TYPE_CHAR: + { + if (PyUnicode_Check(value)) + { + Py_ssize_t val_len; + char *val = _PyUnicode_AsStringAndSize(value, &val_len); + strncpy(ptr, val, attrdef->m_size); + ptr[attrdef->m_size-1] = 0; + } + else + { + PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name); + goto FREE_AND_ERROR; + } + break; + } case KX_PYATTRIBUTE_TYPE_STRING: { STR_String *var = reinterpret_cast<STR_String*>(ptr); - if (PyString_Check(value)) + if (PyUnicode_Check(value)) { - char *val = PyString_AsString(value); + Py_ssize_t val_len; + char *val = _PyUnicode_AsStringAndSize(value, &val_len); if (attrdef->m_clamp) { - if (strlen(val) < attrdef->m_imin) + if (val_len < attrdef->m_imin) { // can't increase the length of the string PyErr_Format(PyExc_ValueError, "string length too short for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } - else if (strlen(val) > attrdef->m_imax) + else if (val_len > attrdef->m_imax) { // trim the string char c = val[attrdef->m_imax]; @@ -771,7 +1000,7 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb val[attrdef->m_imax] = c; break; } - } else if (strlen(val) < attrdef->m_imin || strlen(val) > attrdef->m_imax) + } else if (val_len < attrdef->m_imin || val_len > attrdef->m_imax) { PyErr_Format(PyExc_ValueError, "string length out of range for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; @@ -794,7 +1023,7 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb // check if post processing is needed if (attrdef->m_checkFunction != NULL) { - if ((*attrdef->m_checkFunction)(self, attrdef) != 0) + if ((*attrdef->m_checkFunction)(ref, attrdef) != 0) { // restore value RESTORE_AND_ERROR: @@ -815,6 +1044,10 @@ int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyOb FREE_AND_ERROR: if (undoBuffer) free(undoBuffer); + if (list) + Py_DECREF(list); + if (item) + Py_DECREF(item); return 1; } } @@ -834,48 +1067,6 @@ PyObject *PyObjectPlus::py_repr(void) return NULL; } -/*------------------------------ - * PyObjectPlus isA -- the isA functions -------------------------------*/ -bool PyObjectPlus::isA(PyTypeObject *T) // if called with a Type, use "typename" -{ - int i; - PyParentObject P; - PyParentObject *Ps = GetParents(); - - for (P = Ps[i=0]; P != NULL; P = Ps[i++]) - if (P==T) - return true; - - return false; -} - - -bool PyObjectPlus::isA(const char *mytypename) // check typename of each parent -{ - int i; - PyParentObject P; - PyParentObject *Ps = GetParents(); - - for (P = Ps[i=0]; P != NULL; P = Ps[i++]) - if (strcmp(P->tp_name, mytypename)==0) - return true; - - return false; -} - -PyObject *PyObjectPlus::PyisA(PyObject *value) // Python wrapper for isA -{ - if (PyType_Check(value)) { - return PyBool_FromLong(isA((PyTypeObject *)value)); - } else if (PyString_Check(value)) { - return PyBool_FromLong(isA(PyString_AsString(value))); - } - PyErr_SetString(PyExc_TypeError, "object.isA(value): expected a type or a string"); - return NULL; -} - - void PyObjectPlus::ProcessReplica() { /* Clear the proxy, will be created again if needed with GetProxy() @@ -900,44 +1091,35 @@ void PyObjectPlus::InvalidateProxy() // check typename of each parent } } -/* Utility function called by the macro py_getattro_up() - * for getting ob.__dict__() values from our PyObject - * this is used by python for doing dir() on an object, so its good - * if we return a list of attributes and methods. - * - * Other then making dir() useful the value returned from __dict__() is not useful - * since every value is a Py_None - * */ -PyObject *py_getattr_dict(PyObject *pydict, PyObject *tp_dict) -{ - if(pydict==NULL) { /* incase calling __dict__ on the parent of this object raised an error */ - PyErr_Clear(); - pydict = PyDict_New(); - } - - PyDict_Update(pydict, tp_dict); - return pydict; -} - - - -PyObject *PyObjectPlus::GetProxy_Ext(PyObjectPlus *self, PyTypeObject *tp) +PyObject *PyObjectPlus::GetProxyPlus_Ext(PyObjectPlus *self, PyTypeObject *tp, void *ptr) { if (self->m_proxy==NULL) { self->m_proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp); BGE_PROXY_PYOWNS(self->m_proxy) = false; + BGE_PROXY_PYREF(self->m_proxy) = true; } //PyObject_Print(self->m_proxy, stdout, 0); //printf("ref %d\n", self->m_proxy->ob_refcnt); BGE_PROXY_REF(self->m_proxy) = self; /* Its possible this was set to NULL, so set it back here */ + BGE_PROXY_PTR(self->m_proxy) = ptr; Py_INCREF(self->m_proxy); /* we own one, thos ones fore the return */ return self->m_proxy; } -PyObject *PyObjectPlus::NewProxy_Ext(PyObjectPlus *self, PyTypeObject *tp, bool py_owns) +PyObject *PyObjectPlus::NewProxyPlus_Ext(PyObjectPlus *self, PyTypeObject *tp, void *ptr, bool py_owns) { + if (!self) + { + // in case of proxy without reference to game object + PyObject* proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp); + BGE_PROXY_PYREF(proxy) = false; + BGE_PROXY_PYOWNS(proxy) = py_owns; + BGE_PROXY_REF(proxy) = NULL; + BGE_PROXY_PTR(proxy) = ptr; + return proxy; + } if (self->m_proxy) { if(py_owns) @@ -953,7 +1135,7 @@ PyObject *PyObjectPlus::NewProxy_Ext(PyObjectPlus *self, PyTypeObject *tp, bool } - GetProxy_Ext(self, tp); + GetProxyPlus_Ext(self, tp, ptr); if(py_owns) { BGE_PROXY_PYOWNS(self->m_proxy) = py_owns; Py_DECREF(self->m_proxy); /* could avoid thrashing here but for now its ok */ @@ -971,45 +1153,47 @@ void PyObjectPlus::SetDeprecationWarnings(bool ignoreDeprecationWarnings) m_ignore_deprecation_warnings = ignoreDeprecationWarnings; } -void PyObjectPlus::ShowDeprecationWarning_func(const char* old_way,const char* new_way) +void PyDebugLine() { - { - printf("Method %s is deprecated, please use %s instead.\n", old_way, new_way); - - // import sys; print '\t%s:%d' % (sys._getframe(0).f_code.co_filename, sys._getframe(0).f_lineno) - - PyObject *getframe, *frame; - PyObject *f_lineno, *f_code, *co_filename; - - getframe = PySys_GetObject((char *)"_getframe"); // borrowed - if (getframe) { - frame = PyObject_CallObject(getframe, NULL); - if (frame) { - f_lineno= PyObject_GetAttrString(frame, "f_lineno"); - f_code= PyObject_GetAttrString(frame, "f_code"); - if (f_lineno && f_code) { - co_filename= PyObject_GetAttrString(f_code, "co_filename"); - if (co_filename) { - - printf("\t%s:%d\n", PyString_AsString(co_filename), (int)PyInt_AsLong(f_lineno)); - - Py_DECREF(f_lineno); - Py_DECREF(f_code); - Py_DECREF(co_filename); - Py_DECREF(frame); - return; - } + // import sys; print '\t%s:%d' % (sys._getframe(0).f_code.co_filename, sys._getframe(0).f_lineno) + + PyObject *getframe, *frame; + PyObject *f_lineno, *f_code, *co_filename; + + getframe = PySys_GetObject((char *)"_getframe"); // borrowed + if (getframe) { + frame = PyObject_CallObject(getframe, NULL); + if (frame) { + f_lineno= PyObject_GetAttrString(frame, "f_lineno"); + f_code= PyObject_GetAttrString(frame, "f_code"); + if (f_lineno && f_code) { + co_filename= PyObject_GetAttrString(f_code, "co_filename"); + if (co_filename) { + + printf("\t%s:%d\n", _PyUnicode_AsString(co_filename), (int)PyLong_AsSsize_t(f_lineno)); + + Py_DECREF(f_lineno); + Py_DECREF(f_code); + Py_DECREF(co_filename); + Py_DECREF(frame); + return; } - - Py_XDECREF(f_lineno); - Py_XDECREF(f_code); - Py_DECREF(frame); } + Py_XDECREF(f_lineno); + Py_XDECREF(f_code); + Py_DECREF(frame); } - PyErr_Clear(); - printf("\tERROR - Could not access sys._getframe(0).f_lineno or sys._getframe().f_code.co_filename\n"); + } + PyErr_Clear(); + printf("\tERROR - Could not access sys._getframe(0).f_lineno or sys._getframe().f_code.co_filename\n"); +} + +void PyObjectPlus::ShowDeprecationWarning_func(const char* old_way,const char* new_way) +{ + printf("Method %s is deprecated, please use %s instead.\n", old_way, new_way); + PyDebugLine(); } void PyObjectPlus::ClearDeprecationWarning() |