PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/interfaces/python/swig.cpp

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