VirtualBox

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

Last change on this file since 90631 was 90631, checked in by vboxsync, 4 years ago

libs/XPCOM: Just use PyObject_Init() everywhere (also for Python < 3.9) instead of _Py_NewReference(). bugref:10079

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

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