f7499e695d1ad4fcf46537ab618171da0ec58adc
[vuplus_xbmc] / xbmc / interfaces / python / swig.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "LanguageHook.h"
22 #include "swig.h"
23
24 #include <string>
25
26 namespace PythonBindings
27 {
28   TypeInfo::TypeInfo(const std::type_info& ti) : swigType(NULL), parentType(NULL), typeIndex(ti)
29   {
30     static PyTypeObject py_type_object_header = { PyObject_HEAD_INIT(NULL) 0};
31     static int size = (long*)&(py_type_object_header.tp_name) - (long*)&py_type_object_header;
32     memcpy(&(this->pythonType), &py_type_object_header, size);
33   }
34
35   class PyObjectDecrementor
36   {
37     PyObject* obj;
38   public:
39     inline PyObjectDecrementor(PyObject* pyobj) : obj(pyobj) {}
40     inline ~PyObjectDecrementor() { Py_XDECREF(obj); }
41
42     inline PyObject* get() { return obj; }
43   };
44
45   void PyXBMCGetUnicodeString(std::string& buf, PyObject* pObject, bool coerceToString,
46                               const char* argumentName, const char* methodname) throw (XBMCAddon::WrongTypeException)
47   {
48     // TODO: UTF-8: Does python use UTF-16?
49     //              Do we need to convert from the string charset to UTF-8
50     //              for non-unicode data?
51     if (PyUnicode_Check(pObject))
52     {
53       // Python unicode objects are UCS2 or UCS4 depending on compilation
54       // options, wchar_t is 16-bit or 32-bit depending on platform.
55       // Avoid the complexity by just letting python convert the string.
56       PyObject *utf8_pyString = PyUnicode_AsUTF8String(pObject);
57
58       if (utf8_pyString)
59       {
60         buf = PyString_AsString(utf8_pyString);
61         Py_DECREF(utf8_pyString);
62         return;
63       }
64     }
65     if (PyString_Check(pObject))
66     {
67       buf = PyString_AsString(pObject);
68       return;
69     }
70
71     // if we got here then we need to coerce the value to a string
72     if (coerceToString)
73     {
74       PyObjectDecrementor dec(PyObject_Str(pObject));
75       PyObject* pyStrCast = dec.get();
76       if (pyStrCast)
77       {
78         PyXBMCGetUnicodeString(buf,pyStrCast,false,argumentName,methodname);
79         return;
80       }
81     }
82
83     // Object is not a unicode or a normal string.
84     buf = "";
85     throw XBMCAddon::WrongTypeException("argument \"%s\" for method \"%s\" must be unicode or str", argumentName, methodname);
86   }
87
88   // need to compare the typestring
89   bool isParameterRightType(const char* passedType, const char* expectedType, const char* methodNamespacePrefix, bool tryReverse)
90   {
91     if (strcmp(expectedType,passedType) == 0)
92       return true;
93
94     // well now things are a bit more complicated. We need to see if the passed type
95     // is a subset of the overall type
96     std::string et(expectedType);
97     bool isPointer = (et[0] == 'p' && et[1] == '.');
98     std::string baseType(et,(isPointer ? 2 : 0)); // this may contain a namespace
99
100     std::string ns(methodNamespacePrefix);
101     // cut off trailing '::'
102     if (ns.size() > 2 && ns[ns.size() - 1] == ':' && ns[ns.size() - 2] == ':')
103       ns = ns.substr(0,ns.size()-2);
104
105     bool done = false;
106     while(! done)
107     {
108       done = true;
109
110       // now we need to see if the expected type can be munged
111       //  into the passed type by tacking on the namespace of
112       //  of the method.
113       std::string check(isPointer ? "p." : "");
114       check += ns;
115       check += "::";
116       check += baseType;
117
118       if (strcmp(check.c_str(),passedType) == 0)
119         return true;
120
121       // see if the namespace is nested.
122       int posOfScopeOp = ns.find("::");
123       if (posOfScopeOp >= 0)
124       {
125         done = false;
126         // cur off the outermost namespace
127         ns = ns.substr(posOfScopeOp + 2);
128       }
129     }
130
131     // so far we applied the namespace to the expected type. Now lets try
132     //  the reverse if we haven't already.
133     if (tryReverse)
134       return isParameterRightType(expectedType, passedType, methodNamespacePrefix, false);
135
136     return false;
137   }
138
139   PythonToCppException::PythonToCppException() : XbmcCommons::UncheckedException(" ")
140   {
141     setClassname("PythonToCppException");
142
143     PyObject* exc_type;
144     PyObject* exc_value;
145     PyObject* exc_traceback;
146     PyObject* pystring = NULL;
147
148     CStdString msg;
149
150     PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
151     if (exc_type == 0 && exc_value == 0 && exc_traceback == 0)
152     {
153       msg = "Strange: No Python exception occured";
154     }
155     else
156     {
157       msg = "-->Python callback/script returned the following error<--\n";
158       msg += " - NOTE: IGNORING THIS CAN LEAD TO MEMORY LEAKS!\n";
159       if (exc_type != NULL && (pystring = PyObject_Str(exc_type)) != NULL && (PyString_Check(pystring)))
160       {
161           PyObject *tracebackModule;
162
163           msg.AppendFormat("Error Type: %s\n", PyString_AsString(pystring));
164           if (PyObject_Str(exc_value))
165             msg.AppendFormat("Error Contents: %s\n", PyString_AsString(PyObject_Str(exc_value)));
166
167           tracebackModule = PyImport_ImportModule((char*)"traceback");
168           if (tracebackModule != NULL)
169           {
170             PyObject *tbList, *emptyString, *strRetval;
171
172             tbList = PyObject_CallMethod(tracebackModule, (char*)"format_exception", (char*)"OOO", exc_type, exc_value == NULL ? Py_None : exc_value, exc_traceback == NULL ? Py_None : exc_traceback);
173             emptyString = PyString_FromString("");
174             strRetval = PyObject_CallMethod(emptyString, (char*)"join", (char*)"O", tbList);
175             
176             msg.Format("%s%s", msg.c_str(),PyString_AsString(strRetval));
177
178             Py_DECREF(tbList);
179             Py_DECREF(emptyString);
180             Py_DECREF(strRetval);
181             Py_DECREF(tracebackModule);
182           }
183           msg += "-->End of Python script error report<--\n";
184       }
185       else
186       {
187         pystring = NULL;
188         msg += "<unknown exception type>";
189       }
190     }
191
192     Py_XDECREF(exc_type);
193     Py_XDECREF(exc_value); // caller owns all 3
194     Py_XDECREF(exc_traceback); // already NULL'd out
195     Py_XDECREF(pystring);
196
197     SetMessage("%s",msg.c_str());
198   }
199
200   XBMCAddon::AddonClass* doretrieveApiInstance(const PyHolder* pythonObj, const TypeInfo* typeInfo, const char* expectedType, 
201                               const char* methodNamespacePrefix, const char* methodNameForErrorString) throw (XBMCAddon::WrongTypeException)
202   {
203     if (pythonObj->magicNumber != XBMC_PYTHON_TYPE_MAGIC_NUMBER)
204       throw XBMCAddon::WrongTypeException("Non api type passed to \"%s\" in place of the expected type \"%s.\"",
205                                           methodNameForErrorString, expectedType);
206     if (!isParameterRightType(typeInfo->swigType,expectedType,methodNamespacePrefix))
207     {
208       // maybe it's a child class
209       if (typeInfo->parentType)
210         return doretrieveApiInstance(pythonObj, typeInfo->parentType,expectedType, 
211                                      methodNamespacePrefix, methodNameForErrorString);
212       else
213         throw XBMCAddon::WrongTypeException("Incorrect type passed to \"%s\", was expecting a \"%s\" but received a \"%s\"",
214                                  methodNameForErrorString,expectedType,typeInfo->swigType);
215     }
216     return ((PyHolder*)pythonObj)->pSelf;
217   }
218
219   /**
220    * This method is a helper for the generated API. It's called prior to any API
221    * class constructor being returned from the generated code to Python
222    */
223   void prepareForReturn(XBMCAddon::AddonClass* c)
224   {
225     XBMC_TRACE;
226     if(c) { 
227       c->Acquire(); 
228       PyThreadState* state = PyThreadState_Get();
229       XBMCAddon::Python::PythonLanguageHook::GetIfExists(state->interp)->RegisterAddonClassInstance(c);
230     }
231   }
232
233   static bool handleInterpRegistrationForClean(XBMCAddon::AddonClass* c)
234   {
235     XBMC_TRACE;
236     if(c){
237       XBMCAddon::AddonClass::Ref<XBMCAddon::Python::PythonLanguageHook> lh = 
238         XBMCAddon::AddonClass::Ref<XBMCAddon::AddonClass>(c->GetLanguageHook());
239
240       if (lh.isNotNull())
241       {
242         lh->UnregisterAddonClassInstance(c);
243         return true;
244       }
245       else
246       {
247         PyThreadState* state = PyThreadState_Get();
248         lh = XBMCAddon::Python::PythonLanguageHook::GetIfExists(state->interp);
249         if (lh.isNotNull()) lh->UnregisterAddonClassInstance(c);
250         return true;
251       }
252     }
253     return false;
254   }
255
256   /**
257    * This method is a helper for the generated API. It's called prior to any API
258    * class destructor being dealloc-ed from the generated code from Python
259    */
260   void cleanForDealloc(XBMCAddon::AddonClass* c) 
261   { 
262     XBMC_TRACE;
263     if (handleInterpRegistrationForClean(c))
264       c->Release();
265   }
266
267   /**
268    * This method is a helper for the generated API. It's called prior to any API
269    * class destructor being dealloc-ed from the generated code from Python
270    *
271    * There is a Catch-22 in the destruction of a Window. 'dispose' needs to be
272    * called on destruction but cannot be called from the destructor.
273    * This overrides the default cleanForDealloc to resolve that.
274    */
275   void cleanForDealloc(XBMCAddon::xbmcgui::Window* c) 
276   {
277     XBMC_TRACE;
278     if (handleInterpRegistrationForClean(c))
279     { 
280       c->dispose();
281       c->Release(); 
282     } 
283   }
284
285   /**
286    * This method allows for conversion of the native api Type to the Python type.
287    *
288    * When this form of the call is used (and pytype isn't NULL) then the
289    * passed type is used in the instance. This is for classes that extend API
290    * classes in python. The type passed may not be the same type that's stored
291    * in the class metadata of the AddonClass of which 'api' is an instance, 
292    * it can be a subclass in python.
293    *
294    * if pytype is NULL then the type is inferred using the class metadata 
295    * stored in the AddonClass instance 'api'.
296    */
297   PyObject* makePythonInstance(XBMCAddon::AddonClass* api, PyTypeObject* pytype, bool incrementRefCount)
298   {
299     // null api types result in Py_None
300     if (!api)
301     {
302       Py_INCREF(Py_None);
303       return Py_None;
304     }
305
306     // retrieve the TypeInfo from the api class
307     const TypeInfo* typeInfo = getTypeInfoForInstance(api);
308     PyTypeObject* typeObj = pytype == NULL ? (PyTypeObject*)(&(typeInfo->pythonType)) : pytype;
309
310     PyHolder* self = (PyHolder*)typeObj->tp_alloc(typeObj,0);
311     if (!self) return NULL;
312     self->magicNumber = XBMC_PYTHON_TYPE_MAGIC_NUMBER;
313     self->typeInfo = typeInfo;
314     self->pSelf = api;
315     if (incrementRefCount)
316       Py_INCREF((PyObject*)self);
317     return (PyObject*)self;
318   }
319
320   std::map<XbmcCommons::type_index, const TypeInfo*> typeInfoLookup;
321
322   void registerAddonClassTypeInformation(const TypeInfo* classInfo)
323   {
324     typeInfoLookup[classInfo->typeIndex] = classInfo;
325   }
326
327   const TypeInfo* getTypeInfoForInstance(XBMCAddon::AddonClass* obj)
328   {
329     XbmcCommons::type_index ti(typeid(*obj));
330     return typeInfoLookup[ti];
331   }
332
333 }
334