diff options
Diffstat (limited to 'src/libs/xpcom18a4/python/src/PyXPCOM.h')
-rw-r--r-- | src/libs/xpcom18a4/python/src/PyXPCOM.h | 1036 |
1 files changed, 1036 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/python/src/PyXPCOM.h b/src/libs/xpcom18a4/python/src/PyXPCOM.h new file mode 100644 index 00000000..91bc8d12 --- /dev/null +++ b/src/libs/xpcom18a4/python/src/PyXPCOM.h @@ -0,0 +1,1036 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Python XPCOM language bindings. + * + * The Initial Developer of the Original Code is + * ActiveState Tool Corp. + * Portions created by the Initial Developer are Copyright (C) 2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Hammond <mhammond@skippinet.com.au> (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// PyXPCOM.h - the main header file for the Python XPCOM support. +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#ifndef __PYXPCOM_H__ +#define __PYXPCOM_H__ + +#include "nsIAllocator.h" +#include "nsIWeakReference.h" +#include "nsIInterfaceInfoManager.h" +#include "nsIClassInfo.h" +#include "nsIComponentManager.h" +#include "nsIComponentManagerObsolete.h" +#include "nsIServiceManager.h" +#include "nsIInputStream.h" +#include "nsIVariant.h" +#include "nsIModule.h" + +#include "nsXPIDLString.h" +#include "nsCRT.h" +#include "xptcall.h" +#include "xpt_xdr.h" + +#ifdef VBOX_DEBUG_LIFETIMES +# include <iprt/critsect.h> +# include <iprt/list.h> +# include <iprt/once.h> +#endif + +#ifdef HAVE_LONG_LONG + // Mozilla also defines this - we undefine it to + // prevent a compiler warning. +# undef HAVE_LONG_LONG +#endif // HAVE_LONG_LONG + +#ifdef _POSIX_C_SOURCE // Ditto here +# undef _POSIX_C_SOURCE +#endif // _POSIX_C_SOURCE + +#ifdef VBOX_PYXPCOM +// unfortunatelly, if SOLARIS is defined Python porting layer +// defines gethostname() in invalid fashion what kills compilation +# ifdef SOLARIS +# undef SOLARIS +# define SOLARIS_WAS_DEFINED +# endif + +// Python.h/pyconfig.h redefines _XOPEN_SOURCE on some hosts +# ifdef _XOPEN_SOURCE +# define VBOX_XOPEN_SOURCE_DEFINED _XOPEN_SOURCE +# undef _XOPEN_SOURCE +# endif + +#endif /* VBOX_PYXPCOM */ + +#include <Python.h> + +#ifdef VBOX_PYXPCOM + +# ifdef SOLARIS_WAS_DEFINED +# define SOLARIS +# undef SOLARIS_WAS_DEFINED +# endif + +// restore the old value of _XOPEN_SOURCE if not defined by Python.h/pyconfig.h +# if !defined(_XOPEN_SOURCE) && defined(VBOX_XOPEN_SOURCE_DEFINED) +# define _XOPEN_SOURCE VBOX_XOPEN_SOURCE_DEFINED +# endif +# undef VBOX_XOPEN_SOURCE_DEFINED + +# if (PY_VERSION_HEX <= 0x02040000) +// although in more recent versions of Python this type is ssize_t, earlier +// it was used as int +typedef int Py_ssize_t; +# endif + +# if (PY_VERSION_HEX <= 0x02030000) +// this one not defined before +inline PyObject *PyBool_FromLong(long ok) +{ + PyObject *result; + if (ok) + result = Py_True; + else + result = Py_False; + Py_INCREF(result); + return result; +} +# endif + +# if PY_MAJOR_VERSION >= 3 +# define PyInt_FromLong(l) PyLong_FromLong(l) +# define PyInt_Check(o) PyLong_Check(o) +# define PyInt_AsLong(o) PyLong_AsLong(o) +# define PyNumber_Int(o) PyNumber_Long(o) +# if !defined(Py_LIMITED_API) && PY_VERSION_HEX <= 0x03030000 /* 3.3 added PyUnicode_AsUTF8AndSize */ +# ifndef PyUnicode_AsUTF8 +# define PyUnicode_AsUTF8(o) _PyUnicode_AsString(o) +# endif +# ifndef PyUnicode_AsUTF8AndSize +# define PyUnicode_AsUTF8AndSize(o,s) _PyUnicode_AsStringAndSize(o,s) +# endif +# endif +typedef struct PyMethodChain +{ + PyMethodDef *methods; + struct PyMethodChain *link; +} PyMethodChain; +# endif + +#endif /* VBOX_PYXPCOM */ + +#ifdef BUILD_PYXPCOM + /* We are building the main dll */ +# define PYXPCOM_EXPORT NS_EXPORT +#else + /* This module uses the dll */ +# define PYXPCOM_EXPORT NS_IMPORT +#endif // BUILD_PYXPCOM + +// An IID we treat as NULL when passing as a reference. +extern PYXPCOM_EXPORT nsIID Py_nsIID_NULL; + +class Py_nsISupports; + + +/** @name VBox limited API hacks: + * @{ */ +#ifndef Py_LIMITED_API + +# define PyXPCOM_ObTypeName(obj) (Py_TYPE(obj)->tp_name) + +#else /* Py_LIMITED_API */ + +# if PY_VERSION_HEX <= 0x03030000 +# error "Py_LIMITED_API mode only works for Python 3.3 and higher." +# endif + +const char *PyXPCOMGetObTypeName(PyTypeObject *pTypeObj); +# define PyXPCOM_ObTypeName(obj) PyXPCOMGetObTypeName(Py_TYPE(obj)) + +# if Py_LIMITED_API < 0x030A0000 +/* Note! While we should not technically be using PyUnicode_AsUTF8AndSize, it was + made part of the limited API in 3.10 (see https://bugs.python.org/issue41784 and + https://github.com/python/cpython/commit/a05195ac61f1908ac5990cccb5aa82442bdaf15d). */ +extern "C" PyAPI_FUNC(const char *) PyUnicode_AsUTF8AndSize(PyObject *, Py_ssize_t *); +# endif + +/* PyUnicode_AsUTF8 is just PyUnicode_AsUTF8AndSize without returning a size. */ +# define PyUnicode_AsUTF8(o) PyUnicode_AsUTF8AndSize(o, NULL) + +DECLINLINE(int) PyRun_SimpleString(const char *pszCode) +{ + /* Get the main mode dictionary: */ + PyObject *pMainMod = PyImport_AddModule("__main__"); + if (pMainMod) { + PyObject *pMainModDict = PyModule_GetDict(pMainMod); + + /* Compile and run the code. */ + PyObject *pCodeObject = Py_CompileString(pszCode, "PyXPCOM", Py_file_input); + if (pCodeObject) { + PyObject *pResult = PyEval_EvalCode(pCodeObject, pMainModDict, pMainModDict); + Py_DECREF(pCodeObject); + if (pResult) { + Py_DECREF(pResult); + return 0; + } + PyErr_Print(); + } + } + return -1; +} + +DECLINLINE(PyObject *) PyTuple_GET_ITEM(PyObject *pTuple, Py_ssize_t idx) +{ + return PyTuple_GetItem(pTuple, idx); +} + +DECLINLINE(int) PyTuple_SET_ITEM(PyObject *pTuple, Py_ssize_t idx, PyObject *pItem) +{ + int rc = PyTuple_SetItem(pTuple, idx, pItem); /* Steals pItem ref, just like PyTuple_SET_ITEM. */ + Assert(rc == 0); + return rc; +} + +DECLINLINE(int) PyList_SET_ITEM(PyObject *pList, Py_ssize_t idx, PyObject *pItem) +{ + int rc = PyList_SetItem(pList, idx, pItem); /* Steals pItem ref, just like PyList_SET_ITEM. */ + Assert(rc == 0); + return rc; +} + +DECLINLINE(Py_ssize_t) PyBytes_GET_SIZE(PyObject *pBytes) +{ + return PyBytes_Size(pBytes); +} + +DECLINLINE(const char *) PyBytes_AS_STRING(PyObject *pBytes) +{ + return PyBytes_AsString(pBytes); +} + +DECLINLINE(Py_ssize_t) PyUnicode_GET_SIZE(PyObject *pUnicode) +{ + /* Note! Currently only used for testing for zero or 1 codepoints, so we don't + really need to deal with the different way these two treats surrogate pairs. */ +# if Py_LIMITED_API >= 0x03030000 + return PyUnicode_GetLength(pUnicode); +# else + return PyUnicode_GetSize(pUnicode); +# endif +} + +# define PyObject_CheckBuffer(pAllegedBuffer) PyObject_CheckReadBuffer(pAllegedBuffer) + +# include <iprt/asm.h> +DECLINLINE(Py_hash_t) _Py_HashPointer(void *p) +{ + Py_hash_t uHash = (Py_hash_t)RT_CONCAT(ASMRotateRightU,ARCH_BITS)((uintptr_t)p, 4); + return uHash != -1 ? uHash : -2; +} + +#endif /* Py_LIMITED_API */ +/** @} */ + + +/************************************************************************* +************************************************************************** + + Error and exception related function. + +************************************************************************** +*************************************************************************/ + +#define NS_PYXPCOM_NO_SUCH_METHOD \ + NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_PYXPCOM, 0) + +// The exception object (loaded from the xpcom .py code) +extern PYXPCOM_EXPORT PyObject *PyXPCOM_Error; + +// Client related functions - generally called by interfaces before +// they return NULL back to Python to indicate the error. +// All these functions return NULL so interfaces can generally +// just "return PyXPCOM_BuildPyException(hr, punk, IID_IWhatever)" +PYXPCOM_EXPORT PyObject *PyXPCOM_BuildPyException(nsresult res); + +#ifdef VBOX +// Build human readable error message out of XPCOM error +PYXPCOM_EXPORT PyObject *PyXPCOM_BuildErrorMessage(nsresult r); +#endif + +// Used in gateways to handle the current Python exception +// NOTE: this function assumes it is operating within the Python context +PYXPCOM_EXPORT nsresult PyXPCOM_SetCOMErrorFromPyException(); + +// Write current exception and traceback to a string. +PYXPCOM_EXPORT PRBool PyXPCOM_FormatCurrentException(nsCString &streamout); +// Write specified exception and traceback to a string. +PYXPCOM_EXPORT PRBool PyXPCOM_FormatGivenException(nsCString &streamout, + PyObject *exc_typ, PyObject *exc_val, + PyObject *exc_tb); + +// A couple of logging/error functions. These probably end up +// being written to the console service. + +// Log a warning for the user - something at runtime +// they may care about, but nothing that prevents us actually +// working. +// As it's designed for user error/warning, it exists in non-debug builds. +PYXPCOM_EXPORT void PyXPCOM_LogWarning(const char *fmt, ...); + +// Log an error for the user - something that _has_ prevented +// us working. This is probably accompanied by a traceback. +// As it's designed for user error/warning, it exists in non-debug builds. +PYXPCOM_EXPORT void PyXPCOM_LogError(const char *fmt, ...); + +// The raw one +PYXPCOM_EXPORT void PyXPCOM_Log(const char *level, const nsCString &msg); + +#ifdef DEBUG +// Mainly designed for developers of the XPCOM package. +// Only enabled in debug builds. +PYXPCOM_EXPORT void PyXPCOM_LogDebug(const char *fmt, ...); +#define PYXPCOM_LOG_DEBUG PyXPCOM_LogDebug +#else +#define PYXPCOM_LOG_DEBUG() +#endif // DEBUG + +// Some utility converters +// moz strings to PyObject. +PYXPCOM_EXPORT PyObject *PyObject_FromNSString( const nsACString &s, + PRBool bAssumeUTF8 = PR_FALSE ); +PYXPCOM_EXPORT PyObject *PyObject_FromNSString( const nsAString &s ); +PYXPCOM_EXPORT PyObject *PyObject_FromNSString( const PRUnichar *s, + PRUint32 len = (PRUint32)-1); + +// PyObjects to moz strings. As per the moz string guide, we pass a reference +// to an abstract string +PYXPCOM_EXPORT PRBool PyObject_AsNSString( PyObject *ob, nsAString &aStr); + +// Variants. +PYXPCOM_EXPORT nsresult PyObject_AsVariant( PyObject *ob, nsIVariant **aRet); +PYXPCOM_EXPORT PyObject *PyObject_FromVariant( Py_nsISupports *parent, + nsIVariant *v); + +// Interfaces - these are the "official" functions +PYXPCOM_EXPORT PyObject *PyObject_FromNSInterface( nsISupports *aInterface, + const nsIID &iid, + PRBool bMakeNicePyObject = PR_TRUE); + +/************************************************************************* +************************************************************************** + + Support for CALLING (ie, using) interfaces. + +************************************************************************** +*************************************************************************/ + +typedef Py_nsISupports* (* PyXPCOM_I_CTOR)(nsISupports *, const nsIID &); + +////////////////////////////////////////////////////////////////////////// +// class PyXPCOM_TypeObject +// Base class for (most of) the type objects. + +#ifndef Py_LIMITED_API +class PYXPCOM_EXPORT PyXPCOM_TypeObject : public PyTypeObject { +#else +class PYXPCOM_EXPORT PyXPCOM_TypeObject : public PyObject { +#endif +public: + PyXPCOM_TypeObject( + const char *name, + PyXPCOM_TypeObject *pBaseType, + int typeSize, + struct PyMethodDef* methodList, + PyXPCOM_I_CTOR ctor); + ~PyXPCOM_TypeObject(); + + PyMethodChain chain; + PyXPCOM_TypeObject *baseType; + PyXPCOM_I_CTOR ctor; + + static PRBool IsType(PyTypeObject *t); + // Static methods for the Python type. + static void Py_dealloc(PyObject *ob); + static PyObject *Py_repr(PyObject *ob); + static PyObject *Py_str(PyObject *ob); + static PyObject *Py_getattr(PyObject *self, char *name); + static int Py_setattr(PyObject *op, char *name, PyObject *v); + static int Py_cmp(PyObject *ob1, PyObject *ob2); + static PyObject *Py_richcmp(PyObject *ob1, PyObject *ob2, int op); +#if PY_VERSION_HEX >= 0x03020000 + static Py_hash_t Py_hash(PyObject *self); +#else + static long Py_hash(PyObject *self); +#endif +#ifdef Py_LIMITED_API + PyTypeObject *m_pTypeObj; /**< The python type object we wrap. */ +#endif +}; + +////////////////////////////////////////////////////////////////////////// +// class Py_nsISupports +// This class serves 2 purposes: +// * It is a base class for other interfaces we support "natively" +// * It is instantiated for _all_ other interfaces. +// +// This is different than win32com, where a PyIUnknown only +// ever holds an IUnknown - but here, we could be holding +// _any_ interface. +class PYXPCOM_EXPORT Py_nsISupports : public PyObject +{ +public: + // Check if a Python object can safely be cast to an Py_nsISupports, + // and optionally check that the object is wrapping the specified + // interface. + static PRBool Check( PyObject *ob, const nsIID &checkIID = Py_nsIID_NULL) { + Py_nsISupports *self = static_cast<Py_nsISupports *>(ob); + if (ob==NULL || !PyXPCOM_TypeObject::IsType(ob->ob_type )) + return PR_FALSE; + if (!checkIID.Equals(Py_nsIID_NULL)) + return self->m_iid.Equals(checkIID) != 0; + return PR_TRUE; + } + // Get the nsISupports interface from the PyObject WITH NO REF COUNT ADDED + static nsISupports *GetI(PyObject *self, nsIID *ret_iid = NULL); + nsCOMPtr<nsISupports> m_obj; + nsIID m_iid; +#ifdef Py_LIMITED_API + /** Because PyXPCOM_TypeObject cannot inherit from PyTypeObject in + * Py_LIMITED_API mode, we cannot use ob_type to get to the method list. + * Instead of we store it here. */ + PyXPCOM_TypeObject *m_pMyTypeObj; +#endif + + // Given an nsISupports and an Interface ID, create and return an object + // Does not QI the object - the caller must ensure the nsISupports object + // is really a pointer to an object identified by the IID (although + // debug builds should check this) + // PRBool bMakeNicePyObject indicates if we should call back into + // Python to wrap the object. This allows Python code to + // see the correct xpcom.client.Interface object even when calling + // xpcom functions directly from C++. + // NOTE: There used to be a bAddRef param to this as an internal + // optimization, but since removed. This function *always* takes a + // reference to the nsISupports. + static PyObject *PyObjectFromInterface(nsISupports *ps, + const nsIID &iid, + PRBool bMakeNicePyObject = PR_TRUE, + PRBool bIsInternalCall = PR_FALSE); + + // Given a Python object that is a registered COM type, return a given + // interface pointer on its underlying object, with a NEW REFERENCE ADDED. + // bTryAutoWrap indicates if a Python instance object should attempt to + // be automatically wrapped in an XPCOM object. This is really only + // provided to stop accidental recursion should the object returned by + // the wrap process itself be in instance (where it should already be + // a COM object. + // If |iid|==nsIVariant, then arbitary Python objects will be wrapped + // in an nsIVariant. + static PRBool InterfaceFromPyObject( + PyObject *ob, + const nsIID &iid, + nsISupports **ppret, + PRBool bNoneOK, + PRBool bTryAutoWrap = PR_TRUE); + + // Given a Py_nsISupports, return an interface. + // Object *must* be Py_nsISupports - there is no + // "autowrap", no "None" support, etc + static PRBool InterfaceFromPyISupports(PyObject *ob, + const nsIID &iid, + nsISupports **ppv); + + static Py_nsISupports *Constructor(nsISupports *pInitObj, const nsIID &iid); + // The Python methods + static PyObject *QueryInterface(PyObject *self, PyObject *args); + + // Internal (sort-of) objects. + static NS_EXPORT_STATIC_MEMBER_(PyXPCOM_TypeObject) *type; + static NS_EXPORT_STATIC_MEMBER_(PyMethodDef) methods[]; + static PyObject *mapIIDToType; + static void SafeRelease(Py_nsISupports *ob); +#ifndef Py_LIMITED_API + static void RegisterInterface( const nsIID &iid, PyTypeObject *t); +#else + static void RegisterInterface( const nsIID &iid, PyXPCOM_TypeObject *t); +#endif + static void InitType(); +#ifdef VBOX_DEBUG_LIFETIMES + static void dumpList(void); + static void dumpListToStdOut(void); +#endif + + virtual ~Py_nsISupports(); + virtual PyObject *getattr(const char *name); + virtual int setattr(const char *name, PyObject *val); + // A virtual function to sub-classes can customize the way + // nsISupports objects are returned from their methods. + // ps is a new object just obtained from some operation performed on us + virtual PyObject *MakeInterfaceResult(nsISupports *ps, const nsIID &iid, + PRBool bMakeNicePyObject = PR_TRUE) { + return PyObjectFromInterface(ps, iid, bMakeNicePyObject); + } + +protected: + // ctor is protected - must create objects via + // PyObjectFromInterface() + Py_nsISupports(nsISupports *p, + const nsIID &iid, +#ifndef Py_LIMITED_API + PyTypeObject *type); +#else + PyXPCOM_TypeObject *type); +#endif + + // Make a default wrapper for an ISupports (which is an + // xpcom.client.Component instance) + static PyObject *MakeDefaultWrapper(PyObject *pyis, const nsIID &iid); + +#ifdef VBOX_DEBUG_LIFETIMES + static DECLCALLBACK(int32_t) initOnceCallback(void *pvUser1); + + RTLISTNODE m_ListEntry; /**< List entry. */ + + static RTONCE g_Once; /**< Init list and critsect once. */ + static RTCRITSECT g_CritSect; /**< Critsect protecting the list. */ + static RTLISTANCHOR g_List; /**< List of live interfaces.*/ +#endif +}; + +// Python/XPCOM IID support +class PYXPCOM_EXPORT Py_nsIID : public PyObject +{ +public: + Py_nsIID(const nsIID &riid); + nsIID m_iid; + + PRBool + IsEqual(const nsIID &riid) { + return m_iid.Equals(riid); + } + + PRBool + IsEqual(PyObject *ob) { + return ob && +#ifndef Py_LIMITED_API + ob->ob_type== &type && +#else + ob->ob_type == s_pType && +#endif + m_iid.Equals(((Py_nsIID *)ob)->m_iid); + } + + PRBool + IsEqual(Py_nsIID &iid) { + return m_iid.Equals(iid.m_iid); + } + + static PyObject * + PyObjectFromIID(const nsIID &iid) { + return new Py_nsIID(iid); + } + + static PRBool IIDFromPyObject(PyObject *ob, nsIID *pRet); + /* Python support */ + static PyObject *PyTypeMethod_getattr(PyObject *self, char *name); +#if PY_MAJOR_VERSION <= 2 + static int PyTypeMethod_compare(PyObject *self, PyObject *ob); +#endif + static PyObject *PyTypeMethod_richcompare(PyObject *self, PyObject *ob, int op); + static PyObject *PyTypeMethod_repr(PyObject *self); +#if PY_VERSION_HEX >= 0x03020000 + static Py_hash_t PyTypeMethod_hash(PyObject *self); +#else + static long PyTypeMethod_hash(PyObject *self); +#endif + static PyObject *PyTypeMethod_str(PyObject *self); + static void PyTypeMethod_dealloc(PyObject *self); +#ifndef Py_LIMITED_API + static NS_EXPORT_STATIC_MEMBER_(PyTypeObject) type; +#else + static NS_EXPORT_STATIC_MEMBER_(PyTypeObject *) s_pType; + static PyTypeObject *GetTypeObject(void); +#endif + static NS_EXPORT_STATIC_MEMBER_(PyMethodDef) methods[]; +}; + +/////////////////////////////////////////////////////// +// +// Helper classes for managing arrays of variants. +class PythonTypeDescriptor; // Forward declare. + +class PYXPCOM_EXPORT PyXPCOM_InterfaceVariantHelper { +public: + PyXPCOM_InterfaceVariantHelper(Py_nsISupports *parent, int methodindex); + ~PyXPCOM_InterfaceVariantHelper(); + PRBool Init(PyObject *obParams); + PRBool FillArray(); + + PyObject *MakePythonResult(); + + nsXPTCVariant *m_var_array; + int m_num_array; + int m_methodindex; +protected: + PyObject *MakeSinglePythonResult(int index); + PRBool FillInVariant(const PythonTypeDescriptor &, int, int); + PRBool PrepareOutVariant(const PythonTypeDescriptor &td, int value_index); + PRBool SetSizeIs( int var_index, PRBool is_arg1, PRUint32 new_size); + PRUint32 GetSizeIs( int var_index, PRBool is_arg1); + + PyObject *m_pyparams; // sequence of actual params passed (ie, not including hidden) + PyObject *m_typedescs; // desc of _all_ params, including hidden. + PythonTypeDescriptor *m_python_type_desc_array; + void **m_buffer_array; + Py_nsISupports *m_parent; + +}; + +/************************************************************************* +************************************************************************** + + Support for IMPLEMENTING interfaces. + +************************************************************************** +*************************************************************************/ +#define NS_IINTERNALPYTHON_IID_STR "AC7459FC-E8AB-4f2e-9C4F-ADDC53393A20" +#define NS_IINTERNALPYTHON_IID \ + { 0xac7459fc, 0xe8ab, 0x4f2e, { 0x9c, 0x4f, 0xad, 0xdc, 0x53, 0x39, 0x3a, 0x20 } } + +class PyXPCOM_GatewayWeakReference; + +// This interface is needed primarily to give us a known vtable base. +// If we QI a Python object for this interface, we can safely cast the result +// to a PyG_Base. Any other interface, we do now know which vtable we will get. +// We also allow the underlying PyObject to be extracted +class nsIInternalPython : public nsISupports { +public: + NS_DEFINE_STATIC_IID_ACCESSOR(NS_IINTERNALPYTHON_IID) + // Get the underlying Python object with new reference added + virtual PyObject *UnwrapPythonObject(void) = 0; +}; + +// This is roughly equivalent to PyGatewayBase in win32com +// +class PYXPCOM_EXPORT PyG_Base : public nsIInternalPython, public nsISupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISUPPORTSWEAKREFERENCE + PyObject *UnwrapPythonObject(void); + + // A static "constructor" - the real ctor is protected. + static nsresult CreateNew(PyObject *pPyInstance, + const nsIID &iid, + void **ppResult); + + // A utility to auto-wrap an arbitary Python instance + // in a COM gateway. + static PRBool AutoWrapPythonInstance(PyObject *ob, + const nsIID &iid, + nsISupports **ppret); + + + // A helper that creates objects to be passed for nsISupports + // objects. See extensive comments in PyG_Base.cpp. + PyObject *MakeInterfaceParam(nsISupports *pis, + const nsIID *piid, + int methodIndex = -1, + const XPTParamDescriptor *d = NULL, + int paramIndex = -1); + + // A helper that ensures all casting and vtable offsetting etc + // done against this object happens in the one spot! + virtual void *ThisAsIID( const nsIID &iid ) = 0; + + // Helpers for "native" interfaces. + // Not used by the generic stub interface. + nsresult HandleNativeGatewayError(const char *szMethodName); + + // These data members used by the converter helper functions - hence public + nsIID m_iid; + PyObject * m_pPyObject; + // We keep a reference count on this object, and the object + // itself uses normal refcount rules - thus, it will only + // die when we die, and all external references are removed. + // This means that once we have created it (and while we + // are alive) it will never die. + nsCOMPtr<nsIWeakReference> m_pWeakRef; +#ifdef NS_BUILD_REFCNT_LOGGING + char refcntLogRepr[64]; // sigh - I wish I knew how to use the Moz string classes :( OK for debug only tho. +#endif +protected: + PyG_Base(PyObject *instance, const nsIID &iid); + virtual ~PyG_Base(); + PyG_Base *m_pBaseObject; // A chain to implement identity rules. + nsresult InvokeNativeViaPolicy( const char *szMethodName, + PyObject **ppResult = NULL, + const char *szFormat = NULL, + ... + ); + nsresult InvokeNativeViaPolicyInternal( const char *szMethodName, + PyObject **ppResult, + const char *szFormat, + va_list va); + nsresult InvokeNativeGetViaPolicy(const char *szPropertyName, + PyObject **ppResult = NULL + ); + nsresult InvokeNativeSetViaPolicy(const char *szPropertyName, + ...); +}; + +class PYXPCOM_EXPORT PyXPCOM_XPTStub : public PyG_Base, public nsXPTCStubBase +{ +friend class PyG_Base; +public: + NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) \ + {return PyG_Base::QueryInterface(aIID, aInstancePtr);} \ + NS_IMETHOD_(nsrefcnt) AddRef(void) {return PyG_Base::AddRef();} \ + NS_IMETHOD_(nsrefcnt) Release(void) {return PyG_Base::Release();} \ + + NS_IMETHOD GetInterfaceInfo(nsIInterfaceInfo** info); + // call this method and return result + NS_IMETHOD CallMethod(PRUint16 methodIndex, + const nsXPTMethodInfo* info, + nsXPTCMiniVariant* params); + + virtual void *ThisAsIID(const nsIID &iid); +protected: + PyXPCOM_XPTStub(PyObject *instance, const nsIID &iid) : PyG_Base(instance, iid) {;} +private: +}; + +// For the Gateways we manually implement. +#define PYGATEWAY_BASE_SUPPORT(INTERFACE, GATEWAY_BASE) \ + NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) \ + {return PyG_Base::QueryInterface(aIID, aInstancePtr);} \ + NS_IMETHOD_(nsrefcnt) AddRef(void) {return PyG_Base::AddRef();} \ + NS_IMETHOD_(nsrefcnt) Release(void) {return PyG_Base::Release();} \ + virtual void *ThisAsIID(const nsIID &iid) { \ + if (iid.Equals(NS_GET_IID(INTERFACE))) return (INTERFACE *)this; \ + return GATEWAY_BASE::ThisAsIID(iid); \ + } \ + +extern PYXPCOM_EXPORT void AddDefaultGateway(PyObject *instance, nsISupports *gateway); + +extern PYXPCOM_EXPORT PRInt32 _PyXPCOM_GetGatewayCount(void); +extern PYXPCOM_EXPORT PRInt32 _PyXPCOM_GetInterfaceCount(void); +#ifdef VBOX_DEBUG_LIFETIMES +extern PYXPCOM_EXPORT PRInt32 _PyXPCOM_DumpInterfaces(void); +#endif + + +// Weak Reference class. This is a true COM object, representing +// a weak reference to a Python object. For each Python XPCOM object, +// there is exactly zero or one corresponding weak reference instance. +// When both are alive, each holds a pointer to the other. When the main +// object dies due to XPCOM reference counting, it zaps the pointer +// in its corresponding weak reference object. Thus, the weak-reference +// can live beyond the object (possibly with a NULL pointer back to the +// "real" object, but as implemented, the weak reference will never be +// destroyed before the object +class PYXPCOM_EXPORT PyXPCOM_GatewayWeakReference : public nsIWeakReference { +public: + PyXPCOM_GatewayWeakReference(PyG_Base *base); + virtual ~PyXPCOM_GatewayWeakReference(); + NS_DECL_ISUPPORTS + NS_DECL_NSIWEAKREFERENCE + PyG_Base *m_pBase; // NO REF COUNT!!! +#ifdef NS_BUILD_REFCNT_LOGGING + char refcntLogRepr[41]; +#endif +}; + + +// Helpers classes for our gateways. +class PYXPCOM_EXPORT PyXPCOM_GatewayVariantHelper +{ +public: + PyXPCOM_GatewayVariantHelper( PyG_Base *gateway, + int methodIndex, + const nsXPTMethodInfo *info, + nsXPTCMiniVariant* params ); + ~PyXPCOM_GatewayVariantHelper(); + PyObject *MakePyArgs(); + nsresult ProcessPythonResult(PyObject *ob); + PyG_Base *m_gateway; +private: + nsresult BackFillVariant( PyObject *ob, int index); + PyObject *MakeSingleParam(int index, PythonTypeDescriptor &td); + PRBool GetIIDForINTERFACE_ID(int index, const nsIID **ppret); + nsresult GetArrayType(PRUint8 index, PRUint8 *ret, nsIID **ppiid); + PRUint32 GetSizeIs( int var_index, PRBool is_arg1); + PRBool SetSizeIs( int var_index, PRBool is_arg1, PRUint32 new_size); + PRBool CanSetSizeIs( int var_index, PRBool is_arg1 ); + nsIInterfaceInfo *GetInterfaceInfo(); // NOTE: no ref count on result. + + + nsXPTCMiniVariant* m_params; + const nsXPTMethodInfo *m_info; + int m_method_index; + PythonTypeDescriptor *m_python_type_desc_array; + int m_num_type_descs; + nsCOMPtr<nsIInterfaceInfo> m_interface_info; +}; + +// Misc converters. +PyObject *PyObject_FromXPTType( const nsXPTType *d); +// XPTTypeDescriptor derived from XPTType - latter is automatically processed via PyObject_FromXPTTypeDescriptor XPTTypeDescriptor +PyObject *PyObject_FromXPTTypeDescriptor( const XPTTypeDescriptor *d); + +PyObject *PyObject_FromXPTParamDescriptor( const XPTParamDescriptor *d); +PyObject *PyObject_FromXPTMethodDescriptor( const XPTMethodDescriptor *d); +PyObject *PyObject_FromXPTConstant( const XPTConstDescriptor *d); + +// DLL reference counting functions. +// Although we maintain the count, we never actually +// finalize Python when it hits zero! +void PyXPCOM_DLLAddRef(); +void PyXPCOM_DLLRelease(); + +/************************************************************************* +************************************************************************** + + LOCKING AND THREADING + +************************************************************************** +*************************************************************************/ + +// +// We have 2 discrete locks in use (when no free-threaded is used, anyway). +// The first type of lock is the global Python lock. This is the standard lock +// in use by Python, and must be used as documented by Python. Specifically, no +// 2 threads may _ever_ call _any_ Python code (including INCREF/DECREF) without +// first having this thread lock. +// +// The second type of lock is a "global framework lock", and used whenever 2 threads +// of C code need access to global data. This is different than the Python +// lock - this lock is used when no Python code can ever be called by the +// threads, but the C code still needs thread-safety. + +// We also supply helper classes which make the usage of these locks a one-liner. + +// The "framework" lock, implemented as a PRLock +PYXPCOM_EXPORT void PyXPCOM_AcquireGlobalLock(void); +PYXPCOM_EXPORT void PyXPCOM_ReleaseGlobalLock(void); + +// Helper class for the DLL global lock. +// +// This class magically waits for PyXPCOM framework global lock, and releases it +// when finished. +// NEVER new one of these objects - only use on the stack! +class CEnterLeaveXPCOMFramework { +public: + CEnterLeaveXPCOMFramework() {PyXPCOM_AcquireGlobalLock();} + ~CEnterLeaveXPCOMFramework() {PyXPCOM_ReleaseGlobalLock();} +}; + +// Python thread-lock stuff. Free-threading patches use different semantics, but +// these are abstracted away here... +//#include <threadstate.h> + +// Helper class for Enter/Leave Python +// +// This class magically waits for the Python global lock, and releases it +// when finished. + +// Nested invocations will deadlock, so be careful. + +// NEVER new one of these objects - only use on the stack! + +PYXPCOM_EXPORT void PyXPCOM_MakePendingCalls(); +PYXPCOM_EXPORT PRBool PyXPCOM_Globals_Ensure(); + +// For 2.3, use the PyGILState_ calls +#if (PY_VERSION_HEX >= 0x02030000) +#define PYXPCOM_USE_PYGILSTATE +#endif + +#ifdef PYXPCOM_USE_PYGILSTATE +class CEnterLeavePython { +public: + CEnterLeavePython() { + state = PyGILState_Ensure(); + // See "pending calls" comment below. We reach into the Python + // implementation to see if we are the first call on the stack. +# ifndef Py_LIMITED_API + if (PyThreadState_Get()->gilstate_counter==1) { +# else + if (state == PyGILState_UNLOCKED) { +# endif + PyXPCOM_MakePendingCalls(); + } + } + ~CEnterLeavePython() { + PyGILState_Release(state); + } + PyGILState_STATE state; +}; +#else + +extern PYXPCOM_EXPORT PyInterpreterState *PyXPCOM_InterpreterState; +PYXPCOM_EXPORT PRBool PyXPCOM_ThreadState_Ensure(); +PYXPCOM_EXPORT void PyXPCOM_ThreadState_Free(); +PYXPCOM_EXPORT void PyXPCOM_ThreadState_Clear(); +PYXPCOM_EXPORT void PyXPCOM_InterpreterLock_Acquire(); +PYXPCOM_EXPORT void PyXPCOM_InterpreterLock_Release(); + +// Pre 2.3 thread-state dances. +class CEnterLeavePython { +public: + CEnterLeavePython() { + created = PyXPCOM_ThreadState_Ensure(); + PyXPCOM_InterpreterLock_Acquire(); + if (created) { + // If pending python calls are waiting as we enter Python, + // it will generally mean an asynch signal handler, etc. + // We can either call it here, or wait for Python to call it + // as part of its "even 'n' opcodes" check. If we wait for + // Python to check it and the pending call raises an exception, + // then it is _our_ code that will fail - this is unfair, + // as the signal was raised before we were entered - indeed, + // we may be directly responding to the signal! + // Thus, we flush all the pending calls here, and report any + // exceptions via our normal exception reporting mechanism. + // We can then execute our code in the knowledge that only + // signals raised _while_ we are executing will cause exceptions. + PyXPCOM_MakePendingCalls(); + } + } + ~CEnterLeavePython() { + // The interpreter state must be cleared + // _before_ we release the lock, as some of + // the sys. attributes cleared (eg, the current exception) + // may need the lock to invoke their destructors - + // specifically, when exc_value is a class instance, and + // the exception holds the last reference! + if ( created ) + PyXPCOM_ThreadState_Clear(); + PyXPCOM_InterpreterLock_Release(); + if ( created ) + PyXPCOM_ThreadState_Free(); + } +private: + PRBool created; +}; +#endif // PYXPCOM_USE_PYGILSTATE + +// Our classes. +// Hrm - So we can't have templates, eh?? +// preprocessor to the rescue, I guess. +#define PyXPCOM_INTERFACE_DECLARE(ClassName, InterfaceName, Methods ) \ + \ +extern struct PyMethodDef Methods[]; \ + \ +class ClassName : public Py_nsISupports \ +{ \ +public: \ + static PYXPCOM_EXPORT PyXPCOM_TypeObject *type; \ + static Py_nsISupports *Constructor(nsISupports *pInitObj, const nsIID &iid) { \ + return new ClassName(pInitObj, iid); \ + } \ + static void InitType() { \ + type = new PyXPCOM_TypeObject( \ + #InterfaceName, \ + Py_nsISupports::type, \ + sizeof(ClassName), \ + Methods, \ + Constructor); \ + const nsIID &iid = NS_GET_IID(InterfaceName); \ + RegisterInterface(iid, type); \ + } \ +protected: \ + ClassName(nsISupports *p, const nsIID &iid) : \ + Py_nsISupports(p, iid, type) { \ + /* The IID _must_ be the IID of the interface we are wrapping! */ \ + NS_ABORT_IF_FALSE(iid.Equals(NS_GET_IID(InterfaceName)), "Bad IID"); \ + } \ +}; \ + \ +// End of PyXPCOM_INTERFACE_DECLARE macro + +#define PyXPCOM_ATTR_INTERFACE_DECLARE(ClassName, InterfaceName, Methods )\ + \ +extern struct PyMethodDef Methods[]; \ + \ +class ClassName : public Py_nsISupports \ +{ \ +public: \ + static PyXPCOM_TypeObject *type; \ + static Py_nsISupports *Constructor(nsISupports *pInitObj, const nsIID &iid) { \ + return new ClassName(pInitObj, iid); \ + } \ + static void InitType() { \ + type = new PyXPCOM_TypeObject( \ + #InterfaceName, \ + Py_nsISupports::type, \ + sizeof(ClassName), \ + Methods, \ + Constructor); \ + const nsIID &iid = NS_GET_IID(InterfaceName); \ + RegisterInterface(iid, type); \ +} \ + virtual PyObject *getattr(const char *name); \ + virtual int setattr(const char *name, PyObject *val); \ +protected: \ + ClassName(nsISupports *p, const nsIID &iid) : \ + Py_nsISupports(p, iid, type) { \ + /* The IID _must_ be the IID of the interface we are wrapping! */ \ + NS_ABORT_IF_FALSE(iid.Equals(NS_GET_IID(InterfaceName)), "Bad IID"); \ + } \ +}; \ + \ +// End of PyXPCOM_ATTR_INTERFACE_DECLARE macro + +#define PyXPCOM_INTERFACE_DEFINE(ClassName, InterfaceName, Methods ) \ +PyXPCOM_TypeObject *ClassName::type = NULL; + + +// And the classes +PyXPCOM_INTERFACE_DECLARE(Py_nsIComponentManager, nsIComponentManager, PyMethods_IComponentManager) +PyXPCOM_INTERFACE_DECLARE(Py_nsIInterfaceInfoManager, nsIInterfaceInfoManager, PyMethods_IInterfaceInfoManager) +PyXPCOM_INTERFACE_DECLARE(Py_nsIEnumerator, nsIEnumerator, PyMethods_IEnumerator) +PyXPCOM_INTERFACE_DECLARE(Py_nsISimpleEnumerator, nsISimpleEnumerator, PyMethods_ISimpleEnumerator) +PyXPCOM_INTERFACE_DECLARE(Py_nsIInterfaceInfo, nsIInterfaceInfo, PyMethods_IInterfaceInfo) +PyXPCOM_INTERFACE_DECLARE(Py_nsIInputStream, nsIInputStream, PyMethods_IInputStream) +PyXPCOM_ATTR_INTERFACE_DECLARE(Py_nsIClassInfo, nsIClassInfo, PyMethods_IClassInfo) +PyXPCOM_ATTR_INTERFACE_DECLARE(Py_nsIVariant, nsIVariant, PyMethods_IVariant) +// deprecated, but retained for backward compatibility: +PyXPCOM_INTERFACE_DECLARE(Py_nsIComponentManagerObsolete, nsIComponentManagerObsolete, PyMethods_IComponentManagerObsolete) +#endif // __PYXPCOM_H__ |