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 |
|
---|
56 | static uint32_t cGateways = 0;
|
---|
57 | uint32_t _PyXPCOM_GetGatewayCount(void)
|
---|
58 | {
|
---|
59 | return cGateways;
|
---|
60 | }
|
---|
61 |
|
---|
62 | static char *PyXPCOM_szDefaultGatewayAttributeName = (char*)"_com_instance_default_gateway_";
|
---|
63 | PyG_Base *GetDefaultGateway(PyObject *instance);
|
---|
64 | void AddDefaultGateway(PyObject *instance, nsISupports *gateway);
|
---|
65 | PRBool CheckDefaultGateway(PyObject *real_inst, REFNSIID iid, nsISupports **ret_gateway);
|
---|
66 |
|
---|
67 | /*static*/ nsresult
|
---|
68 | PyG_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 |
|
---|
92 | PyG_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 |
|
---|
158 | PyG_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.
|
---|
182 | void *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
|
---|
197 | PyG_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 | }
|
---|
238 | done:
|
---|
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.
|
---|
259 | PyObject *
|
---|
260 | PyG_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);
|
---|
321 | done:
|
---|
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 |
|
---|
338 | NS_IMETHODIMP
|
---|
339 | PyG_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 |
|
---|
419 | nsrefcnt
|
---|
420 | PyG_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 |
|
---|
431 | nsrefcnt
|
---|
432 | PyG_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 |
|
---|
444 | NS_IMETHODIMP
|
---|
445 | PyG_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 |
|
---|
468 | nsresult 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 |
|
---|
525 | static 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;
|
---|
575 | done:
|
---|
576 | Py_XDECREF(method);
|
---|
577 | Py_XDECREF(real_ob);
|
---|
578 | Py_XDECREF(args);
|
---|
579 | return ret;
|
---|
580 | }
|
---|
581 |
|
---|
582 |
|
---|
583 | nsresult 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 |
|
---|
604 | nsresult 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 |
|
---|
623 | nsresult 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 |
|
---|
666 | done:
|
---|
667 | Py_XDECREF(real_ob);
|
---|
668 | return ret;
|
---|
669 | }
|
---|
670 |
|
---|
671 | nsresult 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);
|
---|
713 | done:
|
---|
714 | Py_XDECREF(real_ob);
|
---|
715 | return ret;
|
---|
716 | }
|
---|
717 |
|
---|
718 | // Get at the underlying Python object.
|
---|
719 | PyObject *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 |
|
---|
761 | PyG_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 |
|
---|
789 | PRBool 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 |
|
---|
824 | void 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 | }
|
---|