VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/python/src/PyISupports.cpp@ 50710

Last change on this file since 50710 was 50710, checked in by vboxsync, 11 years ago

pyxpcom: Support new style python classes (derived from object), upstream changeset b6fce9508c70 (http://hg.mozilla.org/pyxpcom/rev/b6fce9508c70).

  • Property svn:eol-style set to native
File size: 15.2 KB
Line 
1/* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is the Python XPCOM language bindings.
15 *
16 * The Initial Developer of the Original Code is
17 * ActiveState Tool Corp.
18 * Portions created by the Initial Developer are Copyright (C) 2000
19 * the Initial Developer. All Rights Reserved.
20 *
21 * Contributor(s):
22 * Mark Hammond <[email protected]> (original author)
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38//
39// This code is part of the XPCOM extensions for Python.
40//
41// Written May 2000 by Mark Hammond.
42//
43// Based heavily on the Python COM support, which is
44// (c) Mark Hammond and Greg Stein.
45//
46// (c) 2000, ActiveState corp.
47
48#include "PyXPCOM_std.h"
49#include "nsISupportsPrimitives.h"
50
51static PRInt32 cInterfaces=0;
52static PyObject *g_obFuncMakeInterfaceCount = NULL; // XXX - never released!!!
53
54#ifdef VBOX_DEBUG_LIFETIMES
55# include <iprt/log.h>
56# include <iprt/stream.h>
57
58/*static*/ RTLISTNODE Py_nsISupports::g_List;
59/*static*/ RTONCE Py_nsISupports::g_Once = RTONCE_INITIALIZER;
60/*static*/ RTCRITSECT Py_nsISupports::g_CritSect;
61
62/*static*/ DECLCALLBACK(int32_t)
63Py_nsISupports::initOnceCallback(void *pvUser1)
64{
65 NOREF(pvUser1);
66 RTListInit(&g_List);
67 return RTCritSectInit(&g_CritSect);
68}
69
70/*static*/ void
71Py_nsISupports::dumpList(void)
72{
73 RTOnce(&g_Once, initOnceCallback, NULL);
74 RTCritSectEnter(&g_CritSect);
75
76 uint32_t i = 0;
77 Py_nsISupports *pCur;
78 RTListForEach(&g_List, pCur, Py_nsISupports, m_ListEntry)
79 {
80 nsISupports *pISup = pCur->m_obj;
81 PyXPCOM_LogWarning("#%u: %p iid=%RTuuid obj=%p", i, pCur, &pCur->m_iid, pISup);
82 i++;
83 }
84
85 RTCritSectLeave(&g_CritSect);
86}
87
88/*static*/ void
89Py_nsISupports::dumpListToStdOut()
90{
91 RTOnce(&g_Once, initOnceCallback, NULL);
92 RTCritSectEnter(&g_CritSect);
93
94 uint32_t i = 0;
95 Py_nsISupports *pCur;
96 RTListForEach(&g_List, pCur, Py_nsISupports, m_ListEntry)
97 {
98 nsISupports *pISup = pCur->m_obj;
99 RTPrintf("#%u: %p iid=%RTuuid obj=%p\n", i, pCur, &pCur->m_iid, pISup);
100 i++;
101 }
102
103 RTCritSectLeave(&g_CritSect);
104}
105
106PRInt32
107_PyXPCOM_DumpInterfaces(void)
108{
109 Py_nsISupports::dumpListToStdOut();
110 return NS_OK;
111}
112
113#endif /* _DEBUG_LIFETIMES */
114
115
116
117PyObject *PyObject_FromNSInterface( nsISupports *aInterface,
118 const nsIID &iid,
119 PRBool bMakeNicePyObject /*= PR_TRUE */)
120{
121 return Py_nsISupports::PyObjectFromInterface(aInterface, iid,
122 bMakeNicePyObject);
123}
124
125PRInt32
126_PyXPCOM_GetInterfaceCount(void)
127{
128 return cInterfaces;
129}
130
131Py_nsISupports::Py_nsISupports(nsISupports *punk, const nsIID &iid, PyTypeObject *this_type)
132{
133 ob_type = this_type;
134 m_obj = punk;
135 m_iid = iid;
136 // refcnt of object managed by caller.
137 PR_AtomicIncrement(&cInterfaces);
138 PyXPCOM_DLLAddRef();
139 _Py_NewReference(this);
140
141#ifdef VBOX_DEBUG_LIFETIMES
142 RTOnce(&g_Once, initOnceCallback, NULL);
143 RTCritSectEnter(&g_CritSect);
144 RTListAppend(&g_List, &m_ListEntry);
145 RTCritSectLeave(&g_CritSect);
146 PyXPCOM_LogWarning("Creating %p: iid=%RTuuid obj=%p", this, &m_iid, punk);
147#endif
148}
149
150Py_nsISupports::~Py_nsISupports()
151{
152#ifdef VBOX_DEBUG_LIFETIMES
153 RTCritSectEnter(&g_CritSect);
154 nsISupports *punk = m_obj;
155 RTListNodeRemove(&m_ListEntry);
156 RTCritSectLeave(&g_CritSect);
157 PyXPCOM_LogWarning("Destroying %p: iid=%RTuuid obj=%p", this, &m_iid, punk);
158#endif
159
160 SafeRelease(this);
161 PR_AtomicDecrement(&cInterfaces);
162 PyXPCOM_DLLRelease();
163}
164
165/*static*/ nsISupports *
166Py_nsISupports::GetI(PyObject *self, nsIID *ret_iid)
167{
168 if (self==NULL) {
169 PyErr_SetString(PyExc_ValueError, "The Python object is invalid");
170 return NULL;
171 }
172 Py_nsISupports *pis = (Py_nsISupports *)self;
173 if (pis->m_obj==NULL) {
174 // This should never be able to happen.
175 PyErr_SetString(PyExc_ValueError, "Internal Error - The XPCOM object has been released.");
176 return NULL;
177 }
178 if (ret_iid)
179 *ret_iid = pis->m_iid;
180 return pis->m_obj;
181}
182
183/*static*/ void
184Py_nsISupports::SafeRelease(Py_nsISupports *ob)
185{
186 if (!ob)
187 return;
188 if (ob->m_obj)
189 {
190 Py_BEGIN_ALLOW_THREADS;
191 ob->m_obj = nsnull;
192 Py_END_ALLOW_THREADS;
193 }
194}
195
196/* virtual */ PyObject *
197Py_nsISupports::getattr(const char *name)
198{
199 if (strcmp(name, "IID")==0)
200 return Py_nsIID::PyObjectFromIID( m_iid );
201
202 // Support for __unicode__ until we get a tp_unicode slot.
203 if (strcmp(name, "__unicode__")==0) {
204 nsresult rv;
205 PRUnichar *val = NULL;
206 Py_BEGIN_ALLOW_THREADS;
207 { // scope to kill pointer while thread-lock released.
208 nsCOMPtr<nsISupportsString> ss( do_QueryInterface(m_obj, &rv ));
209 if (NS_SUCCEEDED(rv))
210 rv = ss->ToString(&val);
211 } // end-scope
212 Py_END_ALLOW_THREADS;
213 PyObject *ret = NS_FAILED(rv) ?
214 PyXPCOM_BuildPyException(rv) :
215 PyObject_FromNSString(val);
216 if (val) nsMemory::Free(val);
217 return ret;
218 }
219 PyXPCOM_TypeObject *this_type = (PyXPCOM_TypeObject *)ob_type;
220 return Py_FindMethodInChain(&this_type->chain, this, (char *)name);
221}
222
223/* virtual */ int
224Py_nsISupports::setattr(const char *name, PyObject *v)
225{
226 char buf[128];
227#ifdef VBOX
228 snprintf(buf, sizeof(buf), "%s has read-only attributes", ob_type->tp_name );
229#else
230 sprintf(buf, "%s has read-only attributes", ob_type->tp_name );
231#endif
232 PyErr_SetString(PyExc_TypeError, buf);
233 return -1;
234}
235
236/*static*/ Py_nsISupports *
237Py_nsISupports::Constructor(nsISupports *pInitObj, const nsIID &iid)
238{
239 return new Py_nsISupports(pInitObj,
240 iid,
241 type);
242}
243
244PRBool
245Py_nsISupports::InterfaceFromPyISupports(PyObject *ob,
246 const nsIID &iid,
247 nsISupports **ppv)
248{
249 nsISupports *pis;
250 PRBool rc = PR_FALSE;
251 if ( !Check(ob) )
252 {
253 PyErr_Format(PyExc_TypeError, "Objects of type '%s' can not be used as COM objects", ob->ob_type->tp_name);
254 goto done;
255 }
256 nsIID already_iid;
257 pis = GetI(ob, &already_iid);
258 if ( !pis )
259 goto done; /* exception was set by GetI() */
260 /* note: we don't (yet) explicitly hold a reference to pis */
261 if (iid.Equals(Py_nsIID_NULL)) {
262 // a bit of a hack - we are asking for the arbitary interface
263 // wrapped by this object, not some other specific interface -
264 // so no QI, just an AddRef();
265 Py_BEGIN_ALLOW_THREADS
266 pis->AddRef();
267 Py_END_ALLOW_THREADS
268 *ppv = pis;
269 } else {
270 // specific interface requested - if it is not already the
271 // specific interface, QI for it and discard pis.
272 if (iid.Equals(already_iid)) {
273 *ppv = pis;
274 pis->AddRef();
275 } else {
276 nsresult r;
277 Py_BEGIN_ALLOW_THREADS
278 r = pis->QueryInterface(iid, (void **)ppv);
279 Py_END_ALLOW_THREADS
280 if ( NS_FAILED(r) )
281 {
282 PyXPCOM_BuildPyException(r);
283 goto done;
284 }
285 /* note: the QI added a ref for the return value */
286 }
287 }
288 rc = PR_TRUE;
289done:
290 return rc;
291}
292
293PRBool
294Py_nsISupports::InterfaceFromPyObject(PyObject *ob,
295 const nsIID &iid,
296 nsISupports **ppv,
297 PRBool bNoneOK,
298 PRBool bTryAutoWrap /* = PR_TRUE */)
299{
300 if ( ob == NULL )
301 {
302 // don't overwrite an error message
303 if ( !PyErr_Occurred() )
304 PyErr_SetString(PyExc_TypeError, "The Python object is invalid");
305 return PR_FALSE;
306 }
307 if ( ob == Py_None )
308 {
309 if ( bNoneOK )
310 {
311 *ppv = NULL;
312 return PR_TRUE;
313 }
314 else
315 {
316 PyErr_SetString(PyExc_TypeError, "None is not a invalid interface object in this context");
317 return PR_FALSE;
318 }
319 }
320
321 // support nsIVariant
322 if (iid.Equals(NS_GET_IID(nsIVariant)) || iid.Equals(NS_GET_IID(nsIWritableVariant))) {
323 // Check it is not already nsIVariant
324 if (PyObject_HasAttrString(ob, "__class__")) {
325 PyObject *sub_ob = PyObject_GetAttrString(ob, "_comobj_");
326 if (sub_ob==NULL) {
327 PyErr_Clear();
328 } else {
329 if (InterfaceFromPyISupports(sub_ob, iid, ppv)) {
330 Py_DECREF(sub_ob);
331 return PR_TRUE;
332 }
333 PyErr_Clear();
334 Py_DECREF(sub_ob);
335 }
336 }
337 nsresult nr = PyObject_AsVariant(ob, (nsIVariant **)ppv);
338 if (NS_FAILED(nr)) {
339 PyXPCOM_BuildPyException(nr);
340 return PR_FALSE;
341 }
342 NS_ASSERTION(ppv != nsnull, "PyObject_AsVariant worked but gave null!");
343 return PR_TRUE;
344 }
345 // end of variant support.
346
347 if (PyObject_HasAttrString(ob, "__class__")) {
348 // Get the _comobj_ attribute
349 PyObject *use_ob = PyObject_GetAttrString(ob, "_comobj_");
350 if (use_ob==NULL) {
351 PyErr_Clear();
352 if (bTryAutoWrap)
353 // Try and auto-wrap it - errors will leave Py exception set,
354 return PyXPCOM_XPTStub::AutoWrapPythonInstance(ob, iid, ppv);
355 PyErr_SetString(PyExc_TypeError, "The Python instance can not be converted to an XPCOM object");
356 return PR_FALSE;
357 } else
358 ob = use_ob;
359
360 } else {
361 Py_INCREF(ob);
362 }
363 PRBool rc = InterfaceFromPyISupports(ob, iid, ppv);
364 Py_DECREF(ob);
365 return rc;
366}
367
368
369// Interface conversions
370/*static*/void
371Py_nsISupports::RegisterInterface( const nsIID &iid, PyTypeObject *t)
372{
373 if (mapIIDToType==NULL)
374 mapIIDToType = PyDict_New();
375
376 if (mapIIDToType) {
377 PyObject *key = Py_nsIID::PyObjectFromIID(iid);
378 if (key)
379 PyDict_SetItem(mapIIDToType, key, (PyObject *)t);
380 Py_XDECREF(key);
381 }
382}
383
384/*static */PyObject *
385Py_nsISupports::PyObjectFromInterface(nsISupports *pis,
386 const nsIID &riid,
387 PRBool bMakeNicePyObject, /* = PR_TRUE */
388 PRBool bIsInternalCall /* = PR_FALSE */)
389{
390 // Quick exit.
391 if (pis==NULL) {
392 Py_INCREF(Py_None);
393 return Py_None;
394 }
395
396 if (!bIsInternalCall) {
397#ifdef NS_DEBUG
398 nsISupports *queryResult = nsnull;
399 Py_BEGIN_ALLOW_THREADS;
400 pis->QueryInterface(riid, (void **)&queryResult);
401 Py_END_ALLOW_THREADS;
402 NS_ASSERTION(queryResult == pis, "QueryInterface needed");
403 NS_IF_RELEASE(queryResult);
404#endif
405 }
406
407 PyTypeObject *createType = NULL;
408 // If the IID is for nsISupports, dont bother with
409 // a map lookup as we know the type!
410 if (!riid.Equals(NS_GET_IID(nsISupports))) {
411 // Look up the map
412 PyObject *obiid = Py_nsIID::PyObjectFromIID(riid);
413 if (!obiid) return NULL;
414
415 if (mapIIDToType != NULL)
416 createType = (PyTypeObject *)PyDict_GetItem(mapIIDToType, obiid);
417 Py_DECREF(obiid);
418 }
419 if (createType==NULL)
420 createType = Py_nsISupports::type;
421 // Check it is indeed one of our types.
422 if (!PyXPCOM_TypeObject::IsType(createType)) {
423 PyErr_SetString(PyExc_RuntimeError, "The type map is invalid");
424 return NULL;
425 }
426 // we can now safely cast the thing to a PyComTypeObject and use it
427 PyXPCOM_TypeObject *myCreateType = (PyXPCOM_TypeObject *)createType;
428 if (myCreateType->ctor==NULL) {
429 PyErr_SetString(PyExc_TypeError, "The type does not declare a PyCom constructor");
430 return NULL;
431 }
432
433 Py_nsISupports *ret = (*myCreateType->ctor)(pis, riid);
434#ifdef _DEBUG_LIFETIMES
435 PyXPCOM_LogF("XPCOM Object created at 0x%0xld, nsISupports at 0x%0xld",
436 ret, ret->m_obj);
437#endif
438 if (ret && bMakeNicePyObject)
439 return MakeDefaultWrapper(ret, riid);
440 return ret;
441}
442
443// Call back into Python, passing a raw nsIInterface object, getting back
444// the object to actually pass to Python.
445PyObject *
446Py_nsISupports::MakeDefaultWrapper(PyObject *pyis,
447 const nsIID &iid)
448{
449 NS_PRECONDITION(pyis, "NULL pyobject!");
450 PyObject *obIID = NULL;
451 PyObject *args = NULL;
452 PyObject *mod = NULL;
453 PyObject *ret = NULL;
454
455 obIID = Py_nsIID::PyObjectFromIID(iid);
456 if (obIID==NULL)
457 goto done;
458
459 if (g_obFuncMakeInterfaceCount==NULL) {
460 PyObject *mod = PyImport_ImportModule("xpcom.client");
461 if (mod)
462 g_obFuncMakeInterfaceCount = PyObject_GetAttrString(mod, "MakeInterfaceResult");
463 Py_XDECREF(mod);
464 }
465 if (g_obFuncMakeInterfaceCount==NULL) goto done;
466
467 args = Py_BuildValue("OO", pyis, obIID);
468 if (args==NULL) goto done;
469 ret = PyEval_CallObject(g_obFuncMakeInterfaceCount, args);
470done:
471 if (PyErr_Occurred()) {
472 NS_ABORT_IF_FALSE(ret==NULL, "Have an error, but also a return val!");
473 PyXPCOM_LogError("Creating an interface object to be used as a result failed\n");
474 PyErr_Clear();
475 }
476 Py_XDECREF(mod);
477 Py_XDECREF(args);
478 Py_XDECREF(obIID);
479 if (ret==NULL) // eek - error - return the original with no refcount mod.
480 ret = pyis;
481 else
482 // no error - decref the old object
483 Py_DECREF(pyis);
484 // return our obISupports. If NULL, we are really hosed and nothing we can do.
485 return ret;
486}
487
488// @pymethod <o Py_nsISupports>|Py_nsISupports|QueryInterface|Queries an object for a specific interface.
489PyObject *
490Py_nsISupports::QueryInterface(PyObject *self, PyObject *args)
491{
492 PyObject *obiid;
493 int bWrap = 1;
494 // @pyparm IID|iid||The IID requested.
495 // @rdesc The result is always a <o Py_nsISupports> object.
496 // Any error (including E_NOINTERFACE) will generate a <o com_error> exception.
497 if (!PyArg_ParseTuple(args, "O|i:QueryInterface", &obiid, &bWrap))
498 return NULL;
499
500 nsIID iid;
501 if (!Py_nsIID::IIDFromPyObject(obiid, &iid))
502 return NULL;
503
504 nsISupports *pMyIS = GetI(self);
505 if (pMyIS==NULL) return NULL;
506
507 // Optimization, If we already wrap the IID, just return
508 // ourself.
509 if (!bWrap && iid.Equals(((Py_nsISupports *)self)->m_iid)) {
510 Py_INCREF(self);
511 return self;
512 }
513
514 nsCOMPtr<nsISupports> pis;
515 nsresult r;
516 Py_BEGIN_ALLOW_THREADS;
517 r = pMyIS->QueryInterface(iid, getter_AddRefs(pis));
518 Py_END_ALLOW_THREADS;
519
520 /* Note that this failure may include E_NOINTERFACE */
521 if ( NS_FAILED(r) )
522 return PyXPCOM_BuildPyException(r);
523
524 /* Return a type based on the IID (with no extra ref) */
525 return ((Py_nsISupports *)self)->MakeInterfaceResult(pis, iid, (PRBool)bWrap);
526}
527
528
529#ifdef VBOX
530static PyObject *
531QueryErrorObject(PyObject *self, PyObject *args)
532{
533 nsresult rc = 0;
534
535 if (!PyArg_ParseTuple(args, "i", &rc))
536 return NULL;
537
538 return PyXPCOM_BuildErrorMessage(rc);
539}
540#endif
541
542// @object Py_nsISupports|The base object for all PythonCOM objects. Wraps a COM nsISupports interface.
543/*static*/ struct PyMethodDef
544Py_nsISupports::methods[] =
545{
546 { "queryInterface", Py_nsISupports::QueryInterface, 1, "Queries the object for an interface."},
547 { "QueryInterface", Py_nsISupports::QueryInterface, 1, "An alias for queryInterface."},
548#ifdef VBOX
549 { "QueryErrorObject", QueryErrorObject, 1, "Query an error object for given status code."},
550#endif
551 {NULL}
552};
553
554/*static*/void Py_nsISupports::InitType(void)
555{
556 type = new PyXPCOM_TypeObject(
557 "nsISupports",
558 NULL,
559 sizeof(Py_nsISupports),
560 methods,
561 Constructor);
562}
563
564PyXPCOM_TypeObject *Py_nsISupports::type = NULL;
565PyObject *Py_nsISupports::mapIIDToType = NULL;
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette