/* * 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 ActiveState Tool Corp. are Copyright (C) 2000, 2001 * ActiveState Tool Corp. All Rights Reserved. * * Contributor(s): Mark Hammond (original author) * */ // // 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. #include "PyXPCOM_std.h" #include #include #include #include #include // one day we may know what they look like. inline PRBool IsNullDOMString( const nsAReadableString& aString ) { return PR_FALSE; } inline PRBool IsNULLDOMString( const nsAReadableCString& aString ) { return PR_FALSE; } class PythonTypeDescriptor { public: PythonTypeDescriptor() { param_flags = type_flags = argnum = argnum2 = 0; extra = NULL; is_auto_out = PR_FALSE; is_auto_in = PR_FALSE; have_set_auto = PR_FALSE; } ~PythonTypeDescriptor() { Py_XDECREF(extra); } PRUint8 param_flags; PRUint8 type_flags; PRUint8 argnum; /* used for iid_is and size_is */ PRUint8 argnum2; /* used for length_is */ PyObject *extra; // The IID object, or the type of the array. // Extra items to help our processing. // Is this auto-filled by some other "in" param? PRBool is_auto_in; // Is this auto-filled by some other "out" param? PRBool is_auto_out; // If is_auto_out, have I already filled it? Used when multiple // params share a size_is fields - first time sets it, subsequent // time check it. PRBool have_set_auto; }; static int ProcessPythonTypeDescriptors(PythonTypeDescriptor *pdescs, int num) { // Loop over the array, checking all the params marked as having an arg. // If these args nominate another arg as the size_is param, then // we reset the size_is param to _not_ requiring an arg. int i; for (i=0;iRelease(); Py_END_ALLOW_THREADS; } break; default: break; // nothing to do! } } #define FILL_SIMPLE_POINTER( type, val ) *((type *)pthis) = (type)(val); #define BREAK_FALSE {rc=PR_FALSE;break;} PRBool FillSingleArray(void *array_ptr, PyObject *sequence_ob, PRUint32 sequence_size, PRUint32 array_element_size, PRUint8 array_type) { PRUint8 *pthis = (PRUint8 *)array_ptr; NS_ABORT_IF_FALSE(pthis, "Don't have a valid array to fill!"); PRBool rc = PR_TRUE; // We handle T_U8 specially as a string/Unicode. // If it is NOT a string, we just fall through and allow the standard // sequence unpack code process it (just slower!) if ( (array_type & XPT_TDP_TAGMASK) == nsXPTType::T_U8 && (PyString_Check(sequence_ob) || PyUnicode_Check(sequence_ob))) { PRBool release_seq; if (PyUnicode_Check(sequence_ob)) { release_seq = PR_TRUE; sequence_ob = PyObject_Str(sequence_ob); } else release_seq = PR_FALSE; if (!sequence_ob) // presumably a memory error, or Unicode encoding error. return PR_FALSE; nsCRT::memcpy(pthis, PyString_AS_STRING(sequence_ob), sequence_size); if (release_seq) Py_DECREF(sequence_ob); return PR_TRUE; } for (PRUint32 i=0; rc && iRelease(); Py_END_ALLOW_THREADS; } *pp = pnew; // ref-count added by InterfaceFromPyObject break; } default: // try and limp along in this case. // leave rc TRUE PyXPCOM_LogWarning("Converting Python object for an array element - The object type (0x%x) is unknown - leaving param alone!\n", array_type); break; } Py_XDECREF(val_use); Py_DECREF(val); } return rc; } PyObject *UnpackSingleArray(void *array_ptr, PRUint32 sequence_size, PRUint32 array_element_size, PRUint8 array_type) { if (array_ptr==NULL) { Py_INCREF(Py_None); return Py_None; } if ((array_type & XPT_TDP_TAGMASK) == nsXPTType::T_U8) return PyString_FromStringAndSize( (char *)array_ptr, sequence_size ); PyObject *list_ret = PyList_New(sequence_size); PRUint8 *pthis = (PRUint8 *)array_ptr; for (PRUint32 i=0; iRelease(); Py_END_ALLOW_THREADS; } } if (ns_v.IsValDOMString() && ns_v.val.p) { PythonTypeDescriptor &ptd = m_python_type_desc_array[i]; if (XPT_PD_IS_OUT(ptd.param_flags) || XPT_PD_IS_DIPPER(ptd.param_flags)) delete (nsAReadableString *)ns_v.val.p; } if (ns_v.IsValArray()) { nsXPTCVariant &ns_v = m_var_array[i]; if (ns_v.val.p) { PRUint8 array_type = (PRUint8)PyInt_AsLong(m_python_type_desc_array[i].extra); PRUint32 seq_size = GetSizeIs(i, PR_FALSE); FreeSingleArray(ns_v.val.p, seq_size, array_type); } } // IsOwned must be the last check of the loop, as // this frees the underlying data used above (eg, by the array free process) if (ns_v.IsValAllocated() && !ns_v.IsValInterface() && !ns_v.IsValDOMString()) { NS_ABORT_IF_FALSE(ns_v.IsPtrData(), "expecting a pointer to free"); nsAllocator::Free(ns_v.val.p); } } if (m_buffer_array && m_buffer_array[i]) nsAllocator::Free(m_buffer_array[i]); } delete [] m_python_type_desc_array; delete [] m_buffer_array; delete [] m_var_array; } PRBool PyXPCOM_InterfaceVariantHelper::Init(PyObject *obParams) { PRBool ok = PR_FALSE; int i; int total_params_needed = 0; if (!PySequence_Check(obParams) || PySequence_Length(obParams)!=2) { PyErr_Format(PyExc_TypeError, "Param descriptors must be a sequence of exactly length 2"); return PR_FALSE; } PyObject *typedescs = PySequence_GetItem(obParams, 0); if (typedescs==NULL) return PR_FALSE; // NOTE: The length of the typedescs may be different than the // args actually passed. The typedescs always include all // hidden params (such as "size_is"), while the actual // args never include this. m_num_array = PySequence_Length(typedescs); if (PyErr_Occurred()) goto done; m_pyparams = PySequence_GetItem(obParams, 1); if (m_pyparams==NULL) goto done; m_python_type_desc_array = new PythonTypeDescriptor[m_num_array]; if (!m_python_type_desc_array) goto done; // Pull apart the type descs and stash them. for (i=0;iLength() ); CopyUnicodeTo(*rs, 0, PyUnicode_AsUnicode(ret), rs->Length()); } break; } case nsXPTType::T_CHAR_STR: if (*((char **)ns_v.ptr) == NULL) { ret = Py_None; Py_INCREF(Py_None); } else ret = PyString_FromString( *((char **)ns_v.ptr) ); break; case nsXPTType::T_WCHAR_STR: { PRUnichar *us = *((PRUnichar **)ns_v.ptr); if (us == NULL) { ret = Py_None; Py_INCREF(Py_None); } else ret = PyUnicode_FromUnicode( us, nsCRT::strlen(us)); break; } case nsXPTType::T_INTERFACE: { nsIID iid; if (!Py_nsIID::IIDFromPyObject(td.extra, &iid)) break; nsISupports *iret = *((nsISupports **)ns_v.ptr); // We _do_ add a reference here, as our cleanup code will // remove this reference should we own it. ret = Py_nsISupports::PyObjectFromInterface(iret, iid, PR_TRUE); break; } case nsXPTType::T_INTERFACE_IS: { nsIID iid; nsXPTCVariant &ns_viid = m_var_array[td.argnum]; NS_WARN_IF_FALSE(XPT_TDP_TAG(ns_viid.type)==nsXPTType::T_IID, "The INTERFACE_IS iid describer isnt an IID!"); if (XPT_TDP_TAG(ns_viid.type)==nsXPTType::T_IID) { nsIID *piid = (nsIID *)ns_viid.val.p; if (piid==NULL) // Also serious, but like below, not our fault! iid = NS_GET_IID(nsISupports); else iid = *piid; } else // This is a pretty serious problem, but not Python's fault! // Just return an nsISupports and hope the caller does whatever // QI they need before using it. iid = NS_GET_IID(nsISupports); nsISupports *iret = *((nsISupports **)ns_v.ptr); // We _do_ add a reference here, as our cleanup code will // remove this reference should we own it. ret = Py_nsISupports::PyObjectFromInterface(iret, iid, PR_TRUE); break; } case nsXPTType::T_ARRAY: { if ( (* ((void **)ns_v.ptr)) == NULL) { ret = Py_None; Py_INCREF(Py_None); } if (!PyInt_Check(td.extra)) { PyErr_SetString(PyExc_TypeError, "The array info is not valid"); break; } PRUint8 array_type = (PRUint8)PyInt_AsLong(td.extra); PRUint32 element_size = GetArrayElementSize(array_type); PRUint32 seq_size = GetSizeIs(index, PR_FALSE); ret = UnpackSingleArray(* ((void **)ns_v.ptr), seq_size, element_size, array_type); break; } case nsXPTType::T_PSTRING_SIZE_IS: if (*((char **)ns_v.ptr) == NULL) { ret = Py_None; Py_INCREF(Py_None); } else { PRUint32 string_size = GetSizeIs(index, PR_TRUE); ret = PyString_FromStringAndSize( *((char **)ns_v.ptr), string_size ); } break; case nsXPTType::T_PWSTRING_SIZE_IS: if (*((PRUnichar **)ns_v.ptr) == NULL) { ret = Py_None; Py_INCREF(Py_None); } else { PRUint32 string_size = GetSizeIs(index, PR_TRUE); ret = PyUnicode_FromUnicode( *((PRUnichar **)ns_v.ptr), string_size ); } break; default: PyErr_Format(PyExc_ValueError, "Unknown XPCOM type code (0x%x)", XPT_TDP_TAG(ns_v.type)); /* ret remains nsnull */ break; } return ret; } PyObject *PyXPCOM_InterfaceVariantHelper::MakePythonResult() { // First we count the results. int i = 0; int n_results = 0; PyObject *ret = NULL; PRBool have_retval = PR_FALSE; for (i=0;i 1) { ret = PyTuple_New(n_results); if (ret==NULL) return NULL; } int ret_index = 0; int max_index = m_num_array; // Stick the retval at the front if we have have if (have_retval && n_results > 1) { PyObject *val = MakeSinglePythonResult(m_num_array-1); if (val==NULL) { Py_DECREF(ret); return NULL; } PyTuple_SET_ITEM(ret, 0, val); max_index--; ret_index++; } for (i=0;ret_index < n_results && i < max_index;i++) { if (!m_python_type_desc_array[i].is_auto_out) { if (XPT_PD_IS_OUT(m_python_type_desc_array[i].param_flags) || XPT_PD_IS_DIPPER(m_python_type_desc_array[i].param_flags)) { PyObject *val = MakeSinglePythonResult(i); if (val==NULL) { Py_XDECREF(ret); return NULL; } if (n_results > 1) { PyTuple_SET_ITEM(ret, ret_index, val); ret_index++; } else { NS_ABORT_IF_FALSE(ret==NULL, "shouldnt already have a ret!"); ret = val; } } } } } return ret; } /************************************************************************* ************************************************************************** Helpers when IMPLEMENTING interfaces. ************************************************************************** *************************************************************************/ PyXPCOM_GatewayVariantHelper::PyXPCOM_GatewayVariantHelper( PyG_Base *gw, int method_index, const nsXPTMethodInfo *info, nsXPTCMiniVariant* params ) { m_params = params; m_info = info; // no references added - this class is only alive for // a single gateway invocation m_gateway = gw; m_method_index = method_index; m_python_type_desc_array = NULL; m_num_type_descs = 0; } PyXPCOM_GatewayVariantHelper::~PyXPCOM_GatewayVariantHelper() { delete [] m_python_type_desc_array; } PyObject *PyXPCOM_GatewayVariantHelper::MakePyArgs() { NS_PRECONDITION(sizeof(XPTParamDescriptor) == sizeof(nsXPTParamInfo), "We depend on nsXPTParamInfo being a wrapper over the XPTParamDescriptor struct"); // Setup our array of Python typedescs, and determine the number of objects we // pass to Python. m_num_type_descs = m_info->num_args; m_python_type_desc_array = new PythonTypeDescriptor[m_num_type_descs]; if (m_python_type_desc_array==nsnull) return PyErr_NoMemory(); // First loop to count the number of objects // we pass to Python int i; for (i=0;inum_args;i++) { nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+i; PythonTypeDescriptor &td = m_python_type_desc_array[i]; td.param_flags = pi->flags; td.type_flags = pi->type.prefix.flags; td.argnum = pi->type.argnum; td.argnum2 = pi->type.argnum2; } int num_args = ProcessPythonTypeDescriptors(m_python_type_desc_array, m_num_type_descs); PyObject *ret = PyTuple_New(num_args); if (ret==NULL) return NULL; int this_arg = 0; for (i=0;i=0 && this_arg= m_num_type_descs) { PyErr_SetString(PyExc_ValueError, "dont have a valid size_is indicator for this param"); return PR_FALSE; } PRBool is_out = XPT_PD_IS_OUT(m_python_type_desc_array[argnum].param_flags); nsXPTCMiniVariant &ns_v = m_params[argnum]; NS_ABORT_IF_FALSE( (m_python_type_desc_array[argnum].type_flags & XPT_TDP_TAGMASK) == nsXPTType::T_U32, "size param must be Uint32"); return is_out ? *((PRUint32 *)ns_v.val.p) : ns_v.val.u32; } #undef DEREF_IN_OR_OUT #define DEREF_IN_OR_OUT( element, ret_type ) (ret_type)(is_out ? *((ret_type *)ns_v.val.p) : (ret_type)(element)) PyObject *PyXPCOM_GatewayVariantHelper::MakeSingleParam(int index, PythonTypeDescriptor &td) { NS_PRECONDITION(XPT_PD_IS_IN(td.param_flags), "Must be an [in] param!"); nsXPTCMiniVariant &ns_v = m_params[index]; PyObject *ret = NULL; PRBool is_out = XPT_PD_IS_OUT(td.param_flags); switch (td.type_flags & XPT_TDP_TAGMASK) { case nsXPTType::T_I8: ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.i8, PRInt8 ) ); break; case nsXPTType::T_I16: ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.i16, PRInt16) ); break; case nsXPTType::T_I32: ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.i32, PRInt32) ); break; case nsXPTType::T_I64: ret = PyLong_FromLongLong( DEREF_IN_OR_OUT(ns_v.val.i64, PRInt64) ); break; case nsXPTType::T_U8: ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.u8, PRUint8) ); break; case nsXPTType::T_U16: ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.u16, PRUint16) ); break; case nsXPTType::T_U32: ret = PyInt_FromLong( DEREF_IN_OR_OUT(ns_v.val.u32, PRUint32) ); break; case nsXPTType::T_U64: ret = PyLong_FromUnsignedLongLong( DEREF_IN_OR_OUT(ns_v.val.u64, PRUint64) ); break; case nsXPTType::T_FLOAT: ret = PyFloat_FromDouble( DEREF_IN_OR_OUT(ns_v.val.f, float) ); break; case nsXPTType::T_DOUBLE: ret = PyFloat_FromDouble( DEREF_IN_OR_OUT(ns_v.val.d, double) ); break; case nsXPTType::T_BOOL: { PRBool temp = DEREF_IN_OR_OUT(ns_v.val.b, PRBool); ret = temp ? Py_True : Py_False; Py_INCREF(ret); break; } case nsXPTType::T_CHAR: { char temp = DEREF_IN_OR_OUT(ns_v.val.c, char); ret = PyString_FromStringAndSize(&temp, 1); break; } case nsXPTType::T_WCHAR: { wchar_t temp = (wchar_t)DEREF_IN_OR_OUT(ns_v.val.wc, PRUint16); ret = PyUnicode_FromWideChar(&temp, 1); break; } // case nsXPTType::T_VOID: case nsXPTType::T_IID: { ret = Py_nsIID::PyObjectFromIID( * DEREF_IN_OR_OUT(ns_v.val.p, const nsIID *) ); break; } case nsXPTType::T_DOMSTRING: { NS_ABORT_IF_FALSE(is_out || !XPT_PD_IS_DIPPER(td.param_flags), "DOMStrings can't be inout"); nsAReadableString *rs = (nsAReadableString *)ns_v.val.p; if (rs==NULL || IsNullDOMString(*rs)) { ret = Py_None; Py_INCREF(Py_None); } else { ret = PyUnicode_FromUnicode( NULL, rs->Length() ); CopyUnicodeTo(*rs, 0, PyUnicode_AsUnicode(ret), rs->Length()); } break; } case nsXPTType::T_CHAR_STR: { char *t = DEREF_IN_OR_OUT(ns_v.val.p, char *); if (t==NULL) { ret = Py_None; Py_INCREF(Py_None); } else ret = PyString_FromString(t); break; } case nsXPTType::T_WCHAR_STR: { PRUnichar *us = DEREF_IN_OR_OUT(ns_v.val.p, PRUnichar *); if (us==NULL) { ret = Py_None; Py_INCREF(Py_None); } else ret = PyUnicode_FromUnicode( us, nsCRT::strlen(us)); break; } case nsXPTType::T_INTERFACE_IS: // our Python code does it :-) case nsXPTType::T_INTERFACE: { nsISupports *iret = DEREF_IN_OR_OUT(ns_v.val.p, nsISupports *); nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+index; ret = m_gateway->MakeInterfaceParam(iret, NULL, m_method_index, pi, index); break; } /*** nsISupports *iret = DEREF_IN_OR_OUT(ns_v.val.p, nsISupports *); nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+index; nsXPTCMiniVariant &ns_viid = m_params[td.argnum]; NS_ABORT_IF_FALSE((m_python_type_desc_array[td.argnum].type_flags & XPT_TDP_TAGMASK) == nsXPTType::T_IID, "The INTERFACE_IS iid describer isnt an IID!"); const nsIID * iid = NULL; if (XPT_PD_IS_IN(m_python_type_desc_array[td.argnum].param_flags)) // may still be inout! iid = DEREF_IN_OR_OUT(ns_v.val.p, const nsIID *); ret = m_gateway->MakeInterfaceParam(iret, iid, m_method_index, pi, index); break; } ****/ case nsXPTType::T_ARRAY: { void *t = DEREF_IN_OR_OUT(ns_v.val.p, void *); PRUint32 seq_size = GetSizeIs(index, PR_FALSE); if (t==NULL) { ret = Py_None; Py_INCREF(Py_None); } else { PRUint8 array_type; nsresult ns = GetArrayType(index, &array_type); if (NS_FAILED(ns)) { PyXPCOM_BuildPyException(ns); break; } PRUint32 element_size = GetArrayElementSize(array_type); PRUint32 seq_size = GetSizeIs(index, PR_FALSE); ret = UnpackSingleArray(t, seq_size, element_size, array_type); } break; } case nsXPTType::T_PSTRING_SIZE_IS: { char *t = DEREF_IN_OR_OUT(ns_v.val.p, char *); PRUint32 string_size = GetSizeIs(index, PR_TRUE); if (t==NULL) { ret = Py_None; Py_INCREF(Py_None); } else ret = PyString_FromStringAndSize(t, string_size); break; } case nsXPTType::T_PWSTRING_SIZE_IS: { PRUnichar *t = DEREF_IN_OR_OUT(ns_v.val.p, PRUnichar *); PRUint32 string_size = GetSizeIs(index, PR_TRUE); if (t==NULL) { ret = Py_None; Py_INCREF(Py_None); } else ret = PyUnicode_FromUnicode(t, string_size); break; } default: // As this is called by external components, // we return _something_ rather than failing before any user code has run! { char buf[128]; sprintf(buf, "Unknown XPCOM type flags (0x%x)", td.type_flags); PyXPCOM_LogWarning("%s - returning a string object with this message!\n", buf); ret = PyString_FromString(buf); break; } } return ret; } nsresult PyXPCOM_GatewayVariantHelper::GetArrayType(PRUint8 index, PRUint8 *ret) { nsCOMPtr iim = XPTI_GetInterfaceInfoManager(); NS_ABORT_IF_FALSE(iim != nsnull, "Cant get interface from IIM!"); if (iim==nsnull) return NS_ERROR_FAILURE; nsCOMPtr ii; nsresult rc = iim->GetInfoForIID( &m_gateway->m_iid, getter_AddRefs(ii)); if (NS_FAILED(rc)) return rc; nsXPTType datumType; const nsXPTParamInfo& param_info = m_info->GetParam((PRUint8)index); rc = ii->GetTypeForParam(m_method_index, ¶m_info, 1, &datumType); if (NS_FAILED(rc)) return rc; *ret = datumType.flags; return NS_OK; } PRBool PyXPCOM_GatewayVariantHelper::GetIIDForINTERFACE_ID(int index, const nsIID **ppret) { // Not sure if the IID pointed at by by this is allows to be // in or out, so we will allow it. nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+index; nsXPTType typ = pi->GetType(); NS_WARN_IF_FALSE(XPT_TDP_TAG(typ) == nsXPTType::T_IID, "INTERFACE_IS IID param isnt an IID!"); NS_ABORT_IF_FALSE(typ.IsPointer(), "Expecting to re-fill a pointer value."); if (XPT_TDP_TAG(typ) != nsXPTType::T_IID) *ppret = &NS_GET_IID(nsISupports); else { nsXPTCMiniVariant &ns_v = m_params[index]; if (pi->IsOut()) { nsIID **pp = (nsIID **)ns_v.val.p; if (pp && *pp) *ppret = *pp; else *ppret = &NS_GET_IID(nsISupports); } else if (pi->IsIn()) { nsIID *p = (nsIID *)ns_v.val.p; if (p) *ppret = p; else *ppret = &NS_GET_IID(nsISupports); } else { NS_ERROR("Param is not in or out!"); *ppret = &NS_GET_IID(nsISupports); } } return PR_TRUE; } #undef FILL_SIMPLE_POINTER #define FILL_SIMPLE_POINTER( type, ob ) *((type *)ns_v.val.p) = (type)(ob); nsresult PyXPCOM_GatewayVariantHelper::BackFillVariant( PyObject *val, int index) { nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+index; NS_ABORT_IF_FALSE(pi->IsOut() || pi->IsDipper(), "The value must be marked as [out] (or a dipper) to be back-filled!"); NS_ABORT_IF_FALSE(!pi->IsShared(), "Dont know how to back-fill a shared out param"); nsXPTCMiniVariant &ns_v = m_params[index]; PyObject *ret = NULL; nsXPTType typ = pi->GetType(); PyObject* val_use = NULL; NS_ABORT_IF_FALSE(pi->IsDipper() || ns_v.val.p, "No space for result!"); if (!pi->IsDipper() && !ns_v.val.p) return NS_ERROR_INVALID_POINTER; NS_ABORT_IF_FALSE(((pi->IsDipper()==0) ^ (XPT_TDP_TAG(typ)==nsXPTType::T_DOMSTRING==0)) == 0, "Only handle DOMString dippers!"); PRBool rc = PR_TRUE; switch (XPT_TDP_TAG(typ)) { case nsXPTType::T_I8: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRInt8, PyInt_AsLong(val_use) ); break; case nsXPTType::T_I16: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRInt16, PyInt_AsLong(val_use) ); break; case nsXPTType::T_I32: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRInt32, PyInt_AsLong(val_use) ); break; case nsXPTType::T_I64: if ((val_use=PyNumber_Long(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRInt64, PyLong_AsLongLong(val_use) ); break; case nsXPTType::T_U8: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRUint8, PyInt_AsLong(val_use) ); break; case nsXPTType::T_U16: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRUint16, PyInt_AsLong(val_use) ); break; case nsXPTType::T_U32: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRUint32, PyInt_AsLong(val_use) ); break; case nsXPTType::T_U64: if ((val_use=PyNumber_Long(val))==NULL) BREAK_FALSE; FILL_SIMPLE_POINTER( PRUint64, PyLong_AsUnsignedLongLong(val_use) ); break; case nsXPTType::T_FLOAT: if ((val_use=PyNumber_Float(val))==NULL) BREAK_FALSE FILL_SIMPLE_POINTER( float, PyFloat_AsDouble(val_use) ); break; case nsXPTType::T_DOUBLE: if ((val_use=PyNumber_Float(val))==NULL) BREAK_FALSE FILL_SIMPLE_POINTER( double, PyFloat_AsDouble(val_use) ); break; case nsXPTType::T_BOOL: if ((val_use=PyNumber_Int(val))==NULL) BREAK_FALSE FILL_SIMPLE_POINTER( PRBool, PyInt_AsLong(val_use) ); break; case nsXPTType::T_CHAR: if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } if ((val_use = PyObject_Str(val))==NULL) BREAK_FALSE; // Sanity check should PyObject_Str() ever loosen its semantics wrt Unicode! NS_ABORT_IF_FALSE(PyString_Check(val_use), "PyObject_Str didnt return a string object!"); FILL_SIMPLE_POINTER( char, *PyString_AS_STRING(val_use) ); break; case nsXPTType::T_WCHAR: if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } if ((val_use = PyUnicode_FromObject(val))==NULL) BREAK_FALSE; NS_ABORT_IF_FALSE(PyUnicode_Check(val_use), "PyUnicode_FromObject didnt return a Unicode object!"); FILL_SIMPLE_POINTER( PRUnichar, *PyUnicode_AS_UNICODE(val_use) ); break; // case nsXPTType::T_VOID: case nsXPTType::T_IID: { nsIID iid; if (!Py_nsIID::IIDFromPyObject(val, &iid)) BREAK_FALSE; nsIID **pp = (nsIID **)ns_v.val.p; // If there is an existing [in] IID, free it. if (*pp && pi->IsIn()) nsAllocator::Free(*pp); *pp = (nsIID *)nsAllocator::Alloc(sizeof(nsIID)); if (*pp==NULL) { PyErr_NoMemory(); BREAK_FALSE; } nsCRT::memcpy(*pp, &iid, sizeof(iid)); break; } case nsXPTType::T_DOMSTRING: { nsAWritableString *ws = (nsAWritableString *)ns_v.val.p; NS_ABORT_IF_FALSE(ws->Length() == 0, "Why does this writable string already have chars??"); if (val == Py_None) { (*ws) = (PRUnichar *)nsnull; } else { if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } val_use = PyUnicode_FromObject(val); NS_ABORT_IF_FALSE(PyUnicode_Check(val_use), "PyUnicode_FromObject didnt return a Unicode object!"); const PRUnichar *sz = PyUnicode_AS_UNICODE(val_use); ws->Assign(sz); } break; } case nsXPTType::T_CHAR_STR: { // If it is an existing string, free it. char **pp = (char **)ns_v.val.p; if (*pp && pi->IsIn()) nsAllocator::Free(*pp); *pp = nsnull; if (val == Py_None) break; // Remains NULL. if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } if ((val_use = PyObject_Str(val))==NULL) BREAK_FALSE; // Sanity check should PyObject_Str() ever loosen its semantics wrt Unicode! NS_ABORT_IF_FALSE(PyString_Check(val_use), "PyObject_Str didnt return a string object!"); const char *sz = PyString_AS_STRING(val_use); int nch = PyString_GET_SIZE(val_use); *pp = (char *)nsAllocator::Alloc(nch+1); if (*pp==NULL) { PyErr_NoMemory(); BREAK_FALSE; } strncpy(*pp, sz, nch+1); break; } case nsXPTType::T_WCHAR_STR: { // If it is an existing string, free it. PRUnichar **pp = (PRUnichar **)ns_v.val.p; if (*pp && pi->IsIn()) nsAllocator::Free(*pp); *pp = nsnull; if (val == Py_None) break; // Remains NULL. if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } val_use = PyUnicode_FromObject(val); NS_ABORT_IF_FALSE(PyUnicode_Check(val_use), "PyUnicode_FromObject didnt return a Unicode object!"); const PRUnichar *sz = PyUnicode_AS_UNICODE(val_use); int nch = PyUnicode_GET_SIZE(val_use); *pp = (PRUnichar *)nsAllocator::Alloc(sizeof(PRUnichar) * (nch+1)); if (*pp==NULL) { PyErr_NoMemory(); BREAK_FALSE; } nsCRT::memcpy(*pp, sz, sizeof(PRUnichar) * (nch + 1)); break; } case nsXPTType::T_INTERFACE: { nsISupports *pnew = nsnull; // Get it the "standard" way. // We do allow NULL here, even tho doing so will no-doubt crash some objects. // (but there will certainly be objects out there that will allow NULL :-( if (!Py_nsISupports::InterfaceFromPyObject(val, NS_GET_IID(nsISupports), &pnew, PR_TRUE)) BREAK_FALSE; nsISupports **pp = (nsISupports **)ns_v.val.p; if (*pp && pi->IsIn()) { Py_BEGIN_ALLOW_THREADS; // MUST release thread-lock, incase a Python COM object that re-acquires. (*pp)->Release(); Py_END_ALLOW_THREADS; } *pp = pnew; // ref-count added by InterfaceFromPyObject break; } case nsXPTType::T_INTERFACE_IS: { // We do allow NULL here, even tho doing so will no-doubt crash some objects. // (but there will certainly be objects out there that will allow NULL :-( const nsIID *piid; if (!GetIIDForINTERFACE_ID(pi->type.argnum, &piid)) BREAK_FALSE; nsISupports *pnew = nsnull; // Get it the "standard" way. // We do allow NULL here, even tho doing so will no-doubt crash some objects. // (but there will certainly be objects out there that will allow NULL :-( if (!Py_nsISupports::InterfaceFromPyObject(val, *piid, &pnew, PR_TRUE)) BREAK_FALSE; nsISupports **pp = (nsISupports **)ns_v.val.p; if (*pp && pi->IsIn()) { Py_BEGIN_ALLOW_THREADS; // MUST release thread-lock, incase a Python COM object that re-acquires. (*pp)->Release(); Py_END_ALLOW_THREADS; } *pp = pnew; // ref-count added by InterfaceFromPyObject break; } case nsXPTType::T_PSTRING_SIZE_IS: { const char *sz = nsnull; PRUint32 nch = 0; if (val != Py_None) { if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } if ((val_use = PyObject_Str(val))==NULL) BREAK_FALSE; // Sanity check should PyObject_Str() ever loosen its semantics wrt Unicode! NS_ABORT_IF_FALSE(PyString_Check(val_use), "PyObject_Str didnt return a string object!"); sz = PyString_AS_STRING(val_use); nch = PyString_GET_SIZE(val_use); } PRBool bBackFill = PR_FALSE; PRBool bCanSetSizeIs = CanSetSizeIs(index, PR_TRUE); // If we can not change the size, check our sequence is correct. if (!bCanSetSizeIs) { PRUint32 existing_size = GetSizeIs(index, PR_TRUE); if (nch != existing_size) { PyErr_Format(PyExc_ValueError, "This function is expecting a string of exactly length %d - %d characters were passed", existing_size, nch); BREAK_FALSE; } // It we have an "inout" param, but an "in" count, then // it is probably a buffer the caller expects us to // fill in-place! bBackFill = pi->IsIn(); } if (bBackFill) { nsCRT::memcpy(*(char **)ns_v.val.p, sz, nch); } else { // If we have an existing string, free it! char **pp = (char **)ns_v.val.p; if (*pp && pi->IsIn()) nsAllocator::Free(*pp); *pp = nsnull; if (sz==nsnull) // None specified. break; // Remains NULL. *pp = (char *)nsAllocator::Alloc(nch); if (*pp==NULL) { PyErr_NoMemory(); BREAK_FALSE; } nsCRT::memcpy(*pp, sz, nch); if (bCanSetSizeIs) rc = SetSizeIs(index, PR_TRUE, nch); else { NS_ABORT_IF_FALSE(GetSizeIs(index, PR_TRUE) == nch, "Can't set sizeis, but string isnt correct size"); } } break; } case nsXPTType::T_PWSTRING_SIZE_IS: { const PRUnichar *sz = nsnull; PRUint32 nch = 0; PRUint32 nbytes = 0; if (val != Py_None) { if (!PyString_Check(val) && !PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "This parameter must be a string or Unicode object"); BREAK_FALSE; } val_use = PyUnicode_FromObject(val); NS_ABORT_IF_FALSE(PyUnicode_Check(val_use), "PyUnicode_FromObject didnt return a Unicode object!"); sz = PyUnicode_AS_UNICODE(val_use); nch = PyUnicode_GET_SIZE(val_use); nbytes = sizeof(PRUnichar) * nch; } PRBool bBackFill = PR_FALSE; PRBool bCanSetSizeIs = CanSetSizeIs(index, PR_TRUE); // If we can not change the size, check our sequence is correct. if (!bCanSetSizeIs) { // It is a buffer the caller prolly wants us to fill in-place! PRUint32 existing_size = GetSizeIs(index, PR_TRUE); if (nch != existing_size) { PyErr_Format(PyExc_ValueError, "This function is expecting a string of exactly length %d - %d characters were passed", existing_size, nch); BREAK_FALSE; } // It we have an "inout" param, but an "in" count, then // it is probably a buffer the caller expects us to // fill in-place! bBackFill = pi->IsIn(); } if (bBackFill) { nsCRT::memcpy(*(PRUnichar **)ns_v.val.p, sz, nbytes); } else { // If it is an existing string, free it. PRUnichar **pp = (PRUnichar **)ns_v.val.p; if (*pp && pi->IsIn()) nsAllocator::Free(*pp); *pp = nsnull; if (val == Py_None) break; // Remains NULL. *pp = (PRUnichar *)nsAllocator::Alloc(nbytes); if (*pp==NULL) { PyErr_NoMemory(); BREAK_FALSE; } nsCRT::memcpy(*pp, sz, nbytes); if (bCanSetSizeIs) rc = SetSizeIs(index, PR_TRUE, nch); else { NS_ABORT_IF_FALSE(GetSizeIs(index, PR_TRUE) == nch, "Can't set sizeis, but string isnt correct size"); } } break; } case nsXPTType::T_ARRAY: { // If it is an existing array of the correct size, keep it. PRUint32 sequence_size = 0; PRUint8 array_type; nsresult ns = GetArrayType(index, &array_type); if (NS_FAILED(ns)) return ns; PRUint32 element_size = GetArrayElementSize(array_type); if (val != Py_None) { if (!PySequence_Check(val)) { PyErr_Format(PyExc_TypeError, "Object for xpcom array must be a sequence, not type '%s'", val->ob_type->tp_name); BREAK_FALSE; } sequence_size = PySequence_Length(val); } PRUint32 existing_size = GetSizeIs(index, PR_FALSE); PRBool bBackFill = PR_FALSE; PRBool bCanSetSizeIs = CanSetSizeIs(index, PR_FALSE); // If we can not change the size, check our sequence is correct. if (!bCanSetSizeIs) { // It is a buffer the caller prolly wants us to fill in-place! if (sequence_size != existing_size) { PyErr_Format(PyExc_ValueError, "This function is expecting a sequence of exactly length %d - %d items were passed", existing_size, sequence_size); BREAK_FALSE; } // It we have an "inout" param, but an "in" count, then // it is probably a buffer the caller expects us to // fill in-place! bBackFill = pi->IsIn(); } if (bBackFill) rc = FillSingleArray(*(void **)ns_v.val.p, val, sequence_size, element_size, array_type); else { // If it is an existing array, free it. void **pp = (void **)ns_v.val.p; if (*pp && pi->IsIn()) { FreeSingleArray(*pp, existing_size, array_type); nsAllocator::Free(*pp); } *pp = nsnull; if (val == Py_None) break; // Remains NULL. size_t nbytes = sequence_size * element_size; *pp = (void *)nsAllocator::Alloc(nbytes); memset(*pp, 0, nbytes); rc = FillSingleArray(*pp, val, sequence_size, element_size, array_type); if (!rc) break; if (bCanSetSizeIs) rc = SetSizeIs(index, PR_FALSE, sequence_size); else { NS_ABORT_IF_FALSE(GetSizeIs(index, PR_FALSE) == sequence_size, "Can't set sizeis, but string isnt correct size"); } } break; } default: // try and limp along in this case. // leave rc TRUE PyXPCOM_LogWarning("Converting Python object for an [out] param - The object type (0x%x) is unknown - leaving param alone!\n", XPT_TDP_TAG(typ)); break; } Py_XDECREF(val_use); if (!rc) return NS_ERROR_FAILURE; return NS_OK; } nsresult PyXPCOM_GatewayVariantHelper::ProcessPythonResult(PyObject *ret_ob) { // NOTE - although we return an nresult, if we leave a Python // exception set, then our caller may take additional action // (ie, translating our nsresult to a more appropriate nsresult // for the Python exception.) NS_PRECONDITION(!PyErr_Occurred(), "Expecting no Python exception to be pending when processing the return result"); nsresult rc = NS_OK; // If we dont get a tuple back, then the result is only // an int nresult for the underlying function. // (ie, the policy is expected to return (NS_OK, user_retval), // but can also return (say), NS_ERROR_FAILURE if (PyInt_Check(ret_ob)) return PyInt_AsLong(ret_ob); // Now it must be the tuple. if (!PyTuple_Check(ret_ob) || PyTuple_Size(ret_ob)!=2 || !PyInt_Check(PyTuple_GET_ITEM(ret_ob, 0))) { PyErr_SetString(PyExc_TypeError, "The Python result must be a single integer or a tuple of length==2 and first item an int."); return NS_ERROR_FAILURE; } PyObject *user_result = PyTuple_GET_ITEM(ret_ob, 1); // Count up how many results our function needs. int i; int num_results = 0; int last_result = -1; // optimization if we only have one - this is it! int index_retval = -1; for (i=0;iparams+i; if (!m_python_type_desc_array[i].is_auto_out) { if (pi->IsOut() || pi->IsDipper()) { num_results++; last_result = i; } if (pi->IsRetval()) index_retval = i; } } if (num_results==0) { ; // do nothing } else if (num_results==1) { // May or may not be the nominated retval - who cares! NS_ABORT_IF_FALSE(last_result >=0 && last_result < m_num_type_descs, "Have one result, but dont know its index!"); rc = BackFillVariant( user_result, last_result ); } else { // Loop over each one, filling as we go. // We allow arbitary sequences here, but _not_ strings // or Unicode! // NOTE - We ALWAYS do the nominated retval first. // The Python pattern is always: // return retval [, byref1 [, byref2 ...] ] // But the retval is often the last param described in the info. if (!PySequence_Check(user_result) || PyString_Check(user_result) || PyUnicode_Check(user_result)) { PyErr_SetString(PyExc_TypeError, "This function has multiple results, but a sequence was not given to fill them"); return NS_ERROR_FAILURE; } int num_user_results = PySequence_Length(user_result); // If they havent given enough, we dont really care. // although a warning is probably appropriate. if (num_user_results != num_results) { const char *method_name = m_info->GetName(); PyXPCOM_LogWarning("The method '%s' has %d out params, but %d were supplied by the Python code\n", method_name, num_results, num_user_results); } int this_py_index = 0; if (index_retval != -1) { // We always return the nominated result first! PyObject *sub = PySequence_GetItem(user_result, 0); if (sub==NULL) return NS_ERROR_FAILURE; rc = BackFillVariant(sub, index_retval); Py_DECREF(sub); this_py_index = 1; } for (i=0;NS_SUCCEEDED(rc) && iGetParamCount();i++) { // If weve already done it, or dont need to do it! if (i==index_retval || m_python_type_desc_array[i].is_auto_out) continue; nsXPTParamInfo *pi = (nsXPTParamInfo *)m_info->params+i; if (pi->IsOut()) { PyObject *sub = PySequence_GetItem(user_result, this_py_index); if (sub==NULL) return NS_ERROR_FAILURE; rc = BackFillVariant(sub, i); Py_DECREF(sub); this_py_index++; } } } return rc; }