VirtualBox

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

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

xpcom/python: It's either PyObject_Init or _Py_NewReference, we should skip the latter even for older releases. bugref:10079

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