VirtualBox

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

Last change on this file was 101955, checked in by vboxsync, 6 months ago

libs/xpcom/python: Convert to IPRT and remove some dead code, bugref:10545

  • Property svn:eol-style set to native
File size: 16.7 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 <mhammond@skippinet.com.au> (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
51#include <iprt/asm.h>
52
53static volatile uint32_t cInterfaces=0;
54static PyObject *g_obFuncMakeInterfaceCount = NULL; // XXX - never released!!!
55
56#ifdef VBOX_DEBUG_LIFETIMES
57# include <iprt/log.h>
58# include <iprt/stream.h>
59
60/*static*/ RTLISTNODE Py_nsISupports::g_List;
61/*static*/ RTONCE Py_nsISupports::g_Once = RTONCE_INITIALIZER;
62/*static*/ RTCRITSECT Py_nsISupports::g_CritSect;
63
64/*static*/ DECLCALLBACK(int32_t)
65Py_nsISupports::initOnceCallback(void *pvUser1)
66{
67 NOREF(pvUser1);
68 RTListInit(&g_List);
69 return RTCritSectInit(&g_CritSect);
70}
71
72/*static*/ void
73Py_nsISupports::dumpList(void)
74{
75 RTOnce(&g_Once, initOnceCallback, NULL);
76 RTCritSectEnter(&g_CritSect);
77
78 uint32_t i = 0;
79 Py_nsISupports *pCur;
80 RTListForEach(&g_List, pCur, Py_nsISupports, m_ListEntry)
81 {
82 nsISupports *pISup = pCur->m_obj;
83 PyXPCOM_LogWarning("#%u: %p iid=%RTuuid obj=%p", i, pCur, &pCur->m_iid, pISup);
84 i++;
85 }
86
87 RTCritSectLeave(&g_CritSect);
88}
89
90/*static*/ void
91Py_nsISupports::dumpListToStdOut()
92{
93 RTOnce(&g_Once, initOnceCallback, NULL);
94 RTCritSectEnter(&g_CritSect);
95
96 uint32_t i = 0;
97 Py_nsISupports *pCur;
98 RTListForEach(&g_List, pCur, Py_nsISupports, m_ListEntry)
99 {
100 nsISupports *pISup = pCur->m_obj;
101 RTPrintf("#%u: %p iid=%RTuuid obj=%p\n", i, pCur, &pCur->m_iid, pISup);
102 i++;
103 }
104
105 RTCritSectLeave(&g_CritSect);
106}
107
108PRInt32
109_PyXPCOM_DumpInterfaces(void)
110{
111 Py_nsISupports::dumpListToStdOut();
112 return NS_OK;
113}
114
115#endif /* _DEBUG_LIFETIMES */
116
117
118
119PyObject *PyObject_FromNSInterface( nsISupports *aInterface,
120 const nsIID &iid,
121 PRBool bMakeNicePyObject /*= PR_TRUE */)
122{
123 return Py_nsISupports::PyObjectFromInterface(aInterface, iid,
124 bMakeNicePyObject);
125}
126
127uint32_t
128_PyXPCOM_GetInterfaceCount(void)
129{
130 return ASMAtomicReadU32(&cInterfaces);
131}
132
133#ifndef Py_LIMITED_API
134Py_nsISupports::Py_nsISupports(nsISupports *punk, const nsIID &iid, PyTypeObject *this_type)
135#else
136Py_nsISupports::Py_nsISupports(nsISupports *punk, const nsIID &iid, PyXPCOM_TypeObject *this_type)
137#endif
138{
139#ifndef Py_LIMITED_API
140 ob_type = this_type;
141#else
142 ob_type = this_type->m_pTypeObj;
143 m_pMyTypeObj = this_type;
144#endif
145 m_obj = punk;
146 m_iid = iid;
147 // refcnt of object managed by caller.
148 ASMAtomicIncU32(&cInterfaces);
149 PyXPCOM_DLLAddRef();
150#if 1 /* VBox: Must use for 3.9+, includes _Py_NewReferences. Works for all older versions too. @bugref{10079} */
151 PyObject_Init(this, ob_type);
152#else
153 _Py_NewReference(this);
154#endif
155
156#ifdef VBOX_DEBUG_LIFETIMES
157 RTOnce(&g_Once, initOnceCallback, NULL);
158 RTCritSectEnter(&g_CritSect);
159 RTListAppend(&g_List, &m_ListEntry);
160 RTCritSectLeave(&g_CritSect);
161 PyXPCOM_LogWarning("Creating %p: iid=%RTuuid obj=%p", this, &m_iid, punk);
162#endif
163}
164
165Py_nsISupports::~Py_nsISupports()
166{
167#ifdef VBOX_DEBUG_LIFETIMES
168 RTCritSectEnter(&g_CritSect);
169 nsISupports *punk = m_obj;
170 RTListNodeRemove(&m_ListEntry);
171 RTCritSectLeave(&g_CritSect);
172 PyXPCOM_LogWarning("Destroying %p: iid=%RTuuid obj=%p", this, &m_iid, punk);
173#endif
174
175 SafeRelease(this);
176 ASMAtomicDecU32(&cInterfaces);
177 PyXPCOM_DLLRelease();
178}
179
180/*static*/ nsISupports *
181Py_nsISupports::GetI(PyObject *self, nsIID *ret_iid)
182{
183 if (self==NULL) {
184 PyErr_SetString(PyExc_ValueError, "The Python object is invalid");
185 return NULL;
186 }
187 Py_nsISupports *pis = (Py_nsISupports *)self;
188 if (pis->m_obj==NULL) {
189 // This should never be able to happen.
190 PyErr_SetString(PyExc_ValueError, "Internal Error - The XPCOM object has been released.");
191 return NULL;
192 }
193 if (ret_iid)
194 *ret_iid = pis->m_iid;
195 return pis->m_obj;
196}
197
198/*static*/ void
199Py_nsISupports::SafeRelease(Py_nsISupports *ob)
200{
201 if (!ob)
202 return;
203 if (ob->m_obj)
204 {
205 Py_BEGIN_ALLOW_THREADS;
206 ob->m_obj = nsnull;
207 Py_END_ALLOW_THREADS;
208 }
209}
210
211/* virtual */ PyObject *
212Py_nsISupports::getattr(const char *name)
213{
214 if (strcmp(name, "IID")==0)
215 return Py_nsIID::PyObjectFromIID( m_iid );
216
217 // Support for __unicode__ until we get a tp_unicode slot.
218 if (strcmp(name, "__unicode__")==0) {
219 nsresult rv;
220 PRUnichar *val = NULL;
221 Py_BEGIN_ALLOW_THREADS;
222 { // scope to kill pointer while thread-lock released.
223 nsCOMPtr<nsISupportsString> ss( do_QueryInterface(m_obj, &rv ));
224 if (NS_SUCCEEDED(rv))
225 rv = ss->ToString(&val);
226 } // end-scope
227 Py_END_ALLOW_THREADS;
228 PyObject *ret = NS_FAILED(rv) ?
229 PyXPCOM_BuildPyException(rv) :
230 PyObject_FromNSString(val);
231 if (val) nsMemory::Free(val);
232 return ret;
233 }
234#ifndef Py_LIMITED_API
235 PyXPCOM_TypeObject *this_type = (PyXPCOM_TypeObject *)ob_type;
236#else
237 PyXPCOM_TypeObject *this_type = m_pMyTypeObj;
238#endif
239#if PY_MAJOR_VERSION <= 2
240 return Py_FindMethodInChain(&this_type->chain, this, (char *)name);
241#else
242 PyMethodChain *chain = &this_type->chain;
243 if (name[0] == '_' && name[1] == '_') {
244# ifndef Py_LIMITED_API /** @todo ? */
245 if (!strcmp(name, "__doc__")) {
246 const char *doc = ob_type->tp_doc;
247 if (doc)
248 return PyUnicode_FromString(doc);
249 }
250# endif
251 }
252 while (chain) {
253 PyMethodDef *ml = chain->methods;
254 for (; ml->ml_name; ml++) {
255 if (!strcmp(name, ml->ml_name))
256 return PyCFunction_New(ml, this);
257 }
258 chain = chain->link;
259 }
260 PyErr_SetString(PyExc_AttributeError, name);
261 return NULL;
262#endif
263}
264
265/* virtual */ int
266Py_nsISupports::setattr(const char *name, PyObject *v)
267{
268 char buf[128];
269#ifdef VBOX
270 snprintf(buf, sizeof(buf), "%s has read-only attributes", PyXPCOM_ObTypeName(this) );
271#else
272 sprintf(buf, "%s has read-only attributes", PyXPCOM_ObTypeName(this) );
273#endif
274 PyErr_SetString(PyExc_TypeError, buf);
275 return -1;
276}
277
278/*static*/ Py_nsISupports *
279Py_nsISupports::Constructor(nsISupports *pInitObj, const nsIID &iid)
280{
281 return new Py_nsISupports(pInitObj,
282 iid,
283 type);
284}
285
286PRBool
287Py_nsISupports::InterfaceFromPyISupports(PyObject *ob,
288 const nsIID &iid,
289 nsISupports **ppv)
290{
291 nsISupports *pis;
292 PRBool rc = PR_FALSE;
293 if ( !Check(ob) )
294 {
295 PyErr_Format(PyExc_TypeError, "Objects of type '%s' can not be used as COM objects", PyXPCOM_ObTypeName(ob));
296 goto done;
297 }
298 nsIID already_iid;
299 pis = GetI(ob, &already_iid);
300 if ( !pis )
301 goto done; /* exception was set by GetI() */
302 /* note: we don't (yet) explicitly hold a reference to pis */
303 if (iid.Equals(Py_nsIID_NULL)) {
304 // a bit of a hack - we are asking for the arbitary interface
305 // wrapped by this object, not some other specific interface -
306 // so no QI, just an AddRef();
307 Py_BEGIN_ALLOW_THREADS
308 pis->AddRef();
309 Py_END_ALLOW_THREADS
310 *ppv = pis;
311 } else {
312 // specific interface requested - if it is not already the
313 // specific interface, QI for it and discard pis.
314 if (iid.Equals(already_iid)) {
315 *ppv = pis;
316 pis->AddRef();
317 } else {
318 nsresult r;
319 Py_BEGIN_ALLOW_THREADS
320 r = pis->QueryInterface(iid, (void **)ppv);
321 Py_END_ALLOW_THREADS
322 if ( NS_FAILED(r) )
323 {
324 PyXPCOM_BuildPyException(r);
325 goto done;
326 }
327 /* note: the QI added a ref for the return value */
328 }
329 }
330 rc = PR_TRUE;
331done:
332 return rc;
333}
334
335PRBool
336Py_nsISupports::InterfaceFromPyObject(PyObject *ob,
337 const nsIID &iid,
338 nsISupports **ppv,
339 PRBool bNoneOK,
340 PRBool bTryAutoWrap /* = PR_TRUE */)
341{
342 if ( ob == NULL )
343 {
344 // don't overwrite an error message
345 if ( !PyErr_Occurred() )
346 PyErr_SetString(PyExc_TypeError, "The Python object is invalid");
347 return PR_FALSE;
348 }
349 if ( ob == Py_None )
350 {
351 if ( bNoneOK )
352 {
353 *ppv = NULL;
354 return PR_TRUE;
355 }
356 else
357 {
358 PyErr_SetString(PyExc_TypeError, "None is not a invalid interface object in this context");
359 return PR_FALSE;
360 }
361 }
362
363 // support nsIVariant
364 if (iid.Equals(NS_GET_IID(nsIVariant)) || iid.Equals(NS_GET_IID(nsIWritableVariant))) {
365 // Check it is not already nsIVariant
366 if (PyObject_HasAttrString(ob, "__class__")) {
367 PyObject *sub_ob = PyObject_GetAttrString(ob, "_comobj_");
368 if (sub_ob==NULL) {
369 PyErr_Clear();
370 } else {
371 if (InterfaceFromPyISupports(sub_ob, iid, ppv)) {
372 Py_DECREF(sub_ob);
373 return PR_TRUE;
374 }
375 PyErr_Clear();
376 Py_DECREF(sub_ob);
377 }
378 }
379 nsresult nr = PyObject_AsVariant(ob, (nsIVariant **)ppv);
380 if (NS_FAILED(nr)) {
381 PyXPCOM_BuildPyException(nr);
382 return PR_FALSE;
383 }
384 NS_ASSERTION(ppv != nsnull, "PyObject_AsVariant worked but gave null!");
385 return PR_TRUE;
386 }
387 // end of variant support.
388
389 if (PyObject_HasAttrString(ob, "__class__")) {
390 // Get the _comobj_ attribute
391 PyObject *use_ob = PyObject_GetAttrString(ob, "_comobj_");
392 if (use_ob==NULL) {
393 PyErr_Clear();
394 if (bTryAutoWrap)
395 // Try and auto-wrap it - errors will leave Py exception set,
396 return PyXPCOM_XPTStub::AutoWrapPythonInstance(ob, iid, ppv);
397 PyErr_SetString(PyExc_TypeError, "The Python instance can not be converted to an XPCOM object");
398 return PR_FALSE;
399 } else
400 ob = use_ob;
401
402 } else {
403 Py_INCREF(ob);
404 }
405 PRBool rc = InterfaceFromPyISupports(ob, iid, ppv);
406 Py_DECREF(ob);
407 return rc;
408}
409
410
411// Interface conversions
412/*static*/void
413#ifndef Py_LIMITED_API
414Py_nsISupports::RegisterInterface( const nsIID &iid, PyTypeObject *t)
415#else
416Py_nsISupports::RegisterInterface( const nsIID &iid, PyXPCOM_TypeObject *t)
417#endif
418{
419 if (mapIIDToType==NULL)
420 mapIIDToType = PyDict_New();
421
422 if (mapIIDToType) {
423 PyObject *key = Py_nsIID::PyObjectFromIID(iid);
424 if (key)
425 PyDict_SetItem(mapIIDToType, key, (PyObject *)t);
426 Py_XDECREF(key);
427 }
428}
429
430/*static */PyObject *
431Py_nsISupports::PyObjectFromInterface(nsISupports *pis,
432 const nsIID &riid,
433 PRBool bMakeNicePyObject, /* = PR_TRUE */
434 PRBool bIsInternalCall /* = PR_FALSE */)
435{
436 // Quick exit.
437 if (pis==NULL) {
438 Py_INCREF(Py_None);
439 return Py_None;
440 }
441
442 if (!bIsInternalCall) {
443#ifdef NS_DEBUG
444 nsISupports *queryResult = nsnull;
445 Py_BEGIN_ALLOW_THREADS;
446 pis->QueryInterface(riid, (void **)&queryResult);
447 Py_END_ALLOW_THREADS;
448 NS_ASSERTION(queryResult == pis, "QueryInterface needed");
449 NS_IF_RELEASE(queryResult);
450#endif
451 }
452
453#ifndef Py_LIMITED_API
454 PyTypeObject *createType = NULL;
455#else
456 PyXPCOM_TypeObject *createType = NULL;
457#endif
458 // If the IID is for nsISupports, dont bother with
459 // a map lookup as we know the type!
460 if (!riid.Equals(NS_GET_IID(nsISupports))) {
461 // Look up the map
462 PyObject *obiid = Py_nsIID::PyObjectFromIID(riid);
463 if (!obiid) return NULL;
464
465 if (mapIIDToType != NULL)
466#ifndef Py_LIMITED_API
467 createType = (PyTypeObject *)PyDict_GetItem(mapIIDToType, obiid);
468#else
469 createType = (PyXPCOM_TypeObject *)PyDict_GetItem(mapIIDToType, obiid);
470#endif
471 Py_DECREF(obiid);
472 }
473 if (createType==NULL)
474 createType = Py_nsISupports::type;
475#ifndef Py_LIMITED_API
476 // Check it is indeed one of our types.
477 if (!PyXPCOM_TypeObject::IsType(createType)) {
478 PyErr_SetString(PyExc_RuntimeError, "The type map is invalid");
479 return NULL;
480 }
481 // we can now safely cast the thing to a PyComTypeObject and use it
482 PyXPCOM_TypeObject *myCreateType = (PyXPCOM_TypeObject *)createType;
483#else /* Since the mapIIDToType is only updated by us, there should be no need for the above. */
484 PyXPCOM_TypeObject * const myCreateType = createType;
485#endif
486 if (myCreateType->ctor==NULL) {
487 PyErr_SetString(PyExc_TypeError, "The type does not declare a PyCom constructor");
488 return NULL;
489 }
490
491 Py_nsISupports *ret = (*myCreateType->ctor)(pis, riid);
492#ifdef _DEBUG_LIFETIMES
493 PyXPCOM_LogF("XPCOM Object created at 0x%0xld, nsISupports at 0x%0xld",
494 ret, ret->m_obj);
495#endif
496 if (ret && bMakeNicePyObject)
497 return MakeDefaultWrapper(ret, riid);
498 return ret;
499}
500
501// Call back into Python, passing a raw nsIInterface object, getting back
502// the object to actually pass to Python.
503PyObject *
504Py_nsISupports::MakeDefaultWrapper(PyObject *pyis,
505 const nsIID &iid)
506{
507 NS_PRECONDITION(pyis, "NULL pyobject!");
508 PyObject *obIID = NULL;
509 PyObject *args = NULL;
510 PyObject *mod = NULL;
511 PyObject *ret = NULL;
512
513 obIID = Py_nsIID::PyObjectFromIID(iid);
514 if (obIID==NULL)
515 goto done;
516
517 if (g_obFuncMakeInterfaceCount==NULL) {
518 PyObject *mod = PyImport_ImportModule("xpcom.client");
519 if (mod)
520 g_obFuncMakeInterfaceCount = PyObject_GetAttrString(mod, "MakeInterfaceResult");
521 Py_XDECREF(mod);
522 }
523 if (g_obFuncMakeInterfaceCount==NULL) goto done;
524
525 args = Py_BuildValue("OO", pyis, obIID);
526 if (args==NULL) goto done;
527 ret = PyEval_CallObject(g_obFuncMakeInterfaceCount, args);
528done:
529 if (PyErr_Occurred()) {
530 NS_ABORT_IF_FALSE(ret==NULL, "Have an error, but also a return val!");
531 PyXPCOM_LogError("Creating an interface object to be used as a result failed\n");
532 PyErr_Clear();
533 }
534 Py_XDECREF(mod);
535 Py_XDECREF(args);
536 Py_XDECREF(obIID);
537 if (ret==NULL) // eek - error - return the original with no refcount mod.
538 ret = pyis;
539 else
540 // no error - decref the old object
541 Py_DECREF(pyis);
542 // return our obISupports. If NULL, we are really hosed and nothing we can do.
543 return ret;
544}
545
546// @pymethod <o Py_nsISupports>|Py_nsISupports|QueryInterface|Queries an object for a specific interface.
547PyObject *
548Py_nsISupports::QueryInterface(PyObject *self, PyObject *args)
549{
550 PyObject *obiid;
551 int bWrap = 1;
552 // @pyparm IID|iid||The IID requested.
553 // @rdesc The result is always a <o Py_nsISupports> object.
554 // Any error (including E_NOINTERFACE) will generate a <o com_error> exception.
555 if (!PyArg_ParseTuple(args, "O|i:QueryInterface", &obiid, &bWrap))
556 return NULL;
557
558 nsIID iid;
559 if (!Py_nsIID::IIDFromPyObject(obiid, &iid))
560 return NULL;
561
562 nsISupports *pMyIS = GetI(self);
563 if (pMyIS==NULL) return NULL;
564
565 // Optimization, If we already wrap the IID, just return
566 // ourself.
567 if (!bWrap && iid.Equals(((Py_nsISupports *)self)->m_iid)) {
568 Py_INCREF(self);
569 return self;
570 }
571
572 nsCOMPtr<nsISupports> pis;
573 nsresult r;
574 Py_BEGIN_ALLOW_THREADS;
575 r = pMyIS->QueryInterface(iid, getter_AddRefs(pis));
576 Py_END_ALLOW_THREADS;
577
578 /* Note that this failure may include E_NOINTERFACE */
579 if ( NS_FAILED(r) )
580 return PyXPCOM_BuildPyException(r);
581
582 /* Return a type based on the IID (with no extra ref) */
583 return ((Py_nsISupports *)self)->MakeInterfaceResult(pis, iid, (PRBool)bWrap);
584}
585
586
587#ifdef VBOX
588static PyObject *
589QueryErrorObject(PyObject *self, PyObject *args)
590{
591 nsresult rc = 0;
592
593 if (!PyArg_ParseTuple(args, "i", &rc))
594 return NULL;
595
596 return PyXPCOM_BuildErrorMessage(rc);
597}
598#endif
599
600// @object Py_nsISupports|The base object for all PythonCOM objects. Wraps a COM nsISupports interface.
601/*static*/ struct PyMethodDef
602Py_nsISupports::methods[] =
603{
604 { "queryInterface", Py_nsISupports::QueryInterface, 1, "Queries the object for an interface."},
605 { "QueryInterface", Py_nsISupports::QueryInterface, 1, "An alias for queryInterface."},
606#ifdef VBOX
607 { "QueryErrorObject", QueryErrorObject, 1, "Query an error object for given status code."},
608#endif
609 {NULL}
610};
611
612/*static*/void Py_nsISupports::InitType(void)
613{
614 type = new PyXPCOM_TypeObject(
615 "nsISupports",
616 NULL,
617 sizeof(Py_nsISupports),
618 methods,
619 Constructor);
620}
621
622PyXPCOM_TypeObject *Py_nsISupports::type = NULL;
623PyObject *Py_nsISupports::mapIIDToType = NULL;
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use