VirtualBox

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

Last change on this file was 103176, checked in by vboxsync, 3 months ago

libs/xpcom/python: Some cleanup, bugref:3409

  • Property svn:eol-style set to native
File size: 28.4 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// PyGBase.cpp - implementation of the PyG_Base class
39//
40// This code is part of the XPCOM extensions for Python.
41//
42// Written May 2000 by Mark Hammond.
43//
44// Based heavily on the Python COM support, which is
45// (c) Mark Hammond and Greg Stein.
46//
47// (c) 2000, ActiveState corp.
48
49#include "PyXPCOM_std.h"
50#include <nsIModule.h>
51#include <nsIComponentLoader.h>
52#include <nsIInputStream.h>
53
54#include <iprt/asm.h>
55
56static uint32_t cGateways = 0;
57uint32_t _PyXPCOM_GetGatewayCount(void)
58{
59 return cGateways;
60}
61
62static char *PyXPCOM_szDefaultGatewayAttributeName = (char*)"_com_instance_default_gateway_";
63PyG_Base *GetDefaultGateway(PyObject *instance);
64void AddDefaultGateway(PyObject *instance, nsISupports *gateway);
65PRBool CheckDefaultGateway(PyObject *real_inst, REFNSIID iid, nsISupports **ret_gateway);
66
67/*static*/ nsresult
68PyG_Base::CreateNew(PyObject *pPyInstance, const nsIID &iid, void **ppResult)
69{
70 NS_PRECONDITION(ppResult && *ppResult==NULL, "NULL or uninitialized pointer");
71 if (ppResult==nsnull)
72 return NS_ERROR_NULL_POINTER;
73
74 PyG_Base *ret;
75 // Hack for few extra gateways we support.
76 if (iid.Equals(NS_GET_IID(nsIModule)))
77 ret = MakePyG_nsIModule(pPyInstance);
78 else if (iid.Equals(NS_GET_IID(nsIComponentLoader)))
79 ret = MakePyG_nsIComponentLoader(pPyInstance);
80 else if (iid.Equals(NS_GET_IID(nsIInputStream)))
81 ret = MakePyG_nsIInputStream(pPyInstance);
82 else
83 ret = new PyXPCOM_XPTStub(pPyInstance, iid);
84 if (ret==nsnull)
85 return NS_ERROR_OUT_OF_MEMORY;
86 ret->AddRef(); // The first reference for the caller.
87 *ppResult = ret->ThisAsIID(iid);
88 NS_ABORT_IF_FALSE(*ppResult != NULL, "ThisAsIID() gave NULL, but we know it supports it!");
89 return *ppResult ? NS_OK : NS_ERROR_FAILURE;
90}
91
92PyG_Base::PyG_Base(PyObject *instance, const nsIID &iid)
93{
94 // Note that "instance" is the _policy_ instance!!
95 ASMAtomicIncU32(&cGateways);
96 m_pBaseObject = GetDefaultGateway(instance);
97 // m_pWeakRef is an nsCOMPtr and needs no init.
98
99 NS_ABORT_IF_FALSE(!(iid.Equals(NS_GET_IID(nsISupportsWeakReference)) || iid.Equals(NS_GET_IID(nsIWeakReference))),"Should not be creating gateways with weak-ref interfaces");
100 m_iid = iid;
101 m_pPyObject = instance;
102 NS_PRECONDITION(instance, "NULL PyObject for PyXPCOM_XPTStub!");
103
104#ifdef NS_BUILD_REFCNT_LOGGING
105 // If XPCOM reference count logging is enabled, then allow us to give the Python class.
106 PyObject *realInstance = PyObject_GetAttrString(instance, "_obj_");
107 PyObject *r = PyObject_Repr(realInstance);
108 const char *szRepr;
109 if (r==NULL) {
110 PyXPCOM_LogError("Getting the __repr__ of the object failed");
111 PyErr_Clear();
112 szRepr = "(repr failed!)";
113 }
114 else
115#if PY_MAJOR_VERSION <= 2
116 szRepr = PyString_AsString(r);
117#else
118 szRepr = PyUnicode_AsUTF8(r);
119#endif
120 if (szRepr==NULL) szRepr = "";
121 int reprOffset = *szRepr=='<' ? 1 : 0;
122 static const char *reprPrefix = "component:";
123 if (strncmp(reprPrefix, szRepr+reprOffset, strlen(reprPrefix)) == 0)
124 reprOffset += strlen(reprPrefix);
125 strncpy(refcntLogRepr, szRepr + reprOffset, sizeof(refcntLogRepr)-1);
126 refcntLogRepr[sizeof(refcntLogRepr)-1] = '\0';
127 // See if we should get rid of the " at 0x12345" portion.
128 char *lastPos = strstr(refcntLogRepr, " at ");
129 if (lastPos) *lastPos = '\0';
130 Py_XDECREF(realInstance);
131 Py_XDECREF(r);
132#endif // NS_BUILD_REFCNT_LOGGING
133
134#ifdef DEBUG_LIFETIMES
135 {
136 char *iid_repr;
137 nsCOMPtr<nsIInterfaceInfoManager> iim = XPTI_GetInterfaceInfoManager();
138 if (iim!=nsnull)
139 iim->GetNameForIID(&iid, &iid_repr);
140 PyObject *real_instance = PyObject_GetAttrString(instance, "_obj_");
141 PyObject *real_repr = PyObject_Repr(real_instance);
142
143 PYXPCOM_LOG_DEBUG("PyG_Base created at %p\n instance_repr=%s\n IID=%s\n", this, PyString_AsString(real_repr), iid_repr);
144 nsMemory::Free(iid_repr);
145 Py_XDECREF(real_instance);
146 Py_XDECREF(real_repr);
147 }
148#endif // DEBUG_LIFETIMES
149 Py_XINCREF(instance); // instance should never be NULL - but whats an X between friends!
150
151 PyXPCOM_DLLAddRef();
152
153#ifdef DEBUG_FULL
154 LogF("PyGatewayBase: created %s", m_pPyObject ? PyXPCOM_ObTypeName(m_pPyObject) : "<NULL>");
155#endif
156}
157
158PyG_Base::~PyG_Base()
159{
160 ASMAtomicDecU32(&cGateways);
161#ifdef DEBUG_LIFETIMES
162 PYXPCOM_LOG_DEBUG("PyG_Base: deleted %p", this);
163#endif
164 if ( m_pPyObject ) {
165 CEnterLeavePython celp;
166 Py_DECREF(m_pPyObject);
167 }
168 if (m_pBaseObject)
169 m_pBaseObject->Release();
170 if (m_pWeakRef) {
171 // Need to ensure some other thread isnt doing a QueryReferent on
172 // our weak reference at the same time
173 CEnterLeaveXPCOMFramework _celf;
174 PyXPCOM_GatewayWeakReference *p = (PyXPCOM_GatewayWeakReference *)(nsISupports *)m_pWeakRef;
175 p->m_pBase = nsnull;
176 m_pWeakRef = nsnull;
177 }
178 PyXPCOM_DLLRelease();
179}
180
181// Get the correct interface pointer for this object given the IID.
182void *PyG_Base::ThisAsIID( const nsIID &iid )
183{
184 if (this==NULL) return NULL;
185 if (iid.Equals(NS_GET_IID(nsISupports)))
186 return (nsISupports *)(nsIInternalPython *)this;
187 if (iid.Equals(NS_GET_IID(nsISupportsWeakReference)))
188 return (nsISupportsWeakReference *)this;
189 if (iid.Equals(NS_GET_IID(nsIInternalPython)))
190 return (nsISupports *)(nsIInternalPython *)this;
191 return NULL;
192}
193
194// Call back into Python, passing a Python instance, and get back
195// an interface object that wraps the instance.
196/*static*/ PRBool
197PyG_Base::AutoWrapPythonInstance(PyObject *ob, const nsIID &iid, nsISupports **ppret)
198{
199 NS_PRECONDITION(ppret!=NULL, "null pointer when wrapping a Python instance!");
200 NS_PRECONDITION(ob && PyObject_HasAttrString(ob, "__class__"),
201 "AutoWrapPythonInstance is expecting an non-NULL instance!");
202 PRBool ok = PR_FALSE;
203 // XXX - todo - this static object leaks! (but Python on Windows leaks 2000+ objects as it is ;-)
204 static PyObject *func = NULL; // fetch this once and remember!
205 PyObject *obIID = NULL;
206 PyObject *wrap_ret = NULL;
207 PyObject *args = NULL;
208 if (func==NULL) { // not thread-safe, but nothing bad can happen, except an extra reference leak
209 PyObject *mod = PyImport_ImportModule("xpcom.server");
210 if (mod)
211 func = PyObject_GetAttrString(mod, "WrapObject");
212 Py_XDECREF(mod);
213 if (func==NULL) goto done;
214 }
215 // See if the instance has previously been wrapped.
216 if (CheckDefaultGateway(ob, iid, ppret)) {
217 ok = PR_TRUE; // life is good!
218 } else {
219 PyErr_Clear();
220
221 obIID = Py_nsIID::PyObjectFromIID(iid);
222 if (obIID==NULL) goto done;
223 args = Py_BuildValue("OOzi", ob, obIID, NULL, 0);
224 if (args==NULL) goto done;
225 wrap_ret = PyEval_CallObject(func, args);
226 if (wrap_ret==NULL) goto done;
227 ok = Py_nsISupports::InterfaceFromPyObject(wrap_ret, iid, ppret, PR_FALSE, PR_FALSE);
228#ifdef DEBUG
229 if (ok)
230 // Check we _now_ have a default gateway
231 {
232 nsISupports *temp = NULL;
233 NS_ABORT_IF_FALSE(CheckDefaultGateway(ob, iid, &temp), "Auto-wrapped object didnt get a default gateway!");
234 if (temp) temp->Release();
235 }
236#endif
237 }
238done:
239// Py_XDECREF(func); -- func is static for performance reasons.
240 Py_XDECREF(obIID);
241 Py_XDECREF(wrap_ret);
242 Py_XDECREF(args);
243 return ok;
244}
245
246// Call back into Python, passing a raw nsIInterface object, getting back
247// the object to actually use as the gateway parameter for this interface.
248// For example, it is expected that the policy will wrap the interface
249// object in one of the xpcom.client.Interface objects, allowing
250// natural usage of the interface from Python clients.
251// Note that piid will usually be NULL - this is because the runtime
252// reflection interfaces dont provide this information to me.
253// In this case, the Python code may choose to lookup the complete
254// interface info to obtain the IID.
255// It is expected (but should not be assumed) that the method info
256// or the IID will be NULL.
257// Worst case, the code should provide a wrapper for the nsiSupports interface,
258// so at least the user can simply QI the object.
259PyObject *
260PyG_Base::MakeInterfaceParam(nsISupports *pis,
261 const nsIID *piid,
262 int methodIndex /* = -1 */,
263 const XPTParamDescriptor *d /* = NULL */,
264 int paramIndex /* = -1 */)
265{
266 if (pis==NULL) {
267 Py_INCREF(Py_None);
268 return Py_None;
269 }
270 // This condition is true today, but not necessarily so.
271 // But if it ever triggers, the poor Python code has no real hope
272 // of returning something useful, so we should at least do our
273 // best to provide the useful data.
274 NS_WARN_IF_FALSE( ((piid != NULL) ^ (d != NULL)) == 1, "No information on the interface available - Python's gunna have a hard time doing much with it!");
275 PyObject *obIID = NULL;
276 PyObject *obISupports = NULL;
277 PyObject *obParamDesc = NULL;
278 PyObject *result = NULL;
279
280 // get the basic interface first, as if we fail, we can try and use this.
281 // If we don't know the IID, we must explicitly query for nsISupports.
282 nsCOMPtr<nsISupports> piswrap;
283 nsIID iid_check;
284 if (piid) {
285 iid_check = *piid;
286 piswrap = pis;
287 } else {
288 /* HACK ALERT! Dropping the python interpreter lock here while
289 doing QueryInterface because it may involve IPC to a python
290 object in the same interpreter and deadlock. Not at all
291 sure if this is a good idea or not for the internal PyXPCOM
292 state, but it might fix the deadloock... Hoping for the best. */
293 Py_BEGIN_ALLOW_THREADS;
294 iid_check = NS_GET_IID(nsISupports);
295 pis->QueryInterface(iid_check, getter_AddRefs(piswrap));
296 Py_END_ALLOW_THREADS;
297 }
298
299 obISupports = Py_nsISupports::PyObjectFromInterface(piswrap, iid_check, PR_FALSE);
300 if (!obISupports)
301 goto done;
302 if (piid==NULL) {
303 obIID = Py_None;
304 Py_INCREF(Py_None);
305 } else
306 obIID = Py_nsIID::PyObjectFromIID(*piid);
307 if (obIID==NULL)
308 goto done;
309 obParamDesc = PyObject_FromXPTParamDescriptor(d);
310 if (obParamDesc==NULL)
311 goto done;
312
313 result = PyObject_CallMethod(m_pPyObject,
314 (char*)"_MakeInterfaceParam_",
315 (char*)"OOiOi",
316 obISupports,
317 obIID,
318 methodIndex,
319 obParamDesc,
320 paramIndex);
321done:
322 if (PyErr_Occurred()) {
323 NS_WARN_IF_FALSE(result==NULL, "Have an error, but also a result!");
324 PyXPCOM_LogError("Wrapping an interface object for the gateway failed\n");
325 }
326 Py_XDECREF(obIID);
327 Py_XDECREF(obParamDesc);
328 if (result==NULL) { // we had an error.
329 PyErr_Clear(); // but are not reporting it back to Python itself!
330 // return our obISupports. If NULL, we are really hosed and nothing we can do.
331 return obISupports;
332 }
333 // Dont need to return this - we have a better result.
334 Py_XDECREF(obISupports);
335 return result;
336}
337
338NS_IMETHODIMP
339PyG_Base::QueryInterface(REFNSIID iid, void** ppv)
340{
341#ifdef PYXPCOM_DEBUG_FULL
342 {
343 char *sziid = iid.ToString();
344 LogF("PyGatewayBase::QueryInterface: %s", sziid);
345 Allocator::Free(sziid);
346 }
347#endif
348 NS_PRECONDITION(ppv, "NULL pointer");
349 if (ppv==nsnull)
350 return NS_ERROR_NULL_POINTER;
351 *ppv = nsnull;
352 // If one of our native interfaces (but NOT nsISupports if we have a base)
353 // return this.
354 // It is important is that nsISupports come from the base object
355 // to ensure that we live by XPCOM identity rules (other interfaces need
356 // not abide by this rule - only nsISupports.)
357 if ( (m_pBaseObject==NULL || !iid.Equals(NS_GET_IID(nsISupports)))
358 && (*ppv=ThisAsIID(iid)) != NULL ) {
359 AddRef();
360 return NS_OK;
361 }
362 // If we have a "base object", then we need to delegate _every_ remaining
363 // QI to it.
364 if (m_pBaseObject != NULL)
365 return m_pBaseObject->QueryInterface(iid, ppv);
366
367 // Call the Python policy to see if it (says it) supports the interface
368 PRBool supports = PR_FALSE;
369 { // temp scope for Python lock
370 CEnterLeavePython celp;
371
372 PyObject * ob = Py_nsIID::PyObjectFromIID(iid);
373 // must say this is an 'internal' call, else we recurse QI into
374 // oblivion.
375 PyObject * this_interface_ob = Py_nsISupports::PyObjectFromInterface(
376 (nsXPTCStubBase *)this,
377 iid, PR_FALSE, PR_TRUE);
378 if ( !ob || !this_interface_ob) {
379 Py_XDECREF(ob);
380 Py_XDECREF(this_interface_ob);
381 return NS_ERROR_OUT_OF_MEMORY;
382 }
383
384 PyObject *result = PyObject_CallMethod(m_pPyObject, (char*)"_QueryInterface_",
385 (char*)"OO",
386 this_interface_ob, ob);
387 Py_DECREF(ob);
388 Py_DECREF(this_interface_ob);
389
390 if ( result ) {
391 if (Py_nsISupports::InterfaceFromPyObject(result, iid, (nsISupports **)ppv, PR_TRUE)) {
392 // If OK, but NULL, _QI_ returned None, which simply means
393 // "no such interface"
394 supports = (*ppv!=NULL);
395 // result has been QI'd and AddRef'd all ready for return.
396 } else {
397 // Dump this message and any Python exception before
398 // reporting the fact that QI failed - this error
399 // may provide clues!
400 PyXPCOM_LogError("The _QueryInterface_ method returned an object of type '%s', but an interface was expected\n", PyXPCOM_ObTypeName(result));
401 // supports remains false
402 }
403 Py_DECREF(result);
404 } else {
405 NS_ABORT_IF_FALSE(PyErr_Occurred(), "Got NULL result, but no Python error flagged!");
406 NS_WARN_IF_FALSE(!supports, "Have failure with success flag set!");
407 PyXPCOM_LogError("The _QueryInterface_ processing failed.\n");
408 // supports remains false.
409 // We have reported the error, and are returning to COM,
410 // so we should clear it.
411 PyErr_Clear();
412 }
413 } // end of temp scope for Python lock - lock released here!
414 if ( !supports )
415 return NS_ERROR_NO_INTERFACE;
416 return NS_OK;
417}
418
419nsrefcnt
420PyG_Base::AddRef(void)
421{
422 nsrefcnt cnt = (nsrefcnt) ASMAtomicIncS32((volatile int32_t*)&mRefCnt);
423#ifdef NS_BUILD_REFCNT_LOGGING
424 // If we have no pBaseObject, then we need to ignore them
425 if (m_pBaseObject == NULL)
426 NS_LOG_ADDREF(this, cnt, refcntLogRepr, sizeof(*this));
427#endif
428 return cnt;
429}
430
431nsrefcnt
432PyG_Base::Release(void)
433{
434 nsrefcnt cnt = (nsrefcnt) ASMAtomicDecS32((volatile int32_t*)&mRefCnt);
435#ifdef NS_BUILD_REFCNT_LOGGING
436 if (m_pBaseObject == NULL)
437 NS_LOG_RELEASE(this, cnt, refcntLogRepr);
438#endif
439 if ( cnt == 0 )
440 delete this;
441 return cnt;
442}
443
444NS_IMETHODIMP
445PyG_Base::GetWeakReference(nsIWeakReference **ret)
446{
447 // always delegate back to the "base" gateway for the object, as this tear-off
448 // interface may not live as long as the base. So we recurse back to the base.
449 if (m_pBaseObject) {
450 NS_PRECONDITION(m_pWeakRef == nsnull, "Not a base object, but do have a weak-ref!");
451 return m_pBaseObject->GetWeakReference(ret);
452 }
453 NS_PRECONDITION(ret, "null pointer");
454 if (ret==nsnull) return NS_ERROR_INVALID_POINTER;
455 if (!m_pWeakRef) {
456 // First query for a weak reference - create it.
457 // XXX - this looks like it needs thread safety!?
458 m_pWeakRef = new PyXPCOM_GatewayWeakReference(this);
459 NS_ABORT_IF_FALSE(m_pWeakRef, "Shouldn't be able to fail creating a weak reference!");
460 if (!m_pWeakRef)
461 return NS_ERROR_UNEXPECTED;
462 }
463 *ret = m_pWeakRef;
464 (*ret)->AddRef();
465 return NS_OK;
466}
467
468nsresult PyG_Base::HandleNativeGatewayError(const char *szMethodName)
469{
470 nsresult rc = NS_OK;
471 if (PyErr_Occurred()) {
472 // The error handling - fairly involved, but worth it as
473 // good error reporting is critical for users to know WTF
474 // is going on - especially with TypeErrors etc in their
475 // return values (ie, after the Python code has successfully
476 // exited, but we encountered errors unpacking their
477 // result values for the COM caller - there is literally no
478 // way to catch these exceptions from Python code, as their
479 // is no Python function directly on the call-stack)
480
481 // First line of attack in an error is to call-back on the policy.
482 // If the callback of the error handler succeeds and returns an
483 // integer (for the nsresult), we take no further action.
484
485 // If this callback fails, we log _2_ exceptions - the error
486 // handler error, and the original error.
487
488 PRBool bProcessMainError = PR_TRUE; // set to false if our exception handler does its thing!
489 PyObject *exc_typ, *exc_val, *exc_tb;
490 PyErr_Fetch(&exc_typ, &exc_val, &exc_tb);
491
492 PyObject *err_result = PyObject_CallMethod(m_pPyObject,
493 (char*)"_GatewayException_",
494 (char*)"z(OOO)",
495 szMethodName,
496 exc_typ ? exc_typ : Py_None, // should never be NULL, but defensive programming...
497 exc_val ? exc_val : Py_None, // may well be NULL.
498 exc_tb ? exc_tb : Py_None); // may well be NULL.
499 if (err_result == NULL) {
500 PyXPCOM_LogError("The exception handler _CallMethodException_ failed!\n");
501 } else if (err_result == Py_None) {
502 // The exception handler has chosen not to do anything with
503 // this error, so we still need to print it!
504 ;
505 } else if (PyInt_Check(err_result)) {
506 // The exception handler has given us the nresult.
507 rc = PyInt_AsLong(err_result);
508 bProcessMainError = PR_FALSE;
509 } else {
510 // The exception handler succeeded, but returned other than
511 // int or None.
512 PyXPCOM_LogError("The _CallMethodException_ handler returned object of type '%s' - None or an integer expected\n", PyXPCOM_ObTypeName(err_result));
513 }
514 Py_XDECREF(err_result);
515 PyErr_Restore(exc_typ, exc_val, exc_tb);
516 if (bProcessMainError) {
517 PyXPCOM_LogError("The function '%s' failed\n", szMethodName);
518 rc = PyXPCOM_SetCOMErrorFromPyException();
519 }
520 PyErr_Clear();
521 }
522 return rc;
523}
524
525static nsresult do_dispatch(
526 PyObject *pPyObject,
527 PyObject **ppResult,
528 const char *szMethodName,
529 const char *szFormat,
530 va_list va
531 )
532{
533 NS_PRECONDITION(ppResult, "Must provide a result buffer");
534 *ppResult = nsnull;
535 // Build the Invoke arguments...
536 PyObject *args = NULL;
537 PyObject *method = NULL;
538 PyObject *real_ob = NULL;
539 nsresult ret = NS_ERROR_FAILURE;
540 if ( szFormat )
541 args = Py_VaBuildValue((char *)szFormat, va);
542 else
543 args = PyTuple_New(0);
544 if ( !args )
545 goto done;
546
547 // make sure a tuple.
548 if ( !PyTuple_Check(args) ) {
549 PyObject *a = PyTuple_New(1);
550 if ( a == NULL )
551 {
552 Py_DECREF(args);
553 goto done;
554 }
555 PyTuple_SET_ITEM(a, 0, args);
556 args = a;
557 }
558 // Bit to a hack here to maintain the use of a policy.
559 // We actually get the policies underlying object
560 // to make the call on.
561 real_ob = PyObject_GetAttrString(pPyObject, "_obj_");
562 if (real_ob == NULL) {
563 PyErr_Format(PyExc_AttributeError, "The policy object does not have an '_obj_' attribute.");
564 goto done;
565 }
566 method = PyObject_GetAttrString(real_ob, (char *)szMethodName);
567 if ( !method ) {
568 PyErr_Clear();
569 ret = NS_PYXPCOM_NO_SUCH_METHOD;
570 goto done;
571 }
572 // Make the call
573 *ppResult = PyEval_CallObject(method, args);
574 ret = *ppResult ? NS_OK : NS_ERROR_FAILURE;
575done:
576 Py_XDECREF(method);
577 Py_XDECREF(real_ob);
578 Py_XDECREF(args);
579 return ret;
580}
581
582
583nsresult PyG_Base::InvokeNativeViaPolicyInternal(
584 const char *szMethodName,
585 PyObject **ppResult,
586 const char *szFormat,
587 va_list va
588 )
589{
590 if ( m_pPyObject == NULL || szMethodName == NULL )
591 return NS_ERROR_NULL_POINTER;
592
593 PyObject *temp = nsnull;
594 if (ppResult == nsnull)
595 ppResult = &temp;
596 nsresult nr = do_dispatch(m_pPyObject, ppResult, szMethodName, szFormat, va);
597
598 // If temp is NULL, they provided a buffer, and we dont touch it.
599 // If not NULL, *ppResult = temp, and _we_ do own it.
600 Py_XDECREF(temp);
601 return nr;
602}
603
604nsresult PyG_Base::InvokeNativeViaPolicy(
605 const char *szMethodName,
606 PyObject **ppResult /* = NULL */,
607 const char *szFormat /* = NULL */,
608 ...
609 )
610{
611 va_list va;
612 va_start(va, szFormat);
613 nsresult nr = InvokeNativeViaPolicyInternal(szMethodName, ppResult, szFormat, va);
614 va_end(va);
615
616 if (nr == NS_PYXPCOM_NO_SUCH_METHOD) {
617 // Only problem was missing method.
618 PyErr_Format(PyExc_AttributeError, "The object does not have a '%s' function.", szMethodName);
619 }
620 return nr == NS_OK ? NS_OK : HandleNativeGatewayError(szMethodName);
621}
622
623nsresult PyG_Base::InvokeNativeGetViaPolicy(
624 const char *szPropertyName,
625 PyObject **ppResult /* = NULL */
626 )
627{
628 PyObject *ob_ret = NULL;
629 nsresult ret = NS_OK;
630 PyObject *real_ob = NULL;
631 if ( m_pPyObject == NULL || szPropertyName == NULL )
632 return NS_ERROR_NULL_POINTER;
633 // First see if we have a method of that name.
634 char buf[256];
635 strcpy(buf, "get_");
636 strncat(buf, szPropertyName, sizeof(buf)*sizeof(buf[0])-strlen(buf)-1);
637 buf[sizeof(buf)/sizeof(buf[0])-1] = '\0';
638 ret = InvokeNativeViaPolicyInternal(buf, ppResult, nsnull, nsnull);
639 if (ret == NS_PYXPCOM_NO_SUCH_METHOD) {
640 // No method of that name - just try a property.
641 // Bit to a hack here to maintain the use of a policy.
642 // We actually get the policies underlying object
643 // to make the call on.
644 real_ob = PyObject_GetAttrString(m_pPyObject, "_obj_");
645 if (real_ob == NULL) {
646 PyErr_Format(PyExc_AttributeError, "The policy object does not have an '_obj_' attribute.");
647 ret = HandleNativeGatewayError(szPropertyName);
648 goto done;
649 }
650 ob_ret = PyObject_GetAttrString(real_ob, (char *)szPropertyName);
651 if (ob_ret==NULL) {
652 PyErr_Format(PyExc_AttributeError,
653 "The object does not have a 'get_%s' function, or a '%s attribute.",
654 szPropertyName, szPropertyName);
655 } else {
656 ret = NS_OK;
657 if (ppResult)
658 *ppResult = ob_ret;
659 else
660 Py_XDECREF(ob_ret);
661 }
662 }
663 if (ret != NS_OK)
664 ret = HandleNativeGatewayError(szPropertyName);
665
666done:
667 Py_XDECREF(real_ob);
668 return ret;
669}
670
671nsresult PyG_Base::InvokeNativeSetViaPolicy(
672 const char *szPropertyName,
673 ...
674 )
675{
676 if ( m_pPyObject == NULL || szPropertyName == NULL )
677 return NS_ERROR_NULL_POINTER;
678 nsresult ret = NS_OK;
679 PyObject *real_ob = NULL;
680 char buf[256];
681 strcpy(buf, "set_");
682 strncat(buf, szPropertyName, sizeof(buf)*sizeof(buf[0])-strlen(buf)-1);
683 buf[sizeof(buf)/sizeof(buf[0])-1] = '\0';
684 va_list va;
685 va_start(va, szPropertyName);
686 ret = InvokeNativeViaPolicyInternal(buf, NULL, "O", va);
687 va_end(va);
688 if (ret == NS_PYXPCOM_NO_SUCH_METHOD) {
689 // No method of that name - just try a property.
690 // Bit to a hack here to maintain the use of a policy.
691 // We actually get the policies underlying object
692 // to make the call on.
693 real_ob = PyObject_GetAttrString(m_pPyObject, "_obj_");
694 if (real_ob == NULL) {
695 PyErr_Format(PyExc_AttributeError, "The policy object does not have an '_obj_' attribute.");
696 ret = HandleNativeGatewayError(szPropertyName);
697 goto done;
698 }
699 va_list va2;
700 va_start(va2, szPropertyName);
701 PyObject *arg = va_arg( va2, PyObject *);
702 va_end(va2);
703 if (PyObject_SetAttrString(real_ob, (char *)szPropertyName, arg) == 0)
704 ret = NS_OK;
705 else {
706 PyErr_Format(PyExc_AttributeError,
707 "The object does not have a 'set_%s' function, or a '%s attribute.",
708 szPropertyName, szPropertyName);
709 }
710 }
711 if (ret != NS_OK)
712 ret = HandleNativeGatewayError(szPropertyName);
713done:
714 Py_XDECREF(real_ob);
715 return ret;
716}
717
718// Get at the underlying Python object.
719PyObject *PyG_Base::UnwrapPythonObject(void)
720{
721 Py_INCREF(m_pPyObject);
722 return m_pPyObject;
723}
724/******************************************************
725
726 Some special support to help with object identity.
727
728 In the simplest case, assume a Python XPCOM object is
729 supporting a function "nsIWhatever GetWhatever()",
730 so implements it as:
731 return self
732 it is almost certain they intend returning
733 the same COM OBJECT to the caller! Thus, if a user of this COM
734 object does:
735
736 p1 = foo.GetWhatever();
737 p2 = foo.GetWhatever();
738
739 We almost certainly expect p1==p2==foo.
740
741 We previously _did_ have special support for the "self"
742 example above, but this implements a generic scheme that
743 works for _all_ objects.
744
745 Whenever we are asked to "AutoWrap" a Python object, the
746 first thing we do is see if it has been auto-wrapped before.
747
748 If not, we create a new wrapper, then make a COM weak reference
749 to that wrapper, and store it directly back into the instance
750 we are auto-wrapping! The use of a weak-reference prevents
751 cycles.
752
753 The existance of this attribute in an instance indicates if it
754 has been previously auto-wrapped.
755
756 If it _has_ previously been auto-wrapped, we de-reference the
757 weak reference, and use that gateway.
758
759*********************************************************************/
760
761PyG_Base *GetDefaultGateway(PyObject *policy)
762{
763 // NOTE: Instance is the policy, not the real instance
764 PyObject *instance = PyObject_GetAttrString(policy, "_obj_");
765 if (instance == nsnull)
766 return nsnull;
767 PyObject *ob_existing_weak = PyObject_GetAttrString(instance, PyXPCOM_szDefaultGatewayAttributeName);
768 Py_DECREF(instance);
769 if (ob_existing_weak != NULL) {
770 PRBool ok = PR_TRUE;
771 nsCOMPtr<nsIWeakReference> pWeakRef;
772 ok = NS_SUCCEEDED(Py_nsISupports::InterfaceFromPyObject(ob_existing_weak,
773 NS_GET_IID(nsIWeakReference),
774 getter_AddRefs(pWeakRef),
775 PR_FALSE));
776 Py_DECREF(ob_existing_weak);
777 nsISupports *pip;
778 if (ok) {
779 nsresult nr = pWeakRef->QueryReferent( NS_GET_IID(nsIInternalPython), (void **)&pip);
780 if (NS_FAILED(nr))
781 return nsnull;
782 return (PyG_Base *)(nsIInternalPython *)pip;
783 }
784 } else
785 PyErr_Clear();
786 return nsnull;
787}
788
789PRBool CheckDefaultGateway(PyObject *real_inst, REFNSIID iid, nsISupports **ret_gateway)
790{
791 NS_ABORT_IF_FALSE(real_inst, "Did not have an _obj_ attribute");
792 if (real_inst==NULL) {
793 PyErr_Clear();
794 return PR_FALSE;
795 }
796 PyObject *ob_existing_weak = PyObject_GetAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName);
797 if (ob_existing_weak != NULL) {
798 // We have an existing default, but as it is a weak reference, it
799 // may no longer be valid. Check it.
800 PRBool ok = PR_TRUE;
801 nsCOMPtr<nsIWeakReference> pWeakRef;
802 ok = NS_SUCCEEDED(Py_nsISupports::InterfaceFromPyObject(ob_existing_weak,
803 NS_GET_IID(nsIWeakReference),
804 getter_AddRefs(pWeakRef),
805 PR_FALSE));
806 Py_DECREF(ob_existing_weak);
807 if (ok) {
808 Py_BEGIN_ALLOW_THREADS;
809 ok = NS_SUCCEEDED(pWeakRef->QueryReferent( iid, (void **)(ret_gateway)));
810 Py_END_ALLOW_THREADS;
811 }
812 if (!ok) {
813 // We have the attribute, but not valid - wipe it
814 // before restoring it.
815 if (0 != PyObject_DelAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName))
816 PyErr_Clear();
817 }
818 return ok;
819 }
820 PyErr_Clear();
821 return PR_FALSE;
822}
823
824void AddDefaultGateway(PyObject *instance, nsISupports *gateway)
825{
826 // NOTE: Instance is the _policy_!
827 PyObject *real_inst = PyObject_GetAttrString(instance, "_obj_");
828 NS_ABORT_IF_FALSE(real_inst, "Could not get the '_obj_' element");
829 if (!real_inst) return;
830 if (!PyObject_HasAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName)) {
831 nsCOMPtr<nsISupportsWeakReference> swr( do_QueryInterface((nsISupportsWeakReference *)(gateway)) );
832 NS_ABORT_IF_FALSE(swr, "Our gateway failed with a weak reference query");
833 // Create the new default gateway - get a weak reference for our gateway.
834 if (swr) {
835 nsCOMPtr<nsIWeakReference> pWeakReference;
836 swr->GetWeakReference( getter_AddRefs(pWeakReference) );
837 if (pWeakReference) {
838 PyObject *ob_new_weak = Py_nsISupports::PyObjectFromInterface(pWeakReference,
839 NS_GET_IID(nsIWeakReference),
840 PR_FALSE ); /* bMakeNicePyObject */
841 // pWeakReference reference consumed.
842 if (ob_new_weak) {
843 PyObject_SetAttrString(real_inst, PyXPCOM_szDefaultGatewayAttributeName, ob_new_weak);
844 Py_DECREF(ob_new_weak);
845 }
846 }
847 }
848 }
849 Py_DECREF(real_inst);
850}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use